Use a Buffer::changes_since based implementation for undo/redo

This commit is contained in:
Maxime Coste 2014-05-12 19:10:25 +01:00
parent 67a251ffd5
commit 8ab1f58594

View File

@ -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") });
} }