From cb119251bcd6771e30c7f504e260c514572473c0 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 26 Apr 2020 19:27:44 +1000 Subject: [PATCH] Do not replace partially selected ranges in replace-ranges highlighter Partially selected ranges should not be replaced to make it possible to see what is actually selected. --- src/display_buffer.cc | 3 +- src/display_buffer.hh | 7 +++++ src/highlighters.cc | 28 ++++++++++++------- .../.kak_history | 1 + .../replace-only-fully-selected-ranges/cmd | 1 + .../replace-only-fully-selected-ranges/in | 3 ++ .../replace-only-fully-selected-ranges/rc | 2 ++ .../replace-only-fully-selected-ranges/script | 7 +++++ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 test/highlight/replace-only-fully-selected-ranges/.kak_history create mode 100644 test/highlight/replace-only-fully-selected-ranges/cmd create mode 100644 test/highlight/replace-only-fully-selected-ranges/in create mode 100644 test/highlight/replace-only-fully-selected-ranges/rc create mode 100644 test/highlight/replace-only-fully-selected-ranges/script diff --git a/src/display_buffer.cc b/src/display_buffer.cc index d5fc8275..3d68356f 100644 --- a/src/display_buffer.cc +++ b/src/display_buffer.cc @@ -265,7 +265,8 @@ DisplayLine parse_display_line(StringView line, const FaceRegistry& faces, const else { content += StringView{pos, it}; - res.push_back({std::move(content), face}); + if (not content.empty()) + res.push_back({std::move(content), face}); content.clear(); auto closing = std::find(it+1, end, '}'); if (closing == end) diff --git a/src/display_buffer.hh b/src/display_buffer.hh index 65ea93db..abd1a244 100644 --- a/src/display_buffer.hh +++ b/src/display_buffer.hh @@ -55,6 +55,13 @@ public: m_text = std::move(text); } + void replace(const BufferRange& range) + { + kak_assert(m_type == Text); + m_type = ReplacedRange; + m_range = range; + } + bool has_buffer_range() const { return m_type == Range or m_type == ReplacedRange; diff --git a/src/highlighters.cc b/src/highlighters.cc index 33ec6005..3ead3f6f 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -96,10 +96,7 @@ void replace_range(DisplayBuffer& display_buffer, { auto& range = line.range(); if ((begin == end) and begin == range.end) - { - func(line, line.atoms().size(), line.atoms().size()); - continue; - } + return func(line, line.end()); if (range.end <= begin or end < range.begin) continue; @@ -128,7 +125,7 @@ void replace_range(DisplayBuffer& display_buffer, } if (beg_idx != -1 and end_idx != -1) - func(line, beg_idx, end_idx); + return func(line, line.erase(line.begin() + beg_idx, line.begin() + end_idx)); } } @@ -1582,17 +1579,28 @@ private: return c.line >= 0 and c.column >= 0 and c.line < buffer.line_count() and c.column <= buffer[c.line].length(); }; + auto is_fully_selected = [&sels=context.context.selections()](const InclusiveBufferRange& range) { + auto it = std::lower_bound(sels.begin(), sels.end(), range.first, [](const Selection& s, const BufferCoord& c) { return s.max() < c; }); + if (it == sels.end()) + return true; + return it->min() > range.last or (it->min() <= range.first and it->max() >= range.last); + }; + for (auto& [range, spec] : range_and_faces.list) { try { - if (!is_valid(range.first) or (!is_empty(range) and !is_valid(range.last))) + if (!is_valid(range.first) or (!is_empty(range) and !is_valid(range.last)) or !is_fully_selected(range)) continue; auto replacement = parse_display_line(spec, context.context.faces()); - replace_range(display_buffer, range.first, is_empty(range) ? range.first : buffer.char_next(range.last), - [&](DisplayLine& line, int beg_idx, int end_idx){ - auto it = line.erase(line.begin() + beg_idx, line.begin() + end_idx); - std::move(replacement.begin(), replacement.end(), std::inserter(line, it)); + auto end = is_empty(range) ? range.first : buffer.char_next(range.last); + replace_range(display_buffer, range.first, end, + [&](DisplayLine& line, DisplayLine::iterator pos){ + for (auto& atom : replacement) + { + atom.replace(BufferRange{range.first, end}); + pos = ++line.insert(pos, std::move(atom)); + } }); } catch (runtime_error&) diff --git a/test/highlight/replace-only-fully-selected-ranges/.kak_history b/test/highlight/replace-only-fully-selected-ranges/.kak_history new file mode 100644 index 00000000..06fb5c84 --- /dev/null +++ b/test/highlight/replace-only-fully-selected-ranges/.kak_history @@ -0,0 +1 @@ +'reg' ':' '' 'wq' \ No newline at end of file diff --git a/test/highlight/replace-only-fully-selected-ranges/cmd b/test/highlight/replace-only-fully-selected-ranges/cmd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/highlight/replace-only-fully-selected-ranges/cmd @@ -0,0 +1 @@ + diff --git a/test/highlight/replace-only-fully-selected-ranges/in b/test/highlight/replace-only-fully-selected-ranges/in new file mode 100644 index 00000000..75d23e24 --- /dev/null +++ b/test/highlight/replace-only-fully-selected-ranges/in @@ -0,0 +1,3 @@ +%(12345) +1%(234)5 +1%(2)345 diff --git a/test/highlight/replace-only-fully-selected-ranges/rc b/test/highlight/replace-only-fully-selected-ranges/rc new file mode 100644 index 00000000..011ccc67 --- /dev/null +++ b/test/highlight/replace-only-fully-selected-ranges/rc @@ -0,0 +1,2 @@ +declare-option range-specs test_ranges %val{timestamp} '1.2,1.4|{blue}replaced{green} text' '2.2,2.4|{blue}replaced{green} text' '3.2,3.4|{red}not {blue}replaced{green} text' +add-highlighter window/ replace-ranges test_ranges diff --git a/test/highlight/replace-only-fully-selected-ranges/script b/test/highlight/replace-only-fully-selected-ranges/script new file mode 100644 index 00000000..baa7f23b --- /dev/null +++ b/test/highlight/replace-only-fully-selected-ranges/script @@ -0,0 +1,7 @@ +ui_out '{ "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] }' +ui_out '{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "blue", "attributes": [] }, "contents": "1" }, { "face": { "fg": "black", "bg": "blue", "attributes": [] }, "contents": "replaced" }, { "face": { "fg": "black", "bg": "blue", "attributes": [] }, "contents": " text" }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "5" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "1" }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "replaced" }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": " text" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "5\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "1" }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "2" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "345\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }' +ui_out '{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] }' +ui_out '{ "jsonrpc": "2.0", "method": "info_hide", "params": [] }' +ui_out '{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 3:2 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[+]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "3 sels (3)" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }' +ui_out '{ "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 2, "column": 1 }] }' +ui_out '{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }'