From 36e9e7eaf9e7694427ecd11f357ca1712f0d8632 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 22 Jan 2019 21:04:29 +1100 Subject: [PATCH] Change pipe diffing to work linewise This should greatly improve performances as we only need to diff lines instead of individual characters. Closes #2678 Fixes #2037 --- src/normal.cc | 28 ++++++++++++++++------------ src/ranges.cc | 3 +++ src/ranges.hh | 22 ++++++++++++++++++---- src/selection.hh | 1 + 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/normal.cc b/src/normal.cc index e8c5cdb7..9ffc4bd9 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -491,29 +491,33 @@ void command(Context& context, NormalParams params) BufferCoord apply_diff(Buffer& buffer, BufferCoord pos, StringView before, StringView after) { - // The diff algorithm is O(ND) with N the sum of string len, and D the diff count - // do not use it if our data is too big - constexpr ByteCount size_limit = 100 * 1024; - if (before.length() + after.length() > size_limit) - { - buffer.erase(pos, buffer.advance(pos, before.length())); - return buffer.insert(pos, after); - } + const auto lines_before = before | split_after('\n') | gather>(); + const auto lines_after = after | split_after('\n') | gather>(); - auto diffs = find_diff(before.begin(), (int)before.length(), after.begin(), (int)after.length()); + auto diffs = find_diff(lines_before.begin(), (int)lines_before.size(), + lines_after.begin(), (int)lines_after.size()); + auto byte_count = [](auto&& lines, int first, int count) { + return std::accumulate(&lines[first], &lines[first+count], 0_byte, + [](ByteCount l, StringView s) { return l + s.length(); }); + }; + + int posA = 0; for (auto& diff : diffs) { switch (diff.mode) { case Diff::Keep: - pos = buffer.advance(pos, diff.len); + pos = buffer.advance(pos, byte_count(lines_before, posA, diff.len)); + posA += diff.len; break; case Diff::Add: - pos = buffer.insert(pos, after.substr(ByteCount{diff.posB}, ByteCount{diff.len})); + pos = buffer.insert(pos, {lines_after[diff.posB].begin(), + lines_after[diff.posB + diff.len - 1].end()}); break; case Diff::Remove: - pos = buffer.erase(pos, buffer.advance(pos, diff.len)); + pos = buffer.erase(pos, buffer.advance(pos, byte_count(lines_before, posA, diff.len))); + posA += diff.len; break; } } diff --git a/src/ranges.cc b/src/ranges.cc index d1603c18..11885ad1 100644 --- a/src/ranges.cc +++ b/src/ranges.cc @@ -16,6 +16,9 @@ UnitTest test_ranges{[] { check_equal(","_sv | split(','), {"", ""}); check_equal(""_sv | split(','), {}); + check_equal("a,b,c,"_sv | split_after(','), {"a,", "b,", "c,"}); + check_equal("a,b,c"_sv | split_after(','), {"a,", "b,", "c"}); + check_equal(R"(a\,,\,b,\,)"_sv | split(',', '\\') | transform(unescape<',', '\\'>), {"a,", ",b", ","}); check_equal(R"(\,\,)"_sv | split(',', '\\') diff --git a/src/ranges.hh b/src/ranges.hh index cf8a0464..1089c94d 100644 --- a/src/ranges.hh +++ b/src/ranges.hh @@ -197,7 +197,7 @@ inline auto transform(M T::*m) return transform(std::mem_fn(std::forward(m))); } -template, typename ValueTypeParam = void> struct SplitView @@ -226,7 +226,7 @@ struct SplitView bool operator==(const Iterator& other) const { return pos == other.pos and done == other.done; } bool operator!=(const Iterator& other) const { return pos != other.pos or done != other.done; } - ValueType operator*() { return {pos, sep}; } + ValueType operator*() { return {pos, (not include_separator or sep == end) ? sep : sep + 1}; } private: void advance() @@ -239,6 +239,11 @@ struct SplitView } pos = sep+1; + if (include_separator and pos == end) + { + done = true; + return; + } bool escaped = escape and *sep == escaper; for (sep = pos; sep != end; ++sep) { @@ -269,7 +274,16 @@ auto split(Element separator) { return make_view_factory([s = std::move(separator)](auto&& range) { using Range = decltype(range); - return SplitView, false, Element, ValueType>{std::forward(range), std::move(s), {}}; + return SplitView, false, false, Element, ValueType>{std::forward(range), std::move(s), {}}; + }); +} + +template +auto split_after(Element separator) +{ + return make_view_factory([s = std::move(separator)](auto&& range) { + using Range = decltype(range); + return SplitView, false, true, Element, ValueType>{std::forward(range), std::move(s), {}}; }); } @@ -278,7 +292,7 @@ auto split(Element separator, Element escaper) { return make_view_factory([s = std::move(separator), e = std::move(escaper)](auto&& range) { using Range = decltype(range); - return SplitView, true, Element, ValueType>{std::forward(range), std::move(s), std::move(e)}; + return SplitView, true, false, Element, ValueType>{std::forward(range), std::move(s), std::move(e)}; }); } diff --git a/src/selection.hh b/src/selection.hh index 510f999b..493a01b4 100644 --- a/src/selection.hh +++ b/src/selection.hh @@ -140,6 +140,7 @@ struct SelectionList Buffer& buffer() const { return *m_buffer; } size_t timestamp() const { return m_timestamp; } + void force_timestamp(size_t timestamp) { m_timestamp = timestamp; } void insert(ConstArrayView strings, InsertMode mode, Vector* out_insert_pos = nullptr);