diff --git a/ChangeLog b/ChangeLog index 7074b28a0..4bf3fb6a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,8 @@ Bugs file (thanks to Tim None). + Fixed bug #1128, which caused 'age' columns to be right-justified instead of left-justified (thanks to Steve Rader). + + Fixed bug #1136, which incorrectly line-wrapped tasks with annotations + (thanks to Steve Rader). + Fixed bug #1150, which referenced deprecated features in the tutorial man page (thanks to Benjamin Weber). + Fixed bug #1154, which now allows priorities to be specified in any case. diff --git a/src/text.cpp b/src/text.cpp index 29146b1c8..6ce964945 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -56,16 +56,10 @@ void wrapText ( const int width, bool hyphenate) { - std::string copy = text; std::string line; - - int modified_width = width > 0 ? width : 1; - - while (copy.length ()) // Used as Boolean, therefore UTF8 safe. - { - extractLine (copy, line, modified_width, hyphenate); + unsigned int offset = 0; + while (extractLine (line, text, width, hyphenate, offset)) lines.push_back (line); - } } //////////////////////////////////////////////////////////////////////////////// @@ -297,118 +291,115 @@ int longestLine (const std::string& input) return longest; } -//////////////////////////////////////////////////////////////////////////////// // Walk the input text looking for a break point. A break point is one of: // - EOS // - \n // - last space before 'length' characters -// - last comma before 'length' characters, even if not followed by a space +// - last punctuation (, ; . :) before 'length' characters, even if not +// followed by a space // - first 'length' characters -void extractLine ( - std::string& text, + +// text "one two three\n four" +// bytes 0123456789012 3456789 +// characters 1234567890a23 4567890 +// +// leading_ws +// ws ^ ^ ^^ +// punct +// break ^ +bool extractLine ( std::string& line, - int length, - bool hyphenate) + const std::string& text, + int width, + bool hyphenate, + unsigned int& offset) { - std::string::size_type bytes = 0; - std::string::size_type previous = std::string::npos; - std::string::size_type last_space = std::string::npos; - std::string::size_type last_comma = std::string::npos; + // Terminate processing. + // Note: bytes vs bytes. + if (offset >= text.length ()) + return false; + + std::string::size_type last_last_bytes = offset; + std::string::size_type last_bytes = offset; + std::string::size_type bytes = offset; + unsigned int last_ws = 0; int character; - int width = 0; - while (width < length) + int char_width = 0; + int line_width = 0; + while (1) { - previous = bytes; + last_last_bytes = last_bytes; + last_bytes = bytes; character = utf8_next_char (text, bytes); - // Record last seen space. - if (character == ' ') - last_space = previous; - - // Record last seen comma. - else if (character == ',' || - character == ';' || - character == '.') - last_comma = previous; - - // Newline is an early break point. - else if (character == '\n') + if (character == 0 || + character == '\n') { - line = text.substr (0, previous); - text = text.substr (bytes); - return; + line = text.substr (offset, last_bytes - offset); + offset = bytes; + break; + } + else if (character == ' ') + last_ws = last_bytes; + + char_width = mk_wcwidth (character); + if (line_width + char_width > width) + { + int last_last_character = text[last_last_bytes]; + int last_character = text[last_bytes]; + + // [case 1] one| two --> last_last != 32, last == 32, ws == 0 + if (last_last_character != ' ' && + last_character == ' ') + { + line = text.substr (offset, last_bytes - offset); + offset = last_bytes + 1; + break; + } + + // [case 2] one |two --> last_last == 32, last != 32, ws != 0 + else if (last_last_character == ' ' && + last_character != ' ' && + last_ws != 0) + { + line = text.substr (offset, last_bytes - offset - 1); + offset = last_bytes; + break; + } + + else if (last_last_character != ' ' && + last_character != ' ') + { + // [case 3] one t|wo --> last_last != 32, last != 32, ws != 0 + if (last_ws != 0) + { + line = text.substr (offset, last_ws - offset); + offset = last_ws + 1; + break; + } + // [case 4] on|e two --> last_last != 32, last != 32, ws == 0 + else + { + if (hyphenate) + { + line = text.substr (offset, last_bytes - offset - 1) + "-"; + offset = last_last_bytes; + } + else + { + line = text.substr (offset, last_bytes - offset); + offset = last_bytes; + } + } + + break; + } } - // EOS is an early break point. - else if (character == 0) - { - line = text; - text = ""; - return; - } - - width += mk_wcwidth (character); + line_width += char_width; } - // Case where EOS was not quite reached. - // 012345 - // |eleven|\0 - if (text[bytes] == '\0') - { - line = text; - text = ""; - return; - } - - // Case where a word ends at the right margin. - // 012345 - // |eleven|_ - if (text[bytes] == ' ') - { - line = text.substr (0, bytes); - text = text.substr (bytes + 1); - return; - } - - // Case where a word straddles the margin, but there is an earlier space - // to break on. - // 012345 - // |one_tw|o - if (last_space != std::string::npos) - { - line = text.substr (0, last_space); - text = text.substr (last_space + 1); - return; - } - - if (last_comma != std::string::npos) - { - line = text.substr (0, last_comma + 1); - text = text.substr (last_comma + 1); - return; - } - - // Case where a word needs to be split, and there is no last_space - // or last_comma. - // Hyphenation becomes the issue. - // 012345 - // |fiftee|n - // |fifte-|en - else - { - if (hyphenate) - { - line = text.substr (0, previous) + "-"; - text = text.substr (previous); - } - else - { - line = text.substr (0, bytes); - text = text.substr (bytes); - } - - return; - } + return true; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/text.h b/src/text.h index 2bd2f4c21..3bde26f38 100644 --- a/src/text.h +++ b/src/text.h @@ -41,7 +41,7 @@ std::string trim (const std::string& in, const std::string& t = " "); std::string unquoteText (const std::string&); int longestWord (const std::string&); int longestLine (const std::string&); -void extractLine (std::string&, std::string&, int, bool); +bool extractLine (std::string&, const std::string&, int, bool, unsigned int&); void splitq (std::vector&, const std::string&, const char); void split (std::vector&, const std::string&, const char); void split (std::vector&, const std::string&, const std::string&); diff --git a/test/text.t.cpp b/test/text.t.cpp index 31d9cd808..6ad89b16b 100644 --- a/test/text.t.cpp +++ b/test/text.t.cpp @@ -37,6 +37,7 @@ Context context; int main (int argc, char** argv) { UnitTest t (264); + // void wrapText (std::vector & lines, const std::string& text, const int width, bool hyphenate) std::string text = "This is a test of the line wrapping code."; std::vector lines; @@ -67,34 +68,35 @@ int main (int argc, char** argv) t.is (lines[0], "one two three", "wrapText line 0 -> 'one two three'"); t.is (lines[1], " four", "wrapText line 1 -> ' four'"); - // void extractLine (std::string& text, std::string& line, int length, bool hyphenate) + // void extractLine (std::string& text, std::string& line, int length, bool hyphenate, unsigned int& offset) text = "This ☺ is a test of utf8 line extraction."; + unsigned int offset = 0; std::string line; - extractLine (text, line, 7, true); + extractLine (line, text, 7, true, offset); t.is (line, "This ☺", "extractLine 7 'This ☺ is a test of utf8 line extraction.' -> 'This ☺'"); - // void extractLine (std::string& text, std::string& line, int length) + // void extractLine (std::string& text, std::string& line, int length, bool hyphenate, unsigned int& offset) text = "line 1\nlengthy second line that exceeds width"; - extractLine (text, line, 10, true); + offset = 0; + extractLine (line, text, 10, true, offset); t.is (line, "line 1", "extractLine 10 'line 1\\nlengthy second line that exceeds width' -> 'line 1'"); - extractLine (text, line, 10, true); + extractLine (line, text, 10, true, offset); t.is (line, "lengthy", "extractLine 10 'lengthy second line that exceeds width' -> 'lengthy'"); - extractLine (text, line, 10, true); + extractLine (line, text, 10, true, offset); t.is (line, "second", "extractLine 10 'second line that exceeds width' -> 'second'"); - extractLine (text, line, 10, true); + extractLine (line, text, 10, true, offset); t.is (line, "line that", "extractLine 10 'line that exceeds width' -> 'line that'"); - extractLine (text, line, 10, true); + extractLine (line, text, 10, true, offset); t.is (line, "exceeds", "extractLine 10 'exceeds width' -> 'exceeds'"); - extractLine (text, line, 10, true); + extractLine (line, text, 10, true, offset); t.is (line, "width", "extractLine 10 'width' -> 'width'"); - extractLine (text, line, 10, true); - t.is (line, "", "extractLine 10 '' -> ''"); + t.notok (extractLine (line, text, 10, true, offset), "extractLine 10 '' -> ''"); // void split (std::vector& results, const std::string& input, const char delimiter) std::vector items;