From 1c8f1cbc2ffac964e2cbac6f5cf1d97e2f3bafb4 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Wed, 24 Apr 2019 11:38:11 +0100 Subject: [PATCH] Fix wrapping of words with 'wrap col - indent < word len < wrap col' Add a test case for those, and refactor bits of the wrap highlighter. This explains part of the needed complexity in #2820. --- src/highlighters.cc | 46 ++++++++++------------ test/regression/2245-wrap-long-word/cmd | 1 + test/regression/2245-wrap-long-word/in | 4 ++ test/regression/2245-wrap-long-word/rc | 1 + test/regression/2245-wrap-long-word/ui-out | 7 ++++ 5 files changed, 34 insertions(+), 25 deletions(-) create mode 100644 test/regression/2245-wrap-long-word/cmd create mode 100644 test/regression/2245-wrap-long-word/in create mode 100644 test/regression/2245-wrap-long-word/rc create mode 100644 test/regression/2245-wrap-long-word/ui-out diff --git a/src/highlighters.cc b/src/highlighters.cc index 2d9d39a4..bb617986 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -670,16 +670,18 @@ struct WrapHighlighter : Highlighter const auto& cursor = context.context.selections().main().cursor(); const int tabstop = context.context.options()["tabstop"].get(); const LineCount win_height = context.context.window().dimensions().line; - const ColumnCount marker_len = m_marker.column_length(); + const ColumnCount marker_len = zero_if_greater(m_marker.column_length(), wrap_column); const Face face_marker = context.context.faces()["StatusLineInfo"]; for (auto it = display_buffer.lines().begin(); it != display_buffer.lines().end(); ++it) { const LineCount buf_line = it->range().begin.line; const ByteCount line_length = buffer[buf_line].length(); - const ColumnCount indent = m_preserve_indent ? line_indent(buffer, tabstop, buf_line) : 0_col; + const ColumnCount indent = m_preserve_indent ? + zero_if_greater(line_indent(buffer, tabstop, buf_line), wrap_column) : 0_col; + const ColumnCount prefix_len = std::max(marker_len, indent); - auto pos = next_split_pos(buffer, wrap_column, tabstop, buf_line, {0, 0}); + auto pos = next_split_pos(buffer, wrap_column, prefix_len, tabstop, buf_line, {0, 0}); if (pos.byte == line_length) continue; @@ -702,17 +704,12 @@ struct WrapHighlighter : Highlighter DisplayLine new_line{ AtomList{ atom_it, line.end() } }; line.erase(atom_it, line.end()); - ColumnCount prefix_len = 0; - if (marker_len != 0 and marker_len < wrap_column) - { + if (marker_len != 0) new_line.insert(new_line.begin(), {m_marker, face_marker}); - prefix_len = marker_len; - } - if (indent > marker_len and indent < wrap_column) + if (indent > marker_len) { auto it = new_line.insert(new_line.begin() + (marker_len > 0), {buffer, coord, coord}); it->replace(String{' ', indent - marker_len}); - prefix_len = indent; } if (it+1 - display_buffer.lines().begin() == win_height) @@ -730,7 +727,7 @@ struct WrapHighlighter : Highlighter } it = display_buffer.lines().insert(it+1, new_line); - pos = next_split_pos(buffer, wrap_column - prefix_len, tabstop, buf_line, pos); + pos = next_split_pos(buffer, wrap_column - prefix_len, prefix_len, tabstop, buf_line, pos); atom_it = it->begin(); } } @@ -749,14 +746,14 @@ struct WrapHighlighter : Highlighter const auto& cursor = context.context.selections().main().cursor(); const int tabstop = context.context.options()["tabstop"].get(); - auto line_wrap_count = [&](LineCount line, ColumnCount indent) { + auto line_wrap_count = [&](LineCount line, ColumnCount prefix_len) { LineCount count = 0; const ByteCount line_length = buffer[line].length(); SplitPos pos{0, 0}; while (true) { - pos = next_split_pos(buffer, wrap_column - (pos.byte == 0 ? 0_col : indent), - tabstop, line, pos); + pos = next_split_pos(buffer, wrap_column - (pos.byte == 0 ? 0_col : prefix_len), + prefix_len, tabstop, line, pos); if (pos.byte == line_length) break; ++count; @@ -772,7 +769,7 @@ struct WrapHighlighter : Highlighter setup.scroll_offset.column = 0; setup.full_lines = true; - const ColumnCount marker_len = m_marker.column_length(); + const ColumnCount marker_len = zero_if_greater(m_marker.column_length(), wrap_column); for (auto buf_line = setup.window_pos.line, win_line = 0_line; win_line < win_height or buf_line <= cursor.line; @@ -781,13 +778,9 @@ struct WrapHighlighter : Highlighter if (buf_line >= buffer.line_count()) break; - const ColumnCount indent = m_preserve_indent ? line_indent(buffer, tabstop, buf_line) : 0_col; - - ColumnCount prefix_len = 0; - if (marker_len < wrap_column) - prefix_len = marker_len; - if (indent > marker_len and indent < wrap_column) - prefix_len = indent; + const ColumnCount indent = m_preserve_indent ? + zero_if_greater(line_indent(buffer, tabstop, buf_line), wrap_column) : 0_col; + const ColumnCount prefix_len = std::max(marker_len, indent); if (buf_line == cursor.line) { @@ -795,7 +788,7 @@ struct WrapHighlighter : Highlighter for (LineCount count = 0; true; ++count) { auto next_pos = next_split_pos(buffer, wrap_column - (pos.byte != 0 ? prefix_len : 0_col), - tabstop, buf_line, pos); + prefix_len, tabstop, buf_line, pos); if (next_pos.byte > cursor.column) { setup.cursor_pos = DisplayCoord{ @@ -831,7 +824,8 @@ struct WrapHighlighter : Highlighter unique_ids.push_back(ms_id); } - SplitPos next_split_pos(const Buffer& buffer, ColumnCount wrap_column, int tabstop, LineCount line, SplitPos current) const + SplitPos next_split_pos(const Buffer& buffer, ColumnCount wrap_column, ColumnCount prefix_len, + int tabstop, LineCount line, SplitPos current) const { const ColumnCount target_column = current.column + wrap_column; StringView content = buffer[line]; @@ -873,7 +867,7 @@ struct WrapHighlighter : Highlighter // split at last word boundary if the word is shorter than our wrapping width ColumnCount word_length = pos.column - last_boundary.column; const char* it = &content[pos.byte]; - while (it != content.end() and word_length <= wrap_column) + while (it != content.end() and word_length < (wrap_column - prefix_len)) { const Codepoint cp = utf8::read_codepoint(it, content.end()); if (not is_word(cp)) @@ -914,6 +908,8 @@ struct WrapHighlighter : Highlighter parser.get_switch("marker").value_or("").str()); } + static ColumnCount zero_if_greater(ColumnCount val, ColumnCount max) { return val < max ? val : 0; }; + const bool m_word_wrap; const bool m_preserve_indent; const ColumnCount m_max_width; diff --git a/test/regression/2245-wrap-long-word/cmd b/test/regression/2245-wrap-long-word/cmd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/regression/2245-wrap-long-word/cmd @@ -0,0 +1 @@ + diff --git a/test/regression/2245-wrap-long-word/in b/test/regression/2245-wrap-long-word/in new file mode 100644 index 00000000..28bbf0fc --- /dev/null +++ b/test/regression/2245-wrap-long-word/in @@ -0,0 +1,4 @@ + short line + + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line diff --git a/test/regression/2245-wrap-long-word/rc b/test/regression/2245-wrap-long-word/rc new file mode 100644 index 00000000..37739d90 --- /dev/null +++ b/test/regression/2245-wrap-long-word/rc @@ -0,0 +1 @@ +add-highlighter global/ wrap -word -indent diff --git a/test/regression/2245-wrap-long-word/ui-out b/test/regression/2245-wrap-long-word/ui-out new file mode 100644 index 00000000..719bf285 --- /dev/null +++ b/test/regression/2245-wrap-long-word/ui-out @@ -0,0 +1,7 @@ +{ "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] } +{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " short line\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " line\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "ng line\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] } +{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] } +{ "jsonrpc": "2.0", "method": "info_hide", "params": [] } +{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:1 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] } +{ "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 0, "column": 0 }] } +{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }