diff --git a/src/buffer_utils.hh b/src/buffer_utils.hh index bd617595..f47c2c26 100644 --- a/src/buffer_utils.hh +++ b/src/buffer_utils.hh @@ -24,20 +24,6 @@ inline CharCount char_length(const Buffer& buffer, const Selection& range) utf8::next(buffer.iterator_at(range.max()))); } -inline void avoid_eol(const Buffer& buffer, ByteCoord& coord) -{ - const auto column = coord.column; - const auto& line = buffer[coord.line]; - if (column != 0 and column == line.length() - 1) - coord.column = line.byte_count_to(line.char_length() - 2); -} - -inline void avoid_eol(const Buffer& buffer, Selection& sel) -{ - avoid_eol(buffer, sel.anchor()); - avoid_eol(buffer, sel.cursor()); -} - CharCount get_column(const Buffer& buffer, CharCount tabstop, ByteCoord coord); diff --git a/src/input_handler.cc b/src/input_handler.cc index 08cd7f44..459f6751 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -667,7 +667,7 @@ public: } else if (key == Key::Backspace) { - for (auto& sel : context().selections()) + for (auto& sel : reversed(context().selections())) { if (sel.cursor() == ByteCoord{0,0}) continue; @@ -677,7 +677,7 @@ public: } else if (key == Key::Erase) { - for (auto& sel : context().selections()) + for (auto& sel : reversed(context().selections())) { auto pos = buffer.iterator_at(sel.cursor()); buffer.erase(pos, utf8::next(pos)); @@ -761,11 +761,12 @@ private: auto& selections = context().selections(); for (size_t i = 0; i < selections.size(); ++i) { - size_t index = std::min(i, strings.size()-1); - buffer.insert(buffer.iterator_at(selections[i].cursor()), - strings[index]); - selections.update(); + size_t index = selections.size() - 1 - i; + const String& str = strings[std::min(index, strings.size()-1)]; + buffer.insert(buffer.iterator_at(selections[index].cursor()), + str); } + selections.update(); } void insert(Codepoint key) @@ -773,11 +774,9 @@ private: auto str = codepoint_to_str(key); auto& buffer = context().buffer(); auto& selections = context().selections(); - for (auto& sel : selections) - { + for (auto& sel : reversed(selections)) buffer.insert(buffer.iterator_at(sel.cursor()), str); - selections.update(); - } + selections.update(); context().hooks().run_hook("InsertChar", str, context()); } @@ -786,7 +785,7 @@ private: SelectionList& selections = context().selections(); Buffer& buffer = context().buffer(); - for (auto& sel : selections) + for (auto& sel : reversed(selections)) { ByteCoord anchor, cursor; switch (mode) @@ -835,10 +834,10 @@ private: if (buffer.is_end(cursor)) cursor = buffer.char_prev(cursor); - selections.update(); sel.anchor() = anchor; sel.cursor() = cursor; } + selections.update(); if (mode == InsertMode::OpenLineBelow or mode == InsertMode::OpenLineAbove) { insert('\n'); @@ -859,12 +858,13 @@ private: void on_disabled() override { - for (auto& sel : context().selections()) + auto& selections = context().selections(); + for (auto& sel : selections) { if (m_insert_mode == InsertMode::Append and sel.cursor().column > 0) sel.cursor() = context().buffer().char_prev(sel.cursor()); - avoid_eol(context().buffer(), sel); } + selections.avoid_eol(); } enum class Mode { Default, Complete, InsertReg }; diff --git a/src/normal.cc b/src/normal.cc index a8386b32..ff38c29d 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -23,13 +23,12 @@ namespace Kakoune void erase(Buffer& buffer, SelectionList& selections) { - for (auto& sel : selections) + for (auto& sel : reversed(selections)) { erase(buffer, sel); - selections.update(); - avoid_eol(buffer, sel); } - selections.check_invariant(); + selections.update(); + selections.avoid_eol(); buffer.check_invariant(); } @@ -66,20 +65,24 @@ BufferIterator prepare_insert(Buffer& buffer, const Selection& sel) template void insert(Buffer& buffer, SelectionList& selections, const String& str) { - for (auto& sel : selections) + for (auto& sel : reversed(selections)) { auto pos = prepare_insert(buffer, sel); pos = buffer.insert(pos, str); - selections.update(); - if (mode == InsertMode::Replace and pos != buffer.end()) + if (mode == InsertMode::Replace) { + if (pos == buffer.end()) + --pos; sel.anchor() = pos.coord(); sel.cursor() = str.empty() ? pos.coord() : (pos + str.byte_count_to(str.char_length() - 1)).coord(); } - avoid_eol(buffer, sel); } - selections.check_invariant(); + if (mode == InsertMode::Replace) + selections.set_timestamp(buffer.timestamp()); + else + selections.update(); + selections.avoid_eol(); buffer.check_invariant(); } @@ -90,20 +93,25 @@ void insert(Buffer& buffer, SelectionList& selections, memoryview string return; for (size_t i = 0; i < selections.size(); ++i) { - auto& sel = selections[i]; + size_t index = selections.size() - 1 - i; + auto& sel = selections[index]; auto pos = prepare_insert(buffer, sel); - const String& str = strings[std::min(i, strings.size()-1)]; + const String& str = strings[std::min(index, strings.size()-1)]; pos = buffer.insert(pos, str); - selections.update(); - if (mode == InsertMode::Replace and pos != buffer.end()) + 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(); } - avoid_eol(buffer, sel); } - selections.check_invariant(); + if (mode == InsertMode::Replace) + selections.set_timestamp(buffer.timestamp()); + else + selections.update(); + selections.avoid_eol(); buffer.check_invariant(); } @@ -1352,9 +1360,9 @@ void move(Context& context, int count) : context.buffer().offset_coord(sel.cursor(), offset); sel.anchor() = mode == SelectMode::Extend ? sel.anchor() : cursor; - sel.cursor() = cursor; - avoid_eol(context.buffer(), sel); + sel.cursor() = cursor; } + selections.avoid_eol(); selections.sort_and_merge_overlapping(); } diff --git a/src/selection.cc b/src/selection.cc index 746253f9..1e8356ab 100644 --- a/src/selection.cc +++ b/src/selection.cc @@ -188,5 +188,31 @@ void SelectionList::sort_and_merge_overlapping() std::stable_sort(begin(), end(), compare_selections); merge_overlapping(overlaps); } +namespace +{ + +inline void _avoid_eol(const Buffer& buffer, ByteCoord& coord) +{ + const auto column = coord.column; + const auto& line = buffer[coord.line]; + if (column != 0 and column == line.length() - 1) + coord.column = line.byte_count_to(line.char_length() - 2); +} + + +inline void _avoid_eol(const Buffer& buffer, Selection& sel) +{ + _avoid_eol(buffer, sel.anchor()); + _avoid_eol(buffer, sel.cursor()); +} + +} + +void SelectionList::avoid_eol() +{ + update(); + for (auto& sel : m_selections) + _avoid_eol(buffer(), sel); +} } diff --git a/src/selection.hh b/src/selection.hh index f8727b90..5f0ce459 100644 --- a/src/selection.hh +++ b/src/selection.hh @@ -73,6 +73,8 @@ struct SelectionList void rotate_main(int count) { m_main = (m_main + count) % size(); } + void avoid_eol(); + void push_back(const Selection& sel) { m_selections.push_back(sel); } void push_back(Selection&& sel) { m_selections.push_back(std::move(sel)); } @@ -92,6 +94,10 @@ struct SelectionList iterator begin() { return m_selections.begin(); } iterator end() { return m_selections.end(); } + using reverse_iterator = std::vector::reverse_iterator; + reverse_iterator rbegin() { return m_selections.rbegin(); } + reverse_iterator rend() { return m_selections.rend(); } + using const_iterator = std::vector::const_iterator; const_iterator begin() const { return m_selections.begin(); } const_iterator end() const { return m_selections.end(); } diff --git a/src/selectors.hh b/src/selectors.hh index d98b73bf..d5b0523d 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -13,8 +13,8 @@ inline void clear_selections(const Buffer& buffer, SelectionList& selections) { auto& sel = selections.main(); auto& pos = sel.cursor(); - avoid_eol(buffer, pos); sel.anchor() = pos; + selections.avoid_eol(); selections = SelectionList{ buffer, std::move(sel) }; }