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(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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user