From 43e8aadcaad0036b6910d1a730e4f6c4c1ebd72c Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 26 Dec 2023 20:55:47 +1100 Subject: Fix performance of diff-based reloading of buffers It turns out diffing was pretty fast, but applying the diff was sub-optimal as it was constantly inserting/erasing lines which led to lots of unnecessary shifting. Fix this by manually tracking a read/write iterator and only shifting when necessary (on keeps, and inserts). --- src/buffer.cc | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'src/buffer.cc') diff --git a/src/buffer.cc b/src/buffer.cc index 4460bab7..b9335322 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -219,40 +219,55 @@ void Buffer::reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, F [](const StringDataPtr& lhs, const StringDataPtr& rhs) { return lhs->strview() == rhs->strview(); }); - auto it = m_lines.begin(); + auto read_it = m_lines.begin(); + auto write_it = m_lines.begin(); auto new_it = lines.begin(); - for (auto& [op, len] : diff) + for (auto [op, len] : diff) { + kak_assert(read_it >= write_it); if (op == DiffOp::Keep) { - it += len; + if (read_it != write_it) + std::move(read_it, read_it + len, write_it); + write_it += len; + read_it += len; new_it += len; } else if (op == DiffOp::Add) { - const LineCount cur_line = (int)(it - m_lines.begin()); - + const LineCount cur_line = (int)(write_it - m_lines.begin()); for (LineCount line = 0; line < len; ++line) m_current_undo_group.push_back({Modification::Insert, cur_line + line, *(new_it + (int)line)}); - m_changes.push_back({Change::Insert, cur_line, cur_line + len}); - m_lines.insert(it, new_it, new_it + len); - it = m_lines.begin() + (int)(cur_line + len); + + if (read_it != write_it) + { + auto count = std::min(len, static_cast(read_it - write_it)); + write_it = std::copy(new_it, new_it + count, write_it); + new_it += count; + if (len == count) + continue; + len -= count; + } + + auto read_pos = read_it - m_lines.begin(); + write_it = m_lines.insert(write_it, new_it, new_it + len) + len; + read_it = m_lines.begin() + read_pos + len; new_it += len; } else if (op == DiffOp::Remove) { - const LineCount cur_line = (int)(it - m_lines.begin()); - + const LineCount cur_line = (int)(write_it - m_lines.begin()); for (LineCount line = len-1; line >= 0; --line) m_current_undo_group.push_back({ Modification::Erase, cur_line + line, m_lines.get_storage(cur_line + line)}); - it = m_lines.erase(it, it + len); + read_it += len; m_changes.push_back({ Change::Erase, cur_line, cur_line + len }); } } + m_lines.erase(write_it, m_lines.end()); } commit_undo_group(); -- cgit v1.2.3 From 5ea5c99c58ea9e71e9239366a6faf78aa20ecb0d Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 21 Jan 2024 10:24:08 +1100 Subject: Fix using invalid strings for undo content strings The code was wrongly using the write_it instead of the read_it to access the removed lines content. Fixes #5088 --- src/buffer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/buffer.cc') diff --git a/src/buffer.cc b/src/buffer.cc index b9335322..7054e6b8 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -261,7 +261,7 @@ void Buffer::reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, F for (LineCount line = len-1; line >= 0; --line) m_current_undo_group.push_back({ Modification::Erase, cur_line + line, - m_lines.get_storage(cur_line + line)}); + *(read_it + (size_t)line)}); read_it += len; m_changes.push_back({ Change::Erase, cur_line, cur_line + len }); -- cgit v1.2.3 From 3d5a0c672e6f3cf87944b33712e17531aa42c607 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Wed, 28 Feb 2024 19:00:51 +1100 Subject: Templatize StringData::create This improves performance by letting the compiler optimize most use cases where string count and length are known are compile time. --- src/buffer.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/buffer.cc') diff --git a/src/buffer.cc b/src/buffer.cc index 7054e6b8..9c38605a 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -430,14 +430,14 @@ BufferRange Buffer::do_insert(BufferCoord pos, StringView content) if (content[i] == '\n') { StringView line = content.substr(start, i + 1 - start); - new_lines.push_back(start == 0 ? StringData::create({prefix, line}) : StringData::create({line})); + new_lines.push_back(start == 0 ? StringData::create(prefix, line) : StringData::create(line)); start = i + 1; } } if (start == 0) - new_lines.push_back(StringData::create({prefix, content, suffix})); + new_lines.push_back(StringData::create(prefix, content, suffix)); else if (start != content.length() or not suffix.empty()) - new_lines.push_back(StringData::create({content.substr(start), suffix})); + new_lines.push_back(StringData::create(content.substr(start), suffix)); auto line_it = m_lines.begin() + (int)pos.line; auto new_lines_it = new_lines.begin(); @@ -466,7 +466,7 @@ BufferCoord Buffer::do_erase(BufferCoord begin, BufferCoord end) StringView prefix = m_lines[begin.line].substr(0, begin.column); StringView suffix = end.line == line_count() ? StringView{} : m_lines[end.line].substr(end.column); - auto new_line = (not prefix.empty() or not suffix.empty()) ? StringData::create({prefix, suffix}) : StringDataPtr{}; + auto new_line = (not prefix.empty() or not suffix.empty()) ? StringData::create(prefix, suffix) : StringDataPtr{}; m_lines.erase(m_lines.begin() + (int)begin.line, m_lines.begin() + (int)end.line); m_changes.push_back({ Change::Erase, begin, end }); @@ -691,7 +691,7 @@ String Buffer::debug_description() const UnitTest test_buffer{[]() { - auto make_lines = [](auto&&... lines) { return BufferLines{StringData::create({lines})...}; }; + auto make_lines = [](auto&&... lines) { return BufferLines{StringData::create(lines)...}; }; Buffer empty_buffer("empty", Buffer::Flags::None, make_lines("\n")); @@ -738,7 +738,7 @@ UnitTest test_buffer{[]() UnitTest test_undo{[]() { - auto make_lines = [](auto&&... lines) { return BufferLines{StringData::create({lines})...}; }; + auto make_lines = [](auto&&... lines) { return BufferLines{StringData::create(lines)...}; }; Buffer buffer("test", Buffer::Flags::None, make_lines("allo ?\n", "mais que fais la police\n", " hein ?\n", " youpi\n")); auto pos = buffer.end_coord(); -- cgit v1.2.3 From dbda8d6dc838c57625d94b3ec7060ce4c9084a93 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 4 Mar 2024 09:01:19 +1100 Subject: Ensure ReadOnly buffer flag get reflected to readonly option Fixes #5110 --- src/buffer.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src/buffer.cc') diff --git a/src/buffer.cc b/src/buffer.cc index 9c38605a..ffb45e18 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -47,6 +47,7 @@ Buffer::Buffer(String name, Flags flags, BufferLines lines, options().get_local_option("eolformat").set(eolformat); options().get_local_option("BOM").set(bom); + options().get_local_option("readonly").set((bool)(flags & Flags::ReadOnly)); // now we may begin to record undo data if (not (flags & Flags::NoUndo)) -- cgit v1.2.3 From 699c70ac91304078cdf4a8fee6d1737553f96606 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sat, 23 Mar 2024 20:02:16 +1100 Subject: Add some missing ScopedEdition and fix redo tabs <-> space conversion functions did not create a ScopedEdition leading to uncommited modifications. Fixing this did fix the interactive error, but that error still existed in non interactive context so redo now considers there there is no redo child if there are uncommited modifiations (which is correct as this means we are currently creating a new leaf in the undo tree) Fixes #5124 --- src/buffer.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/buffer.cc') diff --git a/src/buffer.cc b/src/buffer.cc index ffb45e18..ada61ad8 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -321,11 +321,10 @@ bool Buffer::redo(size_t count) { throw_if_read_only(); - if (current_history_node().redo_child == HistoryId::Invalid) + if (current_history_node().redo_child == HistoryId::Invalid or + not m_current_undo_group.empty()) return false; - kak_assert(m_current_undo_group.empty()); - while (count-- != 0 and current_history_node().redo_child != HistoryId::Invalid) { m_history_id = current_history_node().redo_child; -- cgit v1.2.3