diff --git a/src/editor.cc b/src/editor.cc index 6ee3f433..b3572333 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -14,11 +14,8 @@ namespace Kakoune Editor::Editor(Buffer& buffer) : m_buffer(&buffer), m_edition_level(0), - m_selections(buffer) -{ - m_selections.push_back(Selection({}, {})); - m_main_sel = 0; -} + m_selections(buffer, { {{},{}} }) +{} void avoid_eol(const Buffer& buffer, BufferCoord& coord) { @@ -124,50 +121,6 @@ std::vector Editor::selections_content() const return contents; } -static bool compare_selections(const Selection& lhs, const Selection& rhs) -{ - return lhs.min() < rhs.min(); -} - -template -void merge_overlapping(SelectionList& selections, size_t& main_selection, - OverlapsFunc overlaps) -{ - kak_assert(std::is_sorted(selections.begin(), selections.end(), compare_selections)); - for (size_t i = 0; i+1 < selections.size() and selections.size() > 1;) - { - if (overlaps(selections[i], selections[i+1])) - { - selections[i].merge_with(selections[i+1]); - selections.erase(selections.begin() + i + 1); - if (i + 1 <= main_selection) - --main_selection; - } - else - ++i; - } -} - -void sort_and_merge_overlapping(SelectionList& selections, size_t& main_selection) -{ - if (selections.size() == 1) - return; - - const auto& main = selections[main_selection]; - const auto main_begin = main.min(); - main_selection = std::count_if(selections.begin(), selections.end(), - [&](const Selection& sel) { - auto begin = sel.min(); - if (begin == main_begin) - return &sel < &main; - else - return begin < main_begin; - }); - std::stable_sort(selections.begin(), selections.end(), compare_selections); - - merge_overlapping(selections, main_selection, overlaps); -} - BufferCoord Editor::offset_coord(BufferCoord coord, CharCount offset) { auto& line = buffer()[coord.line]; @@ -186,7 +139,7 @@ void Editor::move_selections(CharCount offset, SelectMode mode) sel.last() = last; avoid_eol(*m_buffer, sel); } - sort_and_merge_overlapping(m_selections, m_main_sel); + m_selections.sort_and_merge_overlapping(); } BufferCoord Editor::offset_coord(BufferCoord coord, LineCount offset) @@ -209,19 +162,17 @@ void Editor::move_selections(LineCount offset, SelectMode mode) sel.last() = pos; avoid_eol(*m_buffer, sel); } - sort_and_merge_overlapping(m_selections, m_main_sel); + m_selections.sort_and_merge_overlapping(); } void Editor::clear_selections() { - auto& sel = m_selections[m_main_sel]; + auto& sel = m_selections.main(); auto& pos = sel.last(); avoid_eol(*m_buffer, pos); sel.first() = pos; - 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; + m_selections = SelectionList{ std::move(sel) }; check_invariant(); } @@ -236,9 +187,8 @@ void Editor::keep_selection(int index) { if (index < m_selections.size()) { - size_t real_index = (index + m_main_sel + 1) % m_selections.size(); + size_t real_index = (index + m_selections.main_index() + 1) % m_selections.size(); m_selections = SelectionList{ std::move(m_selections[real_index]) }; - m_main_sel = 0; } check_invariant(); } @@ -247,11 +197,12 @@ void Editor::remove_selection(int index) { if (m_selections.size() > 1 and index < m_selections.size()) { - size_t real_index = (index + m_main_sel + 1) % m_selections.size(); + size_t real_index = (index + m_selections.main_index() + 1) % m_selections.size(); m_selections.erase(m_selections.begin() + real_index); - if (real_index <= m_main_sel) - m_main_sel = (m_main_sel > 0 ? m_main_sel - : m_selections.size()) - 1; + size_t main_index = m_selections.main_index(); + if (real_index <= main_index) + m_selections.set_main_index((main_index > 0 ? main_index + : m_selections.size()) - 1); } check_invariant(); } @@ -259,21 +210,17 @@ void Editor::remove_selection(int index) void Editor::select(const Selection& selection, SelectMode mode) { if (mode == SelectMode::Replace) - { m_selections = SelectionList{ selection }; - m_main_sel = 0; - } else if (mode == SelectMode::Extend) { - m_selections[m_main_sel].merge_with(selection); - m_selections = SelectionList{ std::move(m_selections[m_main_sel]) }; - m_main_sel = 0; + m_selections.main().merge_with(selection); + m_selections = SelectionList{ std::move(m_selections.main()) }; } else if (mode == SelectMode::Append) { - m_main_sel = m_selections.size(); m_selections.push_back(selection); - sort_and_merge_overlapping(m_selections, m_main_sel); + m_selections.set_main_index(m_selections.size()-1); + m_selections.sort_and_merge_overlapping(); } else kak_assert(false); @@ -285,7 +232,6 @@ 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(); } @@ -293,16 +239,16 @@ void Editor::select(const Selector& selector, SelectMode mode) { if (mode == SelectMode::Append) { - auto& sel = m_selections[m_main_sel]; + auto& sel = m_selections.main(); auto res = selector(*m_buffer, sel); if (res.captures().empty()) res.captures() = sel.captures(); - m_main_sel = m_selections.size(); m_selections.push_back(res); + m_selections.set_main_index(m_selections.size() - 1); } else if (mode == SelectMode::ReplaceMain) { - auto& sel = m_selections[m_main_sel]; + auto& sel = m_selections.main(); auto res = selector(*m_buffer, sel); sel.first() = res.first(); sel.last() = res.last(); @@ -325,7 +271,7 @@ void Editor::select(const Selector& selector, SelectMode mode) sel.captures() = std::move(res.captures()); } } - sort_and_merge_overlapping(m_selections, m_main_sel); + m_selections.sort_and_merge_overlapping(); check_invariant(); } @@ -353,8 +299,8 @@ void Editor::multi_select(const MultiSelector& selector) } if (new_selections.empty()) throw nothing_selected(); - m_main_sel = new_selections.size() - 1; - sort_and_merge_overlapping(new_selections, m_main_sel); + new_selections.set_main_index(new_selections.size() - 1); + new_selections.sort_and_merge_overlapping(); m_selections = std::move(new_selections); check_invariant(); } @@ -403,8 +349,8 @@ bool Editor::undo() if (res and not listener.ranges().empty()) { m_selections = std::move(listener.ranges()); - m_main_sel = m_selections.size() - 1; - merge_overlapping(m_selections, m_main_sel, std::bind(touches, std::ref(buffer()), _1, _2)); + m_selections.set_main_index(m_selections.size() - 1); + m_selections.merge_overlapping(std::bind(touches, std::ref(buffer()), _1, _2)); } check_invariant(); return res; @@ -418,8 +364,8 @@ bool Editor::redo() if (res and not listener.ranges().empty()) { m_selections = std::move(listener.ranges()); - m_main_sel = m_selections.size() - 1; - merge_overlapping(m_selections, m_main_sel, std::bind(touches, std::ref(buffer()), _1, _2)); + m_selections.set_main_index(m_selections.size() - 1); + m_selections.merge_overlapping(std::bind(touches, std::ref(buffer()), _1, _2)); } check_invariant(); return res; @@ -429,10 +375,8 @@ void Editor::check_invariant() const { #ifdef KAK_DEBUG kak_assert(not m_selections.empty()); - kak_assert(m_main_sel < m_selections.size()); m_selections.check_invariant(); buffer().check_invariant(); - kak_assert(std::is_sorted(m_selections.begin(), m_selections.end(), compare_selections)); #endif } diff --git a/src/editor.hh b/src/editor.hh index 8778a1a0..4037a76e 100644 --- a/src/editor.hh +++ b/src/editor.hh @@ -71,11 +71,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(); } + void rotate_selections(int count) { m_selections.rotate_main(count); } const SelectionList& selections() const { return m_selections; } - const Selection& main_selection() const { return m_selections[m_main_sel]; } - size_t main_selection_index() const { return m_main_sel; } + const Selection& main_selection() const { return m_selections.main(); } + size_t main_selection_index() const { return m_selections.main_index(); } std::vector selections_content() const; bool undo(); @@ -97,7 +97,6 @@ private: safe_ptr m_buffer; DynamicSelectionList m_selections; - size_t m_main_sel; }; struct scoped_edition @@ -116,7 +115,6 @@ private: void avoid_eol(const Buffer& buffer, BufferCoord& coord); void avoid_eol(const Buffer& buffer, Range& sel); -void sort_and_merge_overlapping(SelectionList& selections, size_t& main_selection); } diff --git a/src/input_handler.cc b/src/input_handler.cc index b8c4470f..dea75944 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -1082,7 +1082,7 @@ private: } } } - sort_and_merge_overlapping(editor.m_selections, editor.m_main_sel); + editor.m_selections.sort_and_merge_overlapping(); editor.check_invariant(); } diff --git a/src/selection.cc b/src/selection.cc index a8ab889c..4fc7caf7 100644 --- a/src/selection.cc +++ b/src/selection.cc @@ -103,6 +103,7 @@ void SelectionList::update_erase(const Buffer& buffer, BufferCoord begin, Buffer void SelectionList::check_invariant() const { + kak_assert(m_main < size()); for (size_t i = 0; i+1 < size(); ++ i) kak_assert((*this)[i].min() <= (*this)[i+1].min()); } diff --git a/src/selection.hh b/src/selection.hh index bb52602d..6dae9e95 100644 --- a/src/selection.hh +++ b/src/selection.hh @@ -71,6 +71,11 @@ private: CaptureList m_captures; }; +static bool compare_selections(const Selection& lhs, const Selection& rhs) +{ + return lhs.min() < rhs.min(); +} + struct SelectionList : std::vector { SelectionList() = default; @@ -80,6 +85,52 @@ struct SelectionList : std::vector void update_erase(const Buffer& buffer, BufferCoord begin, BufferCoord end); void check_invariant() const; + + const Selection& main() const { return (*this)[m_main]; } + Selection& main() { return (*this)[m_main]; } + size_t main_index() const { return m_main; } + void set_main_index(size_t main) { kak_assert(main < size()); m_main = main; } + + void rotate_main(int count) { m_main = (m_main + count) % size(); } + + template + void merge_overlapping(OverlapsFunc overlaps) + { + kak_assert(std::is_sorted(begin(), end(), compare_selections)); + for (size_t i = 0; i+1 < size() and size() > 1;) + { + if (overlaps((*this)[i], (*this)[i+1])) + { + (*this)[i].merge_with((*this)[i+1]); + erase(begin() + i + 1); + if (i + 1 <= m_main) + --m_main; + } + else + ++i; + } + } + + void sort_and_merge_overlapping() + { + if (size() == 1) + return; + + const auto& main = this->main(); + const auto main_begin = main.min(); + m_main = std::count_if(begin(), end(), [&](const Selection& sel) { + auto begin = sel.min(); + if (begin == main_begin) + return &sel < &main; + else + return begin < main_begin; + }); + std::stable_sort(begin(), end(), compare_selections); + merge_overlapping(overlaps); + } + +private: + size_t m_main = 0; }; }