Editor: keep selections sorted and use an index for the main one

This commit is contained in:
Maxime Coste 2013-03-15 18:48:59 +01:00
parent a981d41cde
commit 354ae7ad89
5 changed files with 67 additions and 45 deletions

View File

@ -18,6 +18,7 @@ Editor::Editor(Buffer& buffer)
m_selections(buffer) m_selections(buffer)
{ {
m_selections.push_back(Selection(buffer.begin(), buffer.begin())); m_selections.push_back(Selection(buffer.begin(), buffer.begin()));
m_main_sel = 0;
} }
void Editor::erase() void Editor::erase()
@ -128,28 +129,31 @@ static bool compare_selections(const Selection& lhs, const Selection& rhs)
return lhs.begin() < rhs.begin(); 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) if (selections.size() == 1)
return; return;
Range back = selections.back(); const auto& main = selections[main_selection];
auto back_rank = std::count_if(selections.begin(), selections.end(), const auto main_begin = main.begin();
[&](const Selection& sel) main_selection = std::count_if(selections.begin(), selections.end(),
{ return sel.begin() <= back.begin(); }); [&](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); 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[i+1]))
if (overlaps(selections[i], selections[next]))
{ {
selections[i].merge_with(selections[next]); selections[i].merge_with(selections[i+1]);
selections.erase(selections.begin() + next); selections.erase(selections.begin() + i + 1);
if (i + 1 <= main_selection)
--main_selection;
} }
else else
++i; ++i;
@ -169,7 +173,7 @@ void Editor::move_selections(CharCount offset, SelectMode mode)
sel.last() = last; sel.last() = last;
sel.avoid_eol(); 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) void Editor::move_selections(LineCount offset, SelectMode mode)
@ -186,19 +190,21 @@ void Editor::move_selections(LineCount offset, SelectMode mode)
sel.last() = last; sel.last() = last;
sel.avoid_eol(); sel.avoid_eol();
} }
sort_and_merge_overlapping(m_selections); sort_and_merge_overlapping(m_selections, m_main_sel);
} }
void Editor::clear_selections() void Editor::clear_selections()
{ {
auto& sel = m_selections.back(); auto& sel = m_selections[m_main_sel];
auto& pos = sel.last(); auto& pos = sel.last();
if (*pos == '\n' and not pos.is_begin() and *utf8::previous(pos) != '\n') if (*pos == '\n' and not pos.is_begin() and *utf8::previous(pos) != '\n')
pos = utf8::previous(pos); pos = utf8::previous(pos);
sel.first() = 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(); check_invariant();
} }
@ -212,14 +218,23 @@ void Editor::flip_selections()
void Editor::keep_selection(int index) void Editor::keep_selection(int index)
{ {
if (index < m_selections.size()) 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(); check_invariant();
} }
void Editor::remove_selection(int index) void Editor::remove_selection(int index)
{ {
if (m_selections.size() > 1 and index < m_selections.size()) 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(); check_invariant();
} }
@ -229,14 +244,14 @@ void Editor::select(const Selection& selection, SelectMode mode)
m_selections = SelectionList{ selection }; m_selections = SelectionList{ selection };
else if (mode == SelectMode::Extend) else if (mode == SelectMode::Extend)
{ {
for (auto& sel : m_selections) m_selections[m_main_sel].merge_with(selection);
sel.merge_with(selection); sort_and_merge_overlapping(m_selections, m_main_sel);
sort_and_merge_overlapping(m_selections);
} }
else if (mode == SelectMode::Append) else if (mode == SelectMode::Append)
{ {
m_main_sel = m_selections.size();
m_selections.push_back(selection); m_selections.push_back(selection);
sort_and_merge_overlapping(m_selections); sort_and_merge_overlapping(m_selections, m_main_sel);
} }
else else
assert(false); assert(false);
@ -248,6 +263,7 @@ void Editor::select(SelectionList selections)
if (selections.empty()) if (selections.empty())
throw runtime_error("no selections"); throw runtime_error("no selections");
m_selections = std::move(selections); m_selections = std::move(selections);
m_main_sel = m_selections.size() - 1;
check_invariant(); check_invariant();
} }
@ -255,15 +271,16 @@ void Editor::select(const Selector& selector, SelectMode mode)
{ {
if (mode == SelectMode::Append) if (mode == SelectMode::Append)
{ {
auto& sel = m_selections.back(); auto& sel = m_selections[m_main_sel];
auto res = selector(sel); auto res = selector(sel);
if (res.captures().empty()) if (res.captures().empty())
res.captures() = sel.captures(); res.captures() = sel.captures();
m_main_sel = m_selections.size();
m_selections.push_back(res); m_selections.push_back(res);
} }
else if (mode == SelectMode::ReplaceMain) else if (mode == SelectMode::ReplaceMain)
{ {
auto& sel = m_selections.back(); auto& sel = m_selections[m_main_sel];
auto res = selector(sel); auto res = selector(sel);
sel.first() = res.first(); sel.first() = res.first();
sel.last() = res.last(); sel.last() = res.last();
@ -286,7 +303,7 @@ void Editor::select(const Selector& selector, SelectMode mode)
sel.captures() = std::move(res.captures()); sel.captures() = std::move(res.captures());
} }
} }
sort_and_merge_overlapping(m_selections); sort_and_merge_overlapping(m_selections, m_main_sel);
check_invariant(); check_invariant();
} }
@ -314,7 +331,8 @@ void Editor::multi_select(const MultiSelector& selector)
} }
if (new_selections.empty()) if (new_selections.empty())
throw nothing_selected(); 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); m_selections = std::move(new_selections);
check_invariant(); check_invariant();
} }
@ -360,7 +378,10 @@ bool Editor::undo()
LastModifiedRangeListener listener(buffer()); LastModifiedRangeListener listener(buffer());
bool res = m_buffer->undo(); bool res = m_buffer->undo();
if (res) if (res)
{
m_selections = SelectionList{ {listener.first(), listener.last()} }; m_selections = SelectionList{ {listener.first(), listener.last()} };
m_main_sel = 0;
}
check_invariant(); check_invariant();
return res; return res;
} }
@ -370,7 +391,10 @@ bool Editor::redo()
LastModifiedRangeListener listener(buffer()); LastModifiedRangeListener listener(buffer());
bool res = m_buffer->redo(); bool res = m_buffer->redo();
if (res) if (res)
{
m_selections = SelectionList{ {listener.first(), listener.last()} }; m_selections = SelectionList{ {listener.first(), listener.last()} };
m_main_sel = 0;
}
check_invariant(); check_invariant();
return res; return res;
} }
@ -379,12 +403,9 @@ void Editor::check_invariant() const
{ {
#ifdef KAK_DEBUG #ifdef KAK_DEBUG
assert(not m_selections.empty()); assert(not m_selections.empty());
assert(m_main_sel < m_selections.size());
m_selections.check_invariant(); m_selections.check_invariant();
assert(std::is_sorted(m_selections.begin(), m_selections.end(), compare_selections));
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));
#endif #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(); editor.check_invariant();
} }

