diff --git a/src/normal.cc b/src/normal.cc index cc611d9f..c8ff9c78 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -22,86 +22,6 @@ namespace Kakoune { -void erase(Buffer& buffer, SelectionList& selections) -{ - for (auto& sel : reversed(selections)) - { - erase(buffer, sel); - } - selections.update(); - selections.avoid_eol(); - buffer.check_invariant(); -} - -template -BufferIterator prepare_insert(Buffer& buffer, const Selection& sel) -{ - switch (mode) - { - case InsertMode::Insert: - return buffer.iterator_at(sel.min()); - case InsertMode::Replace: - return Kakoune::erase(buffer, sel); - case InsertMode::Append: - { - // special case for end of lines, append to current line instead - auto pos = buffer.iterator_at(sel.max()); - return *pos == '\n' ? pos : utf8::next(pos); - } - case InsertMode::InsertAtLineBegin: - return buffer.iterator_at(sel.min().line); - case InsertMode::AppendAtLineEnd: - return buffer.iterator_at({sel.max().line, buffer[sel.max().line].length() - 1}); - case InsertMode::InsertAtNextLineBegin: - return buffer.iterator_at(sel.max().line+1); - case InsertMode::OpenLineBelow: - return buffer.insert(buffer.iterator_at(sel.max().line + 1), "\n"); - case InsertMode::OpenLineAbove: - return buffer.insert(buffer.iterator_at(sel.min().line), "\n"); - } - kak_assert(false); - return {}; -} - -template -void insert(Buffer& buffer, SelectionList& selections, memoryview strings) -{ - if (strings.empty()) - return; - - selections.update(); - for (size_t i = 0; i < selections.size(); ++i) - { - size_t index = selections.size() - 1 - i; - auto& sel = selections[index]; - auto pos = prepare_insert(buffer, sel); - const String& str = strings[std::min(index, strings.size()-1)]; - pos = buffer.insert(pos, str); - if (mode == InsertMode::Replace) - { - if (pos == buffer.end()) - --pos; - sel.anchor() = pos.coord(); - sel.cursor() = (str.empty() ? - pos : pos + str.byte_count_to(str.char_length() - 1)).coord(); - - // update following selections - auto changes = compute_modifications(buffer, selections.timestamp()); - for (size_t j = index+1; j < selections.size(); ++j) - { - auto& sel = selections[j]; - sel.anchor() = update_pos(changes, sel.anchor()); - sel.cursor() = update_pos(changes, sel.cursor()); - } - selections.update_timestamp(); - } - } - selections.update(); - selections.avoid_eol(); - selections.check_invariant(); - buffer.check_invariant(); -} - using namespace std::placeholders; enum class SelectMode @@ -172,7 +92,7 @@ constexpr Select make_select(T func) } template -void select_coord(const Buffer& buffer, ByteCoord coord, SelectionList& selections) +void select_coord(Buffer& buffer, ByteCoord coord, SelectionList& selections) { coord = buffer.clamp(coord); if (mode == SelectMode::Replace) @@ -405,7 +325,7 @@ void replace_with_char(Context& context, int) CharCount count = char_length(buffer, sel); strings.emplace_back(key.key, count); } - insert(buffer, selections, strings); + selections.insert(strings, InsertMode::Replace); }, "replace with char", "enter char to replace with\n"); } @@ -428,7 +348,7 @@ void for_each_char(Context& context, int) for (auto& c : sel) c = func(c); } - insert(context.buffer(), context.selections(), sels); + context.selections().insert(sels, InsertMode::Replace); } void command(Context& context, int) @@ -493,7 +413,7 @@ void pipe(Context& context, int) strings.push_back(str); } ScopedEdition edition(context); - insert(buffer, selections, strings); + selections.insert(strings, mode); }); } @@ -543,7 +463,7 @@ void erase_selections(Context& context, int) { RegisterManager::instance()['"'] = context.selections_content(); ScopedEdition edition(context); - erase(context.buffer(), context.selections()); + context.selections().erase(); } void cat_erase_selections(Context& context, int) @@ -553,7 +473,7 @@ void cat_erase_selections(Context& context, int) for (auto& sel : sels) str += sel; RegisterManager::instance()['"'] = memoryview(str); - erase(context.buffer(), context.selections()); + context.selections().erase(); } @@ -587,10 +507,8 @@ void paste(Context& context, int) } } ScopedEdition edition(context); - if (linewise) - insert(context.buffer(), context.selections(), strings); - else - insert(context.buffer(), context.selections(), strings); + context.selections().insert(strings, + linewise ? adapt_for_linewise(mode) : mode); } template @@ -717,7 +635,7 @@ void select_regex(Context& context, int) else RegisterManager::instance()['/'] = String{ex.str()}; if (not ex.empty() and not ex.str().empty()) - select_all_matches(context.buffer(), context.selections(), ex); + select_all_matches(context.selections(), ex); }); } @@ -729,7 +647,7 @@ void split_regex(Context& context, int) else RegisterManager::instance()['/'] = String{ex.str()}; if (not ex.empty() and not ex.str().empty()) - split_selections(context.buffer(), context.selections(), ex); + split_selections(context.selections(), ex); }); } @@ -775,7 +693,7 @@ void join_select_spaces(Context& context, int) return; context.selections() = selections; ScopedEdition edition(context); - insert(buffer, context.selections(), " "_str); + context.selections().insert(" "_str, InsertMode::Replace); } void join(Context& context, int param) @@ -853,7 +771,7 @@ void indent(Context& context, int) { ScopedEdition edition(context); SelectionList selections{buffer, std::move(sels)}; - insert(buffer, selections, indent); + selections.insert(indent, InsertMode::Insert); } } @@ -898,7 +816,7 @@ void deindent(Context& context, int) { ScopedEdition edition(context); SelectionList selections{context.buffer(), std::move(sels)}; - erase(context.buffer(), selections); + selections.erase(); } } @@ -1008,7 +926,7 @@ void rotate_selections_content(Context& context, int group) std::rotate(it, end-count, end); it = end; } - insert(context.buffer(), context.selections(), strings); + context.selections().insert(strings, InsertMode::Replace); context.selections().rotate_main(count); } @@ -1236,7 +1154,7 @@ void spaces_to_tabs(Context& context, int ts) } } -static boost::optional compute_modified_ranges(const Buffer& buffer, size_t timestamp) +static boost::optional compute_modified_ranges(Buffer& buffer, size_t timestamp) { std::vector ranges; for (auto& change : buffer.changes_since(timestamp)) @@ -1402,12 +1320,12 @@ KeyMap keymap = { '.', repeat_last_insert }, - { '%', [](Context& context, int) { select_whole_buffer(context.buffer(), context.selections()); } }, + { '%', [](Context& context, int) { select_whole_buffer(context.selections()); } }, { ':', command }, { '|', pipe }, { alt('|'), pipe }, - { ' ', [](Context& context, int count) { if (count == 0) clear_selections(context.buffer(), context.selections()); + { ' ', [](Context& context, int count) { if (count == 0) clear_selections(context.selections()); else keep_selection(context.selections(), count-1); } }, { alt(' '), [](Context& context, int count) { if (count == 0) flip_selections(context.selections()); else remove_selection(context.selections(), count-1); } }, diff --git a/src/normal.hh b/src/normal.hh index e72d0d20..c35027ce 100644 --- a/src/normal.hh +++ b/src/normal.hh @@ -11,18 +11,6 @@ namespace Kakoune class Context; -enum class InsertMode : unsigned -{ - Insert, - Append, - Replace, - InsertAtLineBegin, - InsertAtNextLineBegin, - AppendAtLineEnd, - OpenLineBelow, - OpenLineAbove -}; - using KeyMap = std::unordered_map>; extern KeyMap keymap; diff --git a/src/selection.cc b/src/selection.cc index 176bc076..7b99cc6d 100644 --- a/src/selection.cc +++ b/src/selection.cc @@ -2,6 +2,7 @@ #include "utf8.hh" #include "modification.hh" +#include "buffer_utils.hh" namespace Kakoune { @@ -102,24 +103,24 @@ struct UpdateErase } -SelectionList::SelectionList(const Buffer& buffer, Selection s, size_t timestamp) +SelectionList::SelectionList(Buffer& buffer, Selection s, size_t timestamp) : m_buffer(&buffer), m_selections({ s }), m_timestamp(timestamp) { check_invariant(); } -SelectionList::SelectionList(const Buffer& buffer, Selection s) +SelectionList::SelectionList(Buffer& buffer, Selection s) : SelectionList(buffer, s, buffer.timestamp()) {} -SelectionList::SelectionList(const Buffer& buffer, std::vector s, size_t timestamp) +SelectionList::SelectionList(Buffer& buffer, std::vector s, size_t timestamp) : m_buffer(&buffer), m_selections(std::move(s)), m_timestamp(timestamp) { kak_assert(size() > 0); check_invariant(); } -SelectionList::SelectionList(const Buffer& buffer, std::vector s) +SelectionList::SelectionList(Buffer& buffer, std::vector s) : SelectionList(buffer, std::move(s), buffer.timestamp()) {} @@ -221,4 +222,83 @@ void SelectionList::avoid_eol() _avoid_eol(buffer(), sel); } +void SelectionList::erase() +{ + for (auto& sel : reversed(m_selections)) + { + Kakoune::erase(*m_buffer, sel); + } + update(); + avoid_eol(); + m_buffer->check_invariant(); +} + +BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, InsertMode mode) +{ + switch (mode) + { + case InsertMode::Insert: + return buffer.iterator_at(sel.min()); + case InsertMode::Replace: + return erase(buffer, sel); + case InsertMode::Append: + { + // special case for end of lines, append to current line instead + auto pos = buffer.iterator_at(sel.max()); + return *pos == '\n' ? pos : utf8::next(pos); + } + case InsertMode::InsertAtLineBegin: + return buffer.iterator_at(sel.min().line); + case InsertMode::AppendAtLineEnd: + return buffer.iterator_at({sel.max().line, buffer[sel.max().line].length() - 1}); + case InsertMode::InsertAtNextLineBegin: + return buffer.iterator_at(sel.max().line+1); + case InsertMode::OpenLineBelow: + return buffer.insert(buffer.iterator_at(sel.max().line + 1), "\n"); + case InsertMode::OpenLineAbove: + return buffer.insert(buffer.iterator_at(sel.min().line), "\n"); + } + kak_assert(false); + return {}; +} + +void SelectionList::insert(memoryview strings, InsertMode mode) +{ + if (strings.empty()) + return; + + update(); + for (size_t i = 0; i < m_selections.size(); ++i) + { + size_t index = m_selections.size() - 1 - i; + auto& sel = m_selections[index]; + auto pos = prepare_insert(*m_buffer, sel, mode); + const String& str = strings[std::min(index, strings.size()-1)]; + pos = m_buffer->insert(pos, str); + if (mode == InsertMode::Replace) + { + if (pos == m_buffer->end()) + --pos; + sel.anchor() = pos.coord(); + sel.cursor() = (str.empty() ? + pos : pos + str.byte_count_to(str.char_length() - 1)).coord(); + + // update following selections + auto changes = compute_modifications(*m_buffer, timestamp()); + for (size_t j = index+1; j < m_selections.size(); ++j) + { + auto& sel = m_selections[j]; + sel.anchor() = update_pos(changes, sel.anchor()); + sel.cursor() = update_pos(changes, sel.cursor()); + } + update_timestamp(); + } + } + update(); + avoid_eol(); + check_invariant(); + m_buffer->check_invariant(); +} + + } diff --git a/src/selection.hh b/src/selection.hh index cd60f817..a1bf8e5c 100644 --- a/src/selection.hh +++ b/src/selection.hh @@ -55,12 +55,24 @@ static bool compare_selections(const Selection& lhs, const Selection& rhs) return lhs.min() < rhs.min(); } +enum class InsertMode : unsigned +{ + Insert, + Append, + Replace, + InsertAtLineBegin, + InsertAtNextLineBegin, + AppendAtLineEnd, + OpenLineBelow, + OpenLineAbove +}; + struct SelectionList { - SelectionList(const Buffer& buffer, Selection s); - SelectionList(const Buffer& buffer, Selection s, size_t timestamp); - SelectionList(const Buffer& buffer, std::vector s); - SelectionList(const Buffer& buffer, std::vector s, size_t timestamp); + SelectionList(Buffer& buffer, Selection s); + SelectionList(Buffer& buffer, Selection s, size_t timestamp); + SelectionList(Buffer& buffer, std::vector s); + SelectionList(Buffer& buffer, std::vector s, size_t timestamp); void update(); @@ -103,17 +115,7 @@ struct SelectionList const_iterator begin() const { return m_selections.begin(); } const_iterator end() const { return m_selections.end(); } - template - iterator insert(Args... args) - { - return m_selections.insert(std::forward(args)...); - } - - template - iterator erase(Args... args) - { - return m_selections.erase(std::forward(args)...); - } + void remove(size_t index) { m_selections.erase(begin() + index); } size_t size() const { return m_selections.size(); } @@ -140,22 +142,25 @@ struct SelectionList (*this)[i] = std::move((*this)[j]); } } - erase(begin() + i + 1, end()); + m_selections.erase(begin() + i + 1, end()); kak_assert(std::is_sorted(begin(), end(), compare_selections)); } void sort_and_merge_overlapping(); - const Buffer& buffer() const { return *m_buffer; } + Buffer& buffer() const { return *m_buffer; } size_t timestamp() const { return m_timestamp; } void update_timestamp() { m_timestamp = m_buffer->timestamp(); } + void insert(memoryview strings, InsertMode mode); + void erase(); + private: size_t m_main = 0; std::vector m_selections; - safe_ptr m_buffer; + safe_ptr m_buffer; size_t m_timestamp; }; diff --git a/src/selectors.cc b/src/selectors.cc index bcae7a06..fb416aff 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -429,15 +429,16 @@ Selection trim_partial_lines(const Buffer& buffer, const Selection& selection) return Selection(first.coord(), last.coord()); } -void select_whole_buffer(const Buffer& buffer, SelectionList& selections) +void select_whole_buffer(SelectionList& selections) { + auto& buffer = selections.buffer(); selections = SelectionList{ buffer, Selection({0,0}, buffer.back_coord()) }; } -void select_all_matches(const Buffer& buffer, SelectionList& selections, - const Regex& regex) +void select_all_matches(SelectionList& selections, const Regex& regex) { std::vector result; + auto& buffer = selections.buffer(); for (auto& sel : selections) { auto sel_end = utf8::next(buffer.iterator_at(sel.max())); @@ -466,10 +467,10 @@ void select_all_matches(const Buffer& buffer, SelectionList& selections, selections = std::move(result); } -void split_selections(const Buffer& buffer, SelectionList& selections, - const Regex& regex) +void split_selections(SelectionList& selections, const Regex& regex) { std::vector result; + auto& buffer = selections.buffer(); for (auto& sel : selections) { auto begin = buffer.iterator_at(sel.min()); diff --git a/src/selectors.hh b/src/selectors.hh index d5b0523d..717dff93 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -9,14 +9,14 @@ namespace Kakoune { -inline void clear_selections(const Buffer& buffer, SelectionList& selections) +inline void clear_selections(SelectionList& selections) { auto& sel = selections.main(); auto& pos = sel.cursor(); sel.anchor() = pos; selections.avoid_eol(); - selections = SelectionList{ buffer, std::move(sel) }; + selections = SelectionList{ selections.buffer(), std::move(sel) }; } inline void flip_selections(SelectionList& selections) @@ -41,7 +41,7 @@ inline void remove_selection(SelectionList& selections, int index) if (selections.size() > 1 and index < selections.size()) { size_t real_index = (index + selections.main_index() + 1) % selections.size(); - selections.erase(selections.begin() + real_index); + selections.remove(real_index); size_t main_index = selections.main_index(); if (real_index <= main_index) selections.set_main_index((main_index > 0 ? main_index @@ -207,7 +207,7 @@ Selection select_whole_paragraph(const Buffer& buffer, const Selection& selectio Selection select_whole_indent(const Buffer& buffer, const Selection& selection, ObjectFlags flags); Selection select_whole_lines(const Buffer& buffer, const Selection& selection); -void select_whole_buffer(const Buffer& buffer, SelectionList& selections); +void select_whole_buffer(SelectionList& selections); Selection trim_partial_lines(const Buffer& buffer, const Selection& selection); enum Direction { Forward, Backward }; @@ -266,10 +266,10 @@ Selection find_next_match(const Buffer& buffer, const Selection& sel, const Rege return {begin.coord(), end.coord(), std::move(captures)}; } -void select_all_matches(const Buffer& buffer, SelectionList& selections, +void select_all_matches(SelectionList& selections, const Regex& regex); -void split_selections(const Buffer& buffer, SelectionList& selections, +void split_selections(SelectionList& selections, const Regex& separator_regex); using CodepointPair = std::pair;