From a51027602544bb7d67a3313cca859bac897894b6 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 11 May 2014 12:20:59 +0100 Subject: [PATCH 01/53] Maintain an append-only list of changes in Buffer --- src/buffer.cc | 34 +++++++++++++++++----------------- src/buffer.hh | 12 +++++++++++- src/buffer.inl.hh | 8 +++++++- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/buffer.cc b/src/buffer.cc index dbdd6247..5219f1b6 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -19,8 +19,6 @@ Buffer::Buffer(String name, Flags flags, std::vector lines, m_flags(flags | Flags::NoUndo), m_history(), m_history_cursor(m_history.begin()), m_last_save_undo_index(0), - // start buffer timestamp at 1 so that caches can init to 0 - m_timestamp(1), m_fs_timestamp(fs_timestamp), m_hooks(GlobalHooks::instance()), m_options(GlobalOptions::instance()), @@ -37,10 +35,12 @@ Buffer::Buffer(String name, Flags flags, std::vector lines, for (auto& line : lines) { kak_assert(not line.empty() and line.back() == '\n'); - m_lines.emplace_back(Line{ m_timestamp, pos, std::move(line) }); + m_lines.emplace_back(Line{ m_changes.size()+1, pos, std::move(line) }); pos += m_lines.back().length(); } + m_changes.push_back({ Change::Insert, {0,0}, line_count() }); + if (flags & Flags::File) { if (flags & Flags::New) @@ -78,12 +78,13 @@ void Buffer::reload(std::vector lines, time_t fs_timestamp) for (auto listener : m_change_listeners) listener->on_erase(*this, {0,0}, back_coord()); + m_changes.push_back({ Change::Erase, {0,0}, back_coord() }); + m_history.clear(); m_current_undo_group.clear(); m_history_cursor = m_history.begin(); m_last_save_undo_index = 0; m_lines.clear(); - ++m_timestamp; if (lines.empty()) lines.emplace_back("\n"); @@ -93,11 +94,13 @@ void Buffer::reload(std::vector lines, time_t fs_timestamp) for (auto& line : lines) { kak_assert(not line.empty() and line.back() == '\n'); - m_lines.emplace_back(Line{ m_timestamp, pos, std::move(line) }); + m_lines.emplace_back(Line{ m_changes.size()+1, pos, std::move(line) }); pos += m_lines.back().length(); } m_fs_timestamp = fs_timestamp; + m_changes.push_back({ Change::Insert, {0,0}, back_coord() }); + for (auto listener : m_change_listeners) listener->on_insert(*this, {0,0}, back_coord()); } @@ -448,7 +451,6 @@ ByteCoord Buffer::do_insert(ByteCoord pos, const String& content) if (content.empty()) return pos; - ++m_timestamp; ByteCount offset = this->offset(pos); // all following lines advanced by length @@ -466,12 +468,12 @@ ByteCoord Buffer::do_insert(ByteCoord pos, const String& content) { if (content[i] == '\n') { - m_lines.push_back({ m_timestamp, offset + start, content.substr(start, i + 1 - start) }); + m_lines.push_back({ m_changes.size()+1, offset + start, content.substr(start, i + 1 - start) }); start = i + 1; } } if (start != content.length()) - m_lines.push_back({ m_timestamp, offset + start, content.substr(start) }); + m_lines.push_back({ m_changes.size()+1, offset + start, content.substr(start) }); begin = pos.column == 0 ? pos : ByteCoord{ pos.line + 1, 0 }; end = ByteCoord{ line_count()-1, m_lines.back().length() }; @@ -492,18 +494,18 @@ ByteCoord Buffer::do_insert(ByteCoord pos, const String& content) if (start == 0) { line_content = prefix + line_content; - new_lines.push_back({ m_timestamp, offset + start - prefix.length(), + new_lines.push_back({ m_changes.size()+1, offset + start - prefix.length(), std::move(line_content) }); } else - new_lines.push_back({ m_timestamp, offset + start, std::move(line_content) }); + new_lines.push_back({ m_changes.size()+1, offset + start, std::move(line_content) }); start = i + 1; } } if (start == 0) - new_lines.push_back({ m_timestamp, offset + start - prefix.length(), prefix + content + suffix }); + new_lines.push_back({ m_changes.size()+1, offset + start - prefix.length(), prefix + content + suffix }); else if (start != content.length() or not suffix.empty()) - new_lines.push_back({ m_timestamp, offset + start, content.substr(start) + suffix }); + new_lines.push_back({ m_changes.size()+1, offset + start, content.substr(start) + suffix }); LineCount last_line = pos.line + new_lines.size() - 1; @@ -517,6 +519,7 @@ ByteCoord Buffer::do_insert(ByteCoord pos, const String& content) end = ByteCoord{ last_line, m_lines[last_line].length() - suffix.length() }; } + m_changes.push_back({ Change::Insert, begin, end }); for (auto listener : m_change_listeners) listener->on_insert(*this, begin, end); return begin; @@ -526,11 +529,10 @@ ByteCoord Buffer::do_erase(ByteCoord begin, ByteCoord end) { kak_assert(is_valid(begin)); kak_assert(is_valid(end)); - ++m_timestamp; const ByteCount length = distance(begin, end); String prefix = m_lines[begin.line].content.substr(0, begin.column); String suffix = m_lines[end.line].content.substr(end.column); - Line new_line = { m_timestamp, m_lines[begin.line].start, prefix + suffix }; + Line new_line = { m_changes.size()+1, m_lines[begin.line].start, prefix + suffix }; ByteCoord next; if (new_line.length() != 0) @@ -548,6 +550,7 @@ ByteCoord Buffer::do_erase(ByteCoord begin, ByteCoord end) for (LineCount i = begin.line+1; i < line_count(); ++i) m_lines[i].start -= length; + m_changes.push_back({ Change::Erase, begin, end }); for (auto listener : m_change_listeners) listener->on_erase(*this, begin, end); return next; @@ -628,10 +631,7 @@ void Buffer::notify_saved() m_flags &= ~Flags::New; size_t history_cursor_index = m_history_cursor - m_history.begin(); if (m_last_save_undo_index != history_cursor_index) - { - ++m_timestamp; m_last_save_undo_index = history_cursor_index; - } m_fs_timestamp = get_fs_timestamp(m_name); } diff --git a/src/buffer.hh b/src/buffer.hh index ff09528e..46b982a8 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -175,6 +175,15 @@ public: void reload(std::vector lines, time_t fs_timestamp = InvalidTime); void check_invariant() const; + + struct Change + { + enum Type { Insert, Erase }; + Type type; + ByteCoord begin; + ByteCoord end; + }; + memoryview changes_since(size_t timestamp) const; private: void on_option_changed(const Option& option) override; @@ -215,7 +224,8 @@ private: void revert_modification(const Modification& modification); size_t m_last_save_undo_index; - size_t m_timestamp; + + std::vector m_changes; time_t m_fs_timestamp; diff --git a/src/buffer.inl.hh b/src/buffer.inl.hh index 1920857d..9cfa2d0f 100644 --- a/src/buffer.inl.hh +++ b/src/buffer.inl.hh @@ -88,7 +88,13 @@ inline LineCount Buffer::line_count() const inline size_t Buffer::timestamp() const { - return m_timestamp; + return m_changes.size(); +} + +inline memoryview Buffer::changes_since(size_t timestamp) const +{ + return { m_changes.data() + timestamp, + m_changes.data() + m_changes.size() }; } inline size_t Buffer::line_timestamp(LineCount line) const From ddd8f8d392ae059298ffa6175165f6e224a009f9 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 11 May 2014 13:20:13 +0100 Subject: [PATCH 02/53] LineChangeWatcher uses Buffer::changes_since rather than listening --- src/buffer.cc | 12 ++++--- src/buffer.hh | 1 + src/line_change_watcher.cc | 69 ++++++++++++++++++++++++++------------ src/line_change_watcher.hh | 23 ++++++------- src/word_db.cc | 2 +- 5 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/buffer.cc b/src/buffer.cc index 5219f1b6..c92f09b1 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -39,7 +39,7 @@ Buffer::Buffer(String name, Flags flags, std::vector lines, pos += m_lines.back().length(); } - m_changes.push_back({ Change::Insert, {0,0}, line_count() }); + m_changes.push_back({ Change::Insert, {0,0}, line_count(), true }); if (flags & Flags::File) { @@ -78,7 +78,7 @@ void Buffer::reload(std::vector lines, time_t fs_timestamp) for (auto listener : m_change_listeners) listener->on_erase(*this, {0,0}, back_coord()); - m_changes.push_back({ Change::Erase, {0,0}, back_coord() }); + m_changes.push_back({ Change::Erase, {0,0}, back_coord(), true }); m_history.clear(); m_current_undo_group.clear(); @@ -99,7 +99,7 @@ void Buffer::reload(std::vector lines, time_t fs_timestamp) } m_fs_timestamp = fs_timestamp; - m_changes.push_back({ Change::Insert, {0,0}, back_coord() }); + m_changes.push_back({ Change::Insert, {0,0}, back_coord(), true }); for (auto listener : m_change_listeners) listener->on_insert(*this, {0,0}, back_coord()); @@ -459,6 +459,7 @@ ByteCoord Buffer::do_insert(ByteCoord pos, const String& content) ByteCoord begin; ByteCoord end; + bool at_end = false; // if we inserted at the end of the buffer, we have created a new // line without inserting a '\n' if (is_end(pos)) @@ -477,6 +478,7 @@ ByteCoord Buffer::do_insert(ByteCoord pos, const String& content) begin = pos.column == 0 ? pos : ByteCoord{ pos.line + 1, 0 }; end = ByteCoord{ line_count()-1, m_lines.back().length() }; + at_end = true; } else { @@ -519,7 +521,7 @@ ByteCoord Buffer::do_insert(ByteCoord pos, const String& content) end = ByteCoord{ last_line, m_lines[last_line].length() - suffix.length() }; } - m_changes.push_back({ Change::Insert, begin, end }); + m_changes.push_back({ Change::Insert, begin, end, at_end }); for (auto listener : m_change_listeners) listener->on_insert(*this, begin, end); return begin; @@ -550,7 +552,7 @@ ByteCoord Buffer::do_erase(ByteCoord begin, ByteCoord end) for (LineCount i = begin.line+1; i < line_count(); ++i) m_lines[i].start -= length; - m_changes.push_back({ Change::Erase, begin, end }); + m_changes.push_back({ Change::Erase, begin, end, is_end(begin) }); for (auto listener : m_change_listeners) listener->on_erase(*this, begin, end); return next; diff --git a/src/buffer.hh b/src/buffer.hh index 46b982a8..89684ce5 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -182,6 +182,7 @@ public: Type type; ByteCoord begin; ByteCoord end; + bool at_end; }; memoryview changes_since(size_t timestamp) const; private: diff --git a/src/line_change_watcher.cc b/src/line_change_watcher.cc index 2e7ba879..c13a43b2 100644 --- a/src/line_change_watcher.cc +++ b/src/line_change_watcher.cc @@ -1,12 +1,57 @@ #include "line_change_watcher.hh" +#include "buffer.hh" + namespace Kakoune { +namespace +{ + +struct Change +{ + LineCount pos; + LineCount num; +}; + +std::vector compute_changes(const Buffer& buffer, size_t timestamp) +{ + std::vector res; + for (auto& change : buffer.changes_since(timestamp)) + { + ByteCoord begin = change.begin; + ByteCoord end = change.end; + if (change.type == Buffer::Change::Insert) + { + if (change.at_end and begin != ByteCoord{0,0}) + { + kak_assert(begin.column == 0); + --begin.line; + } + res.push_back({begin.line, end.line - begin.line}); + } + else + { + if (change.at_end and begin != ByteCoord{0,0}) + { + kak_assert(begin.column == 0); + --begin.line; + } + res.push_back({begin.line, begin.line - end.line}); + } + } + return res; +} + +} + +LineChangeWatcher::LineChangeWatcher(const Buffer& buffer) + : m_buffer(&buffer), m_timestamp(buffer.timestamp()) {} + std::vector LineChangeWatcher::compute_modifications() { std::vector res; - for (auto& change : m_changes) + for (auto& change : compute_changes(*m_buffer, m_timestamp)) { auto pos = std::upper_bound(res.begin(), res.end(), change.pos, [](const LineCount& l, const LineModification& c) @@ -58,28 +103,8 @@ std::vector LineChangeWatcher::compute_modifications() it->new_line -= num_removed; } } - m_changes.clear(); + m_timestamp = m_buffer->timestamp(); return res; } -void LineChangeWatcher::on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end) -{ - if (buffer.is_end(end)) - { - kak_assert(begin.column == 0); - --begin.line; - } - m_changes.push_back({begin.line, end.line - begin.line}); -} - -void LineChangeWatcher::on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end) -{ - if (begin.line == buffer.line_count()) - { - kak_assert(begin.column == 0); - --begin.line; - } - m_changes.push_back({begin.line, begin.line - end.line}); -} - } diff --git a/src/line_change_watcher.hh b/src/line_change_watcher.hh index ce7bedfa..d35167ca 100644 --- a/src/line_change_watcher.hh +++ b/src/line_change_watcher.hh @@ -1,11 +1,14 @@ #ifndef line_change_watcher_hh_INCLUDED #define line_change_watcher_hh_INCLUDED -#include "buffer.hh" +#include "units.hh" +#include "utils.hh" namespace Kakoune { +class Buffer; + struct LineModification { LineCount old_line; // line position in the old buffer @@ -16,23 +19,17 @@ struct LineModification LineCount diff() const { return new_line - old_line + num_added - num_removed; } }; -class LineChangeWatcher : public BufferChangeListener_AutoRegister +class LineChangeWatcher { public: - LineChangeWatcher (const Buffer& buffer) - : BufferChangeListener_AutoRegister(const_cast(buffer)) {} + LineChangeWatcher (const Buffer& buffer); std::vector compute_modifications(); -private: - void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end) override; - void on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end) override; - struct Change - { - LineCount pos; - LineCount num; - }; - std::vector m_changes; + const Buffer& buffer() const { return *m_buffer; } +private: + safe_ptr m_buffer; + size_t m_timestamp; }; } diff --git a/src/word_db.cc b/src/word_db.cc index 62e19290..73f6e1ad 100644 --- a/src/word_db.cc +++ b/src/word_db.cc @@ -65,7 +65,7 @@ void WordDB::update_db() if (modifs.empty()) return; - auto& buffer = m_change_watcher.registry(); + auto& buffer = m_change_watcher.buffer(); LineToWords new_lines; new_lines.reserve((int)buffer.line_count()); From 67a251ffd560d79e8337fef0aee8d8285d34355e Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 12 May 2014 13:59:21 +0100 Subject: [PATCH 03/53] Pass a at_end param to BufferChangeListener::on_{insert,erase} --- src/buffer.cc | 8 +++---- src/buffer.hh | 4 ++-- src/dynamic_selection_list.cc | 8 +++---- src/dynamic_selection_list.hh | 4 ++-- src/normal.cc | 8 +++---- src/selection.cc | 40 ++++++++++++++++++++--------------- src/selection.hh | 4 ++-- 7 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/buffer.cc b/src/buffer.cc index c92f09b1..f4b6782f 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -76,7 +76,7 @@ void Buffer::reload(std::vector lines, time_t fs_timestamp) // use back coord to simulate the persistance of the last end of line // as buffers are expected to never be empty. for (auto listener : m_change_listeners) - listener->on_erase(*this, {0,0}, back_coord()); + listener->on_erase(*this, {0,0}, back_coord(), true); m_changes.push_back({ Change::Erase, {0,0}, back_coord(), true }); @@ -102,7 +102,7 @@ void Buffer::reload(std::vector lines, time_t fs_timestamp) m_changes.push_back({ Change::Insert, {0,0}, back_coord(), true }); for (auto listener : m_change_listeners) - listener->on_insert(*this, {0,0}, back_coord()); + listener->on_insert(*this, {0,0}, back_coord(), true); } String Buffer::display_name() const @@ -523,7 +523,7 @@ ByteCoord Buffer::do_insert(ByteCoord pos, const String& content) m_changes.push_back({ Change::Insert, begin, end, at_end }); for (auto listener : m_change_listeners) - listener->on_insert(*this, begin, end); + listener->on_insert(*this, begin, end, at_end); return begin; } @@ -554,7 +554,7 @@ ByteCoord Buffer::do_erase(ByteCoord begin, ByteCoord end) m_changes.push_back({ Change::Erase, begin, end, is_end(begin) }); for (auto listener : m_change_listeners) - listener->on_erase(*this, begin, end); + listener->on_erase(*this, begin, end, is_end(begin)); return next; } diff --git a/src/buffer.hh b/src/buffer.hh index 89684ce5..f817eef0 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -67,9 +67,9 @@ class BufferChangeListener { public: virtual void on_insert(const Buffer& buffer, - ByteCoord begin, ByteCoord end) = 0; + ByteCoord begin, ByteCoord end, bool at_end) = 0; virtual void on_erase(const Buffer& buffer, - ByteCoord begin, ByteCoord end) = 0; + ByteCoord begin, ByteCoord end, bool at_end) = 0; }; // A Buffer is a in-memory representation of a file diff --git a/src/dynamic_selection_list.cc b/src/dynamic_selection_list.cc index 569d7818..2792da30 100644 --- a/src/dynamic_selection_list.cc +++ b/src/dynamic_selection_list.cc @@ -36,14 +36,14 @@ void DynamicSelectionList::check_invariant() const #endif } -void DynamicSelectionList::on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end) +void DynamicSelectionList::on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) { - update_insert(buffer, begin, end); + update_insert(begin, end, at_end); } -void DynamicSelectionList::on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end) +void DynamicSelectionList::on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) { - update_erase(buffer, begin, end); + update_erase(begin, end, at_end); } } diff --git a/src/dynamic_selection_list.hh b/src/dynamic_selection_list.hh index 4bfbd39a..2f408907 100644 --- a/src/dynamic_selection_list.hh +++ b/src/dynamic_selection_list.hh @@ -19,8 +19,8 @@ public: void check_invariant() const; private: - void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end) override; - void on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end) override; + void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) override; + void on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) override; }; } diff --git a/src/normal.cc b/src/normal.cc index 062f512c..5016ece9 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -1239,18 +1239,18 @@ public: ModifiedRangesListener(Buffer& buffer) : BufferChangeListener_AutoRegister(buffer) {} - void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end) + void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) { - m_ranges.update_insert(buffer, begin, end); + m_ranges.update_insert(begin, end, at_end); auto it = std::upper_bound(m_ranges.begin(), m_ranges.end(), begin, [](ByteCoord c, const Selection& sel) { return c < sel.min(); }); m_ranges.insert(it, Selection{ begin, buffer.char_prev(end) }); } - void on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end) + void on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) { - m_ranges.update_erase(buffer, begin, end); + m_ranges.update_erase(begin, end, at_end); auto pos = std::min(begin, buffer.back_coord()); auto it = std::upper_bound(m_ranges.begin(), m_ranges.end(), pos, [](ByteCoord c, const Selection& sel) diff --git a/src/selection.cc b/src/selection.cc index e1627a0c..0049689d 100644 --- a/src/selection.cc +++ b/src/selection.cc @@ -18,8 +18,8 @@ namespace { template