View File

@ -73,8 +73,11 @@ public:
void select(SelectionList selections); void select(SelectionList selections);
void multi_select(const MultiSelector& selector); 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 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<String> selections_content() const; std::vector<String> selections_content() const;
bool undo(); bool undo();
@ -100,6 +103,7 @@ private:
safe_ptr<Buffer> m_buffer; safe_ptr<Buffer> m_buffer;
DynamicSelectionList m_selections; DynamicSelectionList m_selections;
size_t m_main_sel;
FilterGroup m_filters; FilterGroup m_filters;
}; };

View File

@ -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(); const bool forward = sel.first() <= sel.last();
BufferIterator begin = forward ? sel.first() : utf8::next(sel.last()); BufferIterator begin = forward ? sel.first() : utf8::next(sel.last());
BufferIterator end = forward ? sel.last() : utf8::next(sel.first()); 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 sel_colors = ColorRegistry::instance()[primary ? "PrimarySelection" : "SecondarySelection"];
ColorPair cur_colors = ColorRegistry::instance()[primary ? "PrimaryCursor" : "SecondaryCursor"]; ColorPair cur_colors = ColorRegistry::instance()[primary ? "PrimaryCursor" : "SecondaryCursor"];
highlight_range(display_buffer, begin, end, false, highlight_range(display_buffer, begin, end, false,

View File

@ -417,10 +417,7 @@ void do_rotate_selections(Context& context)
int count = context.numeric_param(); int count = context.numeric_param();
if (count == 0) if (count == 0)
count = 1; count = 1;
SelectionList sels = context.editor().selections(); context.editor().rotate_selections(count);
count %= sels.size();
std::rotate(sels.begin(), sels.begin() + count, sels.end());
context.editor().select(std::move(sels));
}; };
enum class SelectFlags enum class SelectFlags

View File

@ -12,7 +12,7 @@ namespace Kakoune
{ {
// Implementation in highlighters.cc // 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_tabulations(const OptionManager& options, DisplayBuffer& display_buffer);
void expand_unprintable(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({"tabulations", [this](DisplayBuffer& db) { expand_tabulations(m_options, db); }});
m_builtin_highlighters.append({"unprintable", expand_unprintable}); 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()) for (auto& option : m_options.flatten_options())
on_option_changed(*option); on_option_changed(*option);