Make it harder to have an invalid SelectionList
This commit is contained in:
parent
a06094b00e
commit
11d9b60766
|
@ -19,14 +19,12 @@ DynamicSelectionList& DynamicSelectionList::operator=(SelectionList selections)
|
||||||
|
|
||||||
void DynamicSelectionList::on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
void DynamicSelectionList::on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
||||||
{
|
{
|
||||||
update_insert(begin, end, at_end);
|
update();
|
||||||
set_timestamp(buffer.timestamp());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicSelectionList::on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
void DynamicSelectionList::on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
||||||
{
|
{
|
||||||
update_erase(begin, end, at_end);
|
update();
|
||||||
set_timestamp(buffer.timestamp());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -739,7 +739,7 @@ void split_lines(Context& context, int)
|
||||||
{
|
{
|
||||||
auto& selections = context.selections();
|
auto& selections = context.selections();
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
SelectionList res(context.buffer());
|
std::vector<Selection> res;
|
||||||
for (auto& sel : selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
if (sel.anchor().line == sel.cursor().line)
|
if (sel.anchor().line == sel.cursor().line)
|
||||||
|
@ -754,14 +754,13 @@ void split_lines(Context& context, int)
|
||||||
res.push_back({line, {line, buffer[line].length()-1}});
|
res.push_back({line, {line, buffer[line].length()-1}});
|
||||||
res.push_back({max.line, max});
|
res.push_back({max.line, max});
|
||||||
}
|
}
|
||||||
res.set_main_index(res.size() - 1);
|
|
||||||
selections = std::move(res);
|
selections = std::move(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
void join_select_spaces(Context& context, int)
|
void join_select_spaces(Context& context, int)
|
||||||
{
|
{
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
SelectionList selections(buffer);
|
std::vector<Selection> selections;
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
for (LineCount line = sel.min().line; line <= sel.max().line; ++line)
|
for (LineCount line = sel.min().line; line <= sel.max().line; ++line)
|
||||||
|
@ -776,7 +775,6 @@ void join_select_spaces(Context& context, int)
|
||||||
}
|
}
|
||||||
if (selections.empty())
|
if (selections.empty())
|
||||||
return;
|
return;
|
||||||
selections.sort_and_merge_overlapping();
|
|
||||||
context.selections() = selections;
|
context.selections() = selections;
|
||||||
ScopedEdition edition(context);
|
ScopedEdition edition(context);
|
||||||
insert<InsertMode::Replace>(buffer, context.selections(), " ");
|
insert<InsertMode::Replace>(buffer, context.selections(), " ");
|
||||||
|
@ -801,7 +799,7 @@ void keep(Context& context, int)
|
||||||
if (ex.empty())
|
if (ex.empty())
|
||||||
return;
|
return;
|
||||||
const Buffer& buffer = context.buffer();
|
const Buffer& buffer = context.buffer();
|
||||||
SelectionList keep(buffer);
|
std::vector<Selection> keep;
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
if (boost::regex_search(buffer.iterator_at(sel.min()),
|
if (boost::regex_search(buffer.iterator_at(sel.min()),
|
||||||
|
@ -823,7 +821,7 @@ void keep_pipe(Context& context, int)
|
||||||
return;
|
return;
|
||||||
const Buffer& buffer = context.buffer();
|
const Buffer& buffer = context.buffer();
|
||||||
auto& shell_manager = ShellManager::instance();
|
auto& shell_manager = ShellManager::instance();
|
||||||
SelectionList keep(buffer);
|
std::vector<Selection> keep;
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
@ -844,7 +842,7 @@ void indent(Context& context, int)
|
||||||
String indent = indent_width == 0 ? "\t" : String{' ', indent_width};
|
String indent = indent_width == 0 ? "\t" : String{' ', indent_width};
|
||||||
|
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
SelectionList sels(buffer);
|
std::vector<Selection> sels;
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
||||||
|
@ -856,7 +854,8 @@ void indent(Context& context, int)
|
||||||
if (not sels.empty())
|
if (not sels.empty())
|
||||||
{
|
{
|
||||||
ScopedEdition edition(context);
|
ScopedEdition edition(context);
|
||||||
insert<InsertMode::Insert>(buffer, sels, indent);
|
SelectionList selections{buffer, std::move(sels)};
|
||||||
|
insert<InsertMode::Insert>(buffer, selections, indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,7 +868,7 @@ void deindent(Context& context, int)
|
||||||
indent_width = tabstop;
|
indent_width = tabstop;
|
||||||
|
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
SelectionList sels(buffer);
|
std::vector<Selection> sels;
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
||||||
|
@ -900,7 +899,8 @@ void deindent(Context& context, int)
|
||||||
if (not sels.empty())
|
if (not sels.empty())
|
||||||
{
|
{
|
||||||
ScopedEdition edition(context);
|
ScopedEdition edition(context);
|
||||||
erase(context.buffer(), sels);
|
SelectionList selections{context.buffer(), std::move(sels)};
|
||||||
|
erase(context.buffer(), selections);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1238,16 +1238,16 @@ void spaces_to_tabs(Context& context, int ts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static SelectionList compute_modified_ranges(const Buffer& buffer, size_t timestamp)
|
static boost::optional<SelectionList> compute_modified_ranges(const Buffer& buffer, size_t timestamp)
|
||||||
{
|
{
|
||||||
SelectionList ranges(buffer);
|
std::vector<Selection> ranges;
|
||||||
for (auto& change : buffer.changes_since(timestamp))
|
for (auto& change : buffer.changes_since(timestamp))
|
||||||
{
|
{
|
||||||
const ByteCoord& begin = change.begin;
|
const ByteCoord& begin = change.begin;
|
||||||
const ByteCoord& end = change.end;
|
const ByteCoord& end = change.end;
|
||||||
if (change.type == Buffer::Change::Insert)
|
if (change.type == Buffer::Change::Insert)
|
||||||
{
|
{
|
||||||
ranges.update_insert(begin, end, change.at_end);
|
update_insert(ranges, begin, end, change.at_end);
|
||||||
auto it = std::upper_bound(ranges.begin(), ranges.end(), begin,
|
auto it = std::upper_bound(ranges.begin(), ranges.end(), begin,
|
||||||
[](ByteCoord c, const Selection& sel)
|
[](ByteCoord c, const Selection& sel)
|
||||||
{ return c < sel.min(); });
|
{ return c < sel.min(); });
|
||||||
|
@ -1255,7 +1255,7 @@ static SelectionList compute_modified_ranges(const Buffer& buffer, size_t timest
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ranges.update_erase(begin, end, change.at_end);
|
update_erase(ranges, begin, end, change.at_end);
|
||||||
auto pos = begin;
|
auto pos = begin;
|
||||||
if (change.at_end)
|
if (change.at_end)
|
||||||
pos = begin.column ? ByteCoord{begin.line, begin.column - 1}
|
pos = begin.column ? ByteCoord{begin.line, begin.column - 1}
|
||||||
|
@ -1267,23 +1267,22 @@ static SelectionList compute_modified_ranges(const Buffer& buffer, size_t timest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ranges.empty())
|
if (ranges.empty())
|
||||||
return ranges;
|
return {};
|
||||||
|
|
||||||
ranges.set_timestamp(buffer.timestamp());
|
SelectionList result{buffer, std::move(ranges)};
|
||||||
ranges.set_main_index(ranges.size() - 1);
|
|
||||||
|
|
||||||
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
||||||
return lhs.min() <= rhs.min() ? buffer.char_next(lhs.max()) >= rhs.min()
|
return lhs.min() <= rhs.min() ? buffer.char_next(lhs.max()) >= rhs.min()
|
||||||
: lhs.min() <= buffer.char_next(rhs.max());
|
: lhs.min() <= buffer.char_next(rhs.max());
|
||||||
};
|
};
|
||||||
ranges.merge_overlapping(touches);
|
result.merge_overlapping(touches);
|
||||||
|
|
||||||
for (auto& sel : ranges)
|
for (auto& sel : result)
|
||||||
{
|
{
|
||||||
if (sel.anchor() != sel.cursor())
|
if (sel.anchor() != sel.cursor())
|
||||||
sel.cursor() = buffer.char_prev(sel.cursor());
|
sel.cursor() = buffer.char_prev(sel.cursor());
|
||||||
}
|
}
|
||||||
return ranges;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void undo(Context& context, int)
|
void undo(Context& context, int)
|
||||||
|
@ -1294,8 +1293,8 @@ void undo(Context& context, int)
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
auto ranges = compute_modified_ranges(buffer, timestamp);
|
auto ranges = compute_modified_ranges(buffer, timestamp);
|
||||||
if (not ranges.empty())
|
if (ranges)
|
||||||
context.selections() = std::move(ranges);
|
context.selections() = std::move(*ranges);
|
||||||
}
|
}
|
||||||
else if (not res)
|
else if (not res)
|
||||||
context.print_status({ "nothing left to undo", get_color("Information") });
|
context.print_status({ "nothing left to undo", get_color("Information") });
|
||||||
|
@ -1310,8 +1309,8 @@ void redo(Context& context, int)
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
auto ranges = compute_modified_ranges(buffer, timestamp);
|
auto ranges = compute_modified_ranges(buffer, timestamp);
|
||||||
if (not ranges.empty())
|
if (ranges)
|
||||||
context.selections() = std::move(ranges);
|
context.selections() = std::move(*ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (not res)
|
else if (not res)
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
template<template <bool, bool> class UpdateFunc>
|
template<template <bool, bool> class UpdateFunc>
|
||||||
void on_buffer_change(SelectionList& sels,
|
void on_buffer_change(std::vector<Selection>& sels,
|
||||||
ByteCoord begin, ByteCoord end, bool at_end, LineCount end_line)
|
ByteCoord begin, ByteCoord end, bool at_end, LineCount end_line)
|
||||||
{
|
{
|
||||||
auto update_beg = std::lower_bound(sels.begin(), sels.end(), begin,
|
auto update_beg = std::lower_bound(sels.begin(), sels.end(), begin,
|
||||||
|
@ -101,14 +101,11 @@ struct UpdateErase
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionList::SelectionList(const Buffer& buffer)
|
|
||||||
: m_buffer(&buffer), m_timestamp(buffer.timestamp())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectionList::SelectionList(const Buffer& buffer, Selection s, size_t timestamp)
|
SelectionList::SelectionList(const Buffer& buffer, Selection s, size_t timestamp)
|
||||||
: m_buffer(&buffer), m_selections({ s }), m_timestamp(timestamp)
|
: m_buffer(&buffer), m_selections({ s }), m_timestamp(timestamp)
|
||||||
{}
|
{
|
||||||
|
check_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
SelectionList::SelectionList(const Buffer& buffer, Selection s)
|
SelectionList::SelectionList(const Buffer& buffer, Selection s)
|
||||||
: SelectionList(buffer, s, buffer.timestamp())
|
: SelectionList(buffer, s, buffer.timestamp())
|
||||||
|
@ -116,20 +113,23 @@ SelectionList::SelectionList(const Buffer& buffer, Selection s)
|
||||||
|
|
||||||
SelectionList::SelectionList(const Buffer& buffer, std::vector<Selection> s, size_t timestamp)
|
SelectionList::SelectionList(const Buffer& buffer, std::vector<Selection> s, size_t timestamp)
|
||||||
: m_buffer(&buffer), m_selections(std::move(s)), m_timestamp(timestamp)
|
: m_buffer(&buffer), m_selections(std::move(s)), m_timestamp(timestamp)
|
||||||
{}
|
{
|
||||||
|
kak_assert(size() > 0);
|
||||||
|
check_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
SelectionList::SelectionList(const Buffer& buffer, std::vector<Selection> s)
|
SelectionList::SelectionList(const Buffer& buffer, std::vector<Selection> s)
|
||||||
: SelectionList(buffer, std::move(s), buffer.timestamp())
|
: SelectionList(buffer, std::move(s), buffer.timestamp())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void SelectionList::update_insert(ByteCoord begin, ByteCoord end, bool at_end)
|
void update_insert(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end, bool at_end)
|
||||||
{
|
{
|
||||||
on_buffer_change<UpdateInsert>(*this, begin, end, at_end, begin.line);
|
on_buffer_change<UpdateInsert>(sels, begin, end, at_end, begin.line);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectionList::update_erase(ByteCoord begin, ByteCoord end, bool at_end)
|
void update_erase(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end, bool at_end)
|
||||||
{
|
{
|
||||||
on_buffer_change<UpdateErase>(*this, begin, end, at_end, end.line);
|
on_buffer_change<UpdateErase>(sels, begin, end, at_end, end.line);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectionList::update()
|
void SelectionList::update()
|
||||||
|
@ -137,9 +137,9 @@ void SelectionList::update()
|
||||||
for (auto& change : m_buffer->changes_since(m_timestamp))
|
for (auto& change : m_buffer->changes_since(m_timestamp))
|
||||||
{
|
{
|
||||||
if (change.type == Buffer::Change::Insert)
|
if (change.type == Buffer::Change::Insert)
|
||||||
update_insert(change.begin, change.end, change.at_end);
|
update_insert(m_selections, change.begin, change.end, change.at_end);
|
||||||
else
|
else
|
||||||
update_erase(change.begin, change.end, change.at_end);
|
update_erase(m_selections, change.begin, change.end, change.at_end);
|
||||||
}
|
}
|
||||||
m_timestamp = m_buffer->timestamp();
|
m_timestamp = m_buffer->timestamp();
|
||||||
|
|
||||||
|
|
|
@ -57,15 +57,11 @@ static bool compare_selections(const Selection& lhs, const Selection& rhs)
|
||||||
|
|
||||||
struct SelectionList
|
struct SelectionList
|
||||||
{
|
{
|
||||||
SelectionList(const Buffer& buffer);
|
|
||||||
SelectionList(const Buffer& buffer, Selection s);
|
SelectionList(const Buffer& buffer, Selection s);
|
||||||
SelectionList(const Buffer& buffer, Selection s, size_t timestamp);
|
SelectionList(const Buffer& buffer, Selection s, size_t timestamp);
|
||||||
SelectionList(const Buffer& buffer, std::vector<Selection> s);
|
SelectionList(const Buffer& buffer, std::vector<Selection> s);
|
||||||
SelectionList(const Buffer& buffer, std::vector<Selection> s, size_t timestamp);
|
SelectionList(const Buffer& buffer, std::vector<Selection> s, size_t timestamp);
|
||||||
|
|
||||||
void update_insert(ByteCoord begin, ByteCoord end, bool at_end);
|
|
||||||
void update_erase(ByteCoord begin, ByteCoord end, bool at_end);
|
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
void check_invariant() const;
|
void check_invariant() const;
|
||||||
|
@ -83,6 +79,15 @@ struct SelectionList
|
||||||
Selection& operator[](size_t i) { return m_selections[i]; }
|
Selection& operator[](size_t i) { return m_selections[i]; }
|
||||||
const Selection& operator[](size_t i) const { return m_selections[i]; }
|
const Selection& operator[](size_t i) const { return m_selections[i]; }
|
||||||
|
|
||||||
|
SelectionList& operator=(std::vector<Selection> list)
|
||||||
|
{
|
||||||
|
m_selections = std::move(list);
|
||||||
|
m_main = size()-1;
|
||||||
|
sort_and_merge_overlapping();
|
||||||
|
check_invariant();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
using iterator = std::vector<Selection>::iterator;
|
using iterator = std::vector<Selection>::iterator;
|
||||||
iterator begin() { return m_selections.begin(); }
|
iterator begin() { return m_selections.begin(); }
|
||||||
iterator end() { return m_selections.end(); }
|
iterator end() { return m_selections.end(); }
|
||||||
|
@ -148,6 +153,9 @@ private:
|
||||||
size_t m_timestamp;
|
size_t m_timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void update_insert(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end, bool at_end);
|
||||||
|
void update_erase(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end, bool at_end);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // selection_hh_INCLUDED
|
#endif // selection_hh_INCLUDED
|
||||||
|
|
|
@ -437,7 +437,7 @@ void select_whole_buffer(const Buffer& buffer, SelectionList& selections)
|
||||||
void select_all_matches(const Buffer& buffer, SelectionList& selections,
|
void select_all_matches(const Buffer& buffer, SelectionList& selections,
|
||||||
const Regex& regex)
|
const Regex& regex)
|
||||||
{
|
{
|
||||||
SelectionList result(buffer);
|
std::vector<Selection> result;
|
||||||
for (auto& sel : selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
|
auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
|
||||||
|
@ -463,14 +463,13 @@ void select_all_matches(const Buffer& buffer, SelectionList& selections,
|
||||||
}
|
}
|
||||||
if (result.empty())
|
if (result.empty())
|
||||||
throw runtime_error("nothing selected");
|
throw runtime_error("nothing selected");
|
||||||
result.set_main_index(result.size() - 1);
|
|
||||||
selections = std::move(result);
|
selections = std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void split_selections(const Buffer& buffer, SelectionList& selections,
|
void split_selections(const Buffer& buffer, SelectionList& selections,
|
||||||
const Regex& regex)
|
const Regex& regex)
|
||||||
{
|
{
|
||||||
SelectionList result(buffer);
|
std::vector<Selection> result;
|
||||||
for (auto& sel : selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
auto begin = buffer.iterator_at(sel.min());
|
auto begin = buffer.iterator_at(sel.min());
|
||||||
|
@ -489,7 +488,6 @@ void split_selections(const Buffer& buffer, SelectionList& selections,
|
||||||
if (begin.coord() <= sel.max())
|
if (begin.coord() <= sel.max())
|
||||||
result.push_back({ begin.coord(), sel.max() });
|
result.push_back({ begin.coord(), sel.max() });
|
||||||
}
|
}
|
||||||
result.set_main_index(result.size() - 1);
|
|
||||||
selections = std::move(result);
|
selections = std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user