Editor: keep selections sorted and use an index for the main one
This commit is contained in:
parent
a981d41cde
commit
354ae7ad89
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String> selections_content() const;
|
||||
|
||||
bool undo();
|
||||
|
@ -100,6 +103,7 @@ private:
|
|||
|
||||
safe_ptr<Buffer> m_buffer;
|
||||
DynamicSelectionList m_selections;
|
||||
size_t m_main_sel;
|
||||
FilterGroup m_filters;
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user