Use a Buffer::changes_since based implementation for undo/redo
This commit is contained in:
parent
67a251ffd5
commit
8ab1f58594
|
@ -1233,52 +1233,63 @@ void spaces_to_tabs(Context& context, int ts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModifiedRangesListener : public BufferChangeListener_AutoRegister
|
static SelectionList compute_modified_ranges(const Buffer& buffer, size_t timestamp)
|
||||||
{
|
{
|
||||||
public:
|
SelectionList ranges;
|
||||||
ModifiedRangesListener(Buffer& buffer)
|
for (auto& change : buffer.changes_since(timestamp))
|
||||||
: BufferChangeListener_AutoRegister(buffer) {}
|
|
||||||
|
|
||||||
void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
|
||||||
{
|
{
|
||||||
m_ranges.update_insert(begin, end, at_end);
|
const ByteCoord& begin = change.begin;
|
||||||
auto it = std::upper_bound(m_ranges.begin(), m_ranges.end(), begin,
|
const ByteCoord& end = change.end;
|
||||||
|
if (change.type == Buffer::Change::Insert)
|
||||||
|
{
|
||||||
|
ranges.update_insert(begin, end, change.at_end);
|
||||||
|
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(); });
|
||||||
m_ranges.insert(it, Selection{ begin, buffer.char_prev(end) });
|
ranges.insert(it, Selection{begin, end});
|
||||||
}
|
}
|
||||||
|
else
|
||||||
void on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
|
||||||
{
|
{
|
||||||
m_ranges.update_erase(begin, end, at_end);
|
ranges.update_erase(begin, end, change.at_end);
|
||||||
auto pos = std::min(begin, buffer.back_coord());
|
auto pos = begin;
|
||||||
auto it = std::upper_bound(m_ranges.begin(), m_ranges.end(), pos,
|
if (change.at_end)
|
||||||
|
pos = begin.column ? ByteCoord{begin.line, begin.column - 1}
|
||||||
|
: ByteCoord{begin.line - 1};
|
||||||
|
auto it = std::upper_bound(ranges.begin(), ranges.end(), pos,
|
||||||
[](ByteCoord c, const Selection& sel)
|
[](ByteCoord c, const Selection& sel)
|
||||||
{ return c < sel.min(); });
|
{ return c < sel.min(); });
|
||||||
m_ranges.insert(it, Selection{ pos, pos });
|
ranges.insert(it, Selection{pos, pos});
|
||||||
}
|
}
|
||||||
SelectionList& ranges() { return m_ranges; }
|
}
|
||||||
|
if (ranges.empty())
|
||||||
|
return ranges;
|
||||||
|
|
||||||
private:
|
ranges.set_main_index(ranges.size() - 1);
|
||||||
SelectionList m_ranges;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool touches(const Buffer& buffer, 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);
|
||||||
|
|
||||||
|
for (auto& sel : ranges)
|
||||||
|
{
|
||||||
|
if (sel.anchor() != sel.cursor())
|
||||||
|
sel.cursor() = buffer.char_prev(sel.cursor());
|
||||||
|
}
|
||||||
|
return ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
void undo(Context& context, int)
|
void undo(Context& context, int)
|
||||||
{
|
{
|
||||||
ModifiedRangesListener listener(context.buffer());
|
Buffer& buffer = context.buffer();
|
||||||
bool res = context.buffer().undo();
|
size_t timestamp = buffer.timestamp();
|
||||||
if (res and not listener.ranges().empty())
|
bool res = buffer.undo();
|
||||||
|
if (res)
|
||||||
{
|
{
|
||||||
auto& selections = context.selections();
|
auto ranges = compute_modified_ranges(buffer, timestamp);
|
||||||
selections = std::move(listener.ranges());
|
if (not ranges.empty())
|
||||||
selections.set_main_index(selections.size() - 1);
|
context.selections() = std::move(ranges);
|
||||||
selections.merge_overlapping(std::bind(touches, std::ref(context.buffer()), _1, _2));
|
|
||||||
}
|
}
|
||||||
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") });
|
||||||
|
@ -1287,15 +1298,16 @@ void undo(Context& context, int)
|
||||||
void redo(Context& context, int)
|
void redo(Context& context, int)
|
||||||
{
|
{
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
ModifiedRangesListener listener(context.buffer());
|
Buffer& buffer = context.buffer();
|
||||||
bool res = context.buffer().redo();
|
size_t timestamp = buffer.timestamp();
|
||||||
if (res and not listener.ranges().empty())
|
bool res = buffer.redo();
|
||||||
|
if (res)
|
||||||
{
|
{
|
||||||
auto& selections = context.selections();
|
auto ranges = compute_modified_ranges(buffer, timestamp);
|
||||||
selections = std::move(listener.ranges());
|
if (not ranges.empty())
|
||||||
selections.set_main_index(selections.size() - 1);
|
context.selections() = std::move(ranges);
|
||||||
selections.merge_overlapping(std::bind(touches, std::ref(context.buffer()), _1, _2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (not res)
|
else if (not res)
|
||||||
context.print_status({ "nothing left to redo", get_color("Information") });
|
context.print_status({ "nothing left to redo", get_color("Information") });
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user