diff --git a/src/editor.cc b/src/editor.cc index b08a00ea..a3d012b6 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -18,6 +18,7 @@ Editor::Editor(Buffer& buffer) m_selections(buffer) { m_selections.push_back(Selection(buffer.begin(), buffer.begin())); + m_main_sel = 0; } void Editor::erase() @@ -128,28 +129,31 @@ static bool compare_selections(const Selection& lhs, const Selection& rhs) return lhs.begin() < rhs.begin(); } -static void sort_and_merge_overlapping(SelectionList& selections) +static void sort_and_merge_overlapping(SelectionList& selections, size_t& main_selection) { if (selections.size() == 1) return; - Range back = selections.back(); - auto back_rank = std::count_if(selections.begin(), selections.end(), - [&](const Selection& sel) - { return sel.begin() <= back.begin(); }); + const auto& main = selections[main_selection]; + const auto main_begin = main.begin(); + main_selection = std::count_if(selections.begin(), selections.end(), + [&](const Selection& sel) { + auto begin = sel.begin(); + if (begin == main_begin) + return &sel < &main; + else + return sel.begin() < main_begin; + }); std::stable_sort(selections.begin(), selections.end(), compare_selections); - if (back_rank < selections.size()) - std::rotate(selections.begin(), selections.begin() + back_rank, - selections.end()); - assert(selections.back() == back); - for (size_t i = 0; i < selections.size() and selections.size() > 1;) + for (size_t i = 0; i+1 < selections.size() and selections.size() > 1;) { - size_t next = (i + 1) % selections.size(); - if (overlaps(selections[i], selections[next])) + if (overlaps(selections[i], selections[i+1])) { - selections[i].merge_with(selections[next]); - selections.erase(selections.begin() + next); + selections[i].merge_with(selections[i+1]); + selections.erase(selections.begin() + i + 1); + if (i + 1 <= main_selection) + --main_selection; } else ++i; @@ -169,7 +173,7 @@ void Editor::move_selections(CharCount offset, SelectMode mode) sel.last() = last; sel.avoid_eol(); } - sort_and_merge_overlapping(m_selections); + sort_and_merge_overlapping(m_selections, m_main_sel); } void Editor::move_selections(LineCount offset, SelectMode mode) @@ -186,19 +190,21 @@ void Editor::move_selections(LineCount offset, SelectMode mode) sel.last() = last; sel.avoid_eol(); } - sort_and_merge_overlapping(m_selections); + sort_and_merge_overlapping(m_selections, m_main_sel); } void Editor::clear_selections() { - auto& sel = m_selections.back(); + auto& sel = m_selections[m_main_sel]; auto& pos = sel.last(); if (*pos == '\n' and not pos.is_begin() and *utf8::previous(pos) != '\n') pos = utf8::previous(pos); sel.first() = pos; - m_selections.erase(m_selections.begin(), m_selections.end() - 1); + m_selections.erase(m_selections.begin(), m_selections.begin() + m_main_sel); + m_selections.erase(m_selections.begin() + 1, m_selections.end()); + m_main_sel = 0; check_invariant(); } @@ -212,14 +218,23 @@ void Editor::flip_selections() void Editor::keep_selection(int index) { if (index < m_selections.size()) - m_selections = SelectionList{ std::move(m_selections[index]) }; + { + size_t real_index = (index + m_main_sel + 1) % m_selections.size(); + m_selections = SelectionList{ std::move(m_selections[real_index]) }; + m_main_sel = 0; + } check_invariant(); } void Editor::remove_selection(int index) { if (m_selections.size() > 1 and index < m_selections.size()) - m_selections.erase(m_selections.begin() + index); + { + size_t real_index = (index + m_main_sel + 1) % m_selections.size(); + m_selections.erase(m_selections.begin() + real_index); + if (real_index <= m_main_sel) + --m_main_sel; + } check_invariant(); } @@ -229,14 +244,14 @@ void Editor::select(const Selection& selection, SelectMode mode) m_selections = SelectionList{ selection }; else if (mode == SelectMode::Extend) { - for (auto& sel : m_selections) - sel.merge_with(selection); - sort_and_merge_overlapping(m_selections); + m_selections[m_main_sel].merge_with(selection); + sort_and_merge_overlapping(m_selections, m_main_sel); } else if (mode == SelectMode::Append) { + m_main_sel = m_selections.size(); m_selections.push_back(selection); - sort_and_merge_overlapping(m_selections); + sort_and_merge_overlapping(m_selections, m_main_sel); } else assert(false); @@ -248,6 +263,7 @@ void Editor::select(SelectionList selections) if (selections.empty()) throw runtime_error("no selections"); m_selections = std::move(selections); + m_main_sel = m_selections.size() - 1; check_invariant(); } @@ -255,15 +271,16 @@ void Editor::select(const Selector& selector, SelectMode mode) { if (mode == SelectMode::Append) { - auto& sel = m_selections.back(); + auto& sel = m_selections[m_main_sel]; auto res = selector(sel); if (res.captures().empty()) res.captures() = sel.captures(); + m_main_sel = m_selections.size(); m_selections.push_back(res); } else if (mode == SelectMode::ReplaceMain) { - auto& sel = m_selections.back(); + auto& sel = m_selections[m_main_sel]; auto res = selector(sel); sel.first() = res.first(); sel.last() = res.last(); @@ -286,7 +303,7 @@ void Editor::select(const Selector& selector, SelectMode mode) sel.captures() = std::move(res.captures()); } } - sort_and_merge_overlapping(m_selections); + sort_and_merge_overlapping(m_selections, m_main_sel); check_invariant(); } @@ -314,7 +331,8 @@ void Editor::multi_select(const MultiSelector& selector) } if (new_selections.empty()) throw nothing_selected(); - sort_and_merge_overlapping(new_selections); + m_main_sel = new_selections.size() - 1; + sort_and_merge_overlapping(new_selections, m_main_sel); m_selections = std::move(new_selections); check_invariant(); } @@ -360,7 +378,10 @@ bool Editor::undo() LastModifiedRangeListener listener(buffer()); bool res = m_buffer->undo(); if (res) + { m_selections = SelectionList{ {listener.first(), listener.last()} }; + m_main_sel = 0; + } check_invariant(); return res; } @@ -370,7 +391,10 @@ bool Editor::redo() LastModifiedRangeListener listener(buffer()); bool res = m_buffer->redo(); if (res) + { m_selections = SelectionList{ {listener.first(), listener.last()} }; + m_main_sel = 0; + } check_invariant(); return res; } @@ -379,12 +403,9 @@ void Editor::check_invariant() const { #ifdef KAK_DEBUG assert(not m_selections.empty()); + assert(m_main_sel < m_selections.size()); m_selections.check_invariant(); - - auto it = ++std::max_element(m_selections.begin(), m_selections.end(), - compare_selections); - assert(std::is_sorted(m_selections.begin(), it, compare_selections)); - assert(std::is_sorted(it, m_selections.end(), compare_selections)); + assert(std::is_sorted(m_selections.begin(), m_selections.end(), compare_selections)); #endif } @@ -477,7 +498,7 @@ IncrementalInserter::IncrementalInserter(Editor& editor, InsertMode mode) } } } - sort_and_merge_overlapping(editor.m_selections); + sort_and_merge_overlapping(editor.m_selections, editor.m_main_sel); editor.check_invariant(); } diff --git a/src/editor.hh b/src/editor.hh index a1f27265..6a58eb49 100644 --- a/src/editor.hh +++ b/src/editor.hh @@ -73,8 +73,11 @@ public: void select(SelectionList selections); void multi_select(const MultiSelector& selector); + void rotate_selections(int count) { m_main_sel = (m_main_sel + count) % m_selections.size(); } + const SelectionList& selections() const { return m_selections; } - const Selection& main_selection() const { return m_selections.back(); } + const Selection& main_selection() const { return m_selections[m_main_sel]; } + size_t main_selection_index() const { return m_main_sel; } std::vector selections_content() const; bool undo(); @@ -100,6 +103,7 @@ private: safe_ptr m_buffer; DynamicSelectionList m_selections; + size_t m_main_sel; FilterGroup m_filters; }; diff --git a/src/highlighters.cc b/src/highlighters.cc index 48212c99..c3898176 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -259,16 +259,16 @@ void show_line_numbers(DisplayBuffer& display_buffer) } } -void highlight_selections(const SelectionList& selections, DisplayBuffer& display_buffer) +void highlight_selections(const Editor& editor, DisplayBuffer& display_buffer) { - for (size_t i = 0; i < selections.size(); ++i) + for (size_t i = 0; i < editor.selections().size(); ++i) { - auto& sel = selections[i]; + auto& sel = editor.selections()[i]; const bool forward = sel.first() <= sel.last(); BufferIterator begin = forward ? sel.first() : utf8::next(sel.last()); BufferIterator end = forward ? sel.last() : utf8::next(sel.first()); - const bool primary = (i == selections.size() - 1); + const bool primary = (i == editor.main_selection_index()); ColorPair sel_colors = ColorRegistry::instance()[primary ? "PrimarySelection" : "SecondarySelection"]; ColorPair cur_colors = ColorRegistry::instance()[primary ? "PrimaryCursor" : "SecondaryCursor"]; highlight_range(display_buffer, begin, end, false, diff --git a/src/main.cc b/src/main.cc index 19168bf8..594b7d30 100644 --- a/src/main.cc +++ b/src/main.cc @@ -417,10 +417,7 @@ void do_rotate_selections(Context& context) int count = context.numeric_param(); if (count == 0) count = 1; - SelectionList sels = context.editor().selections(); - count %= sels.size(); - std::rotate(sels.begin(), sels.begin() + count, sels.end()); - context.editor().select(std::move(sels)); + context.editor().rotate_selections(count); }; enum class SelectFlags diff --git a/src/window.cc b/src/window.cc index f5fba1f9..56ac12f3 100644 --- a/src/window.cc +++ b/src/window.cc @@ -12,7 +12,7 @@ namespace Kakoune { // Implementation in highlighters.cc -void highlight_selections(const SelectionList& selections, DisplayBuffer& display_buffer); +void highlight_selections(const Editor& editor, DisplayBuffer& display_buffer); void expand_tabulations(const OptionManager& options, DisplayBuffer& display_buffer); void expand_unprintable(DisplayBuffer& display_buffer); @@ -27,7 +27,7 @@ Window::Window(Buffer& buffer) m_builtin_highlighters.append({"tabulations", [this](DisplayBuffer& db) { expand_tabulations(m_options, db); }}); m_builtin_highlighters.append({"unprintable", expand_unprintable}); - m_builtin_highlighters.append({"selections", [this](DisplayBuffer& db) { highlight_selections(selections(), db); }}); + m_builtin_highlighters.append({"selections", [this](DisplayBuffer& db) { highlight_selections(*this, db); }}); for (auto& option : m_options.flatten_options()) on_option_changed(*option);