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
This commit is contained in:
Maxime Coste 2019-01-22 21:04:29 +11:00
parent 370d10ccc7
commit 36e9e7eaf9
4 changed files with 38 additions and 16 deletions

View File

@ -491,29 +491,33 @@ void command(Context& context, NormalParams params)
BufferCoord apply_diff(Buffer& buffer, BufferCoord pos, StringView before, StringView after) 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 const auto lines_before = before | split_after<StringView>('\n') | gather<Vector<StringView>>();
// do not use it if our data is too big const auto lines_after = after | split_after<StringView>('\n') | gather<Vector<StringView>>();
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);
}
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) for (auto& diff : diffs)
{ {
switch (diff.mode) switch (diff.mode)
{ {
case Diff::Keep: case Diff::Keep:
pos = buffer.advance(pos, diff.len); pos = buffer.advance(pos, byte_count(lines_before, posA, diff.len));
posA += diff.len;
break; break;
case Diff::Add: 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; break;
case Diff::Remove: 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; break;
} }
} }

View File

@ -16,6 +16,9 @@ UnitTest test_ranges{[] {
check_equal(","_sv | split<StringView>(','), {"", ""}); check_equal(","_sv | split<StringView>(','), {"", ""});
check_equal(""_sv | split<StringView>(','), {}); check_equal(""_sv | split<StringView>(','), {});
check_equal("a,b,c,"_sv | split_after<StringView>(','), {"a,", "b,", "c,"});
check_equal("a,b,c"_sv | split_after<StringView>(','), {"a,", "b,", "c"});
check_equal(R"(a\,,\,b,\,)"_sv | split<StringView>(',', '\\') check_equal(R"(a\,,\,b,\,)"_sv | split<StringView>(',', '\\')
| transform(unescape<',', '\\'>), {"a,", ",b", ","}); | transform(unescape<',', '\\'>), {"a,", ",b", ","});
check_equal(R"(\,\,)"_sv | split<StringView>(',', '\\') check_equal(R"(\,\,)"_sv | split<StringView>(',', '\\')

View File

@ -197,7 +197,7 @@ inline auto transform(M T::*m)
return transform(std::mem_fn(std::forward<decltype(m)>(m))); return transform(std::mem_fn(std::forward<decltype(m)>(m)));
} }
template<typename Range, bool escape = false, template<typename Range, bool escape, bool include_separator,
typename Element = ValueOf<Range>, typename Element = ValueOf<Range>,
typename ValueTypeParam = void> typename ValueTypeParam = void>
struct SplitView 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 and done == other.done; }
bool operator!=(const Iterator& other) const { return pos != other.pos or 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: private:
void advance() void advance()
@ -239,6 +239,11 @@ struct SplitView
} }
pos = sep+1; pos = sep+1;
if (include_separator and pos == end)
{
done = true;
return;
}
bool escaped = escape and *sep == escaper; bool escaped = escape and *sep == escaper;
for (sep = pos; sep != end; ++sep) for (sep = pos; sep != end; ++sep)
{ {
@ -269,7 +274,16 @@ auto split(Element separator)
{ {
return make_view_factory([s = std::move(separator)](auto&& range) { return make_view_factory([s = std::move(separator)](auto&& range) {
using Range = decltype(range); using Range = decltype(range);
return SplitView<decay_range<Range>, false, Element, ValueType>{std::forward<Range>(range), std::move(s), {}}; return SplitView<decay_range<Range>, false, false, Element, ValueType>{std::forward<Range>(range), std::move(s), {}};
});
}
template<typename ValueType = void, typename Element>
auto split_after(Element separator)
{
return make_view_factory([s = std::move(separator)](auto&& range) {
using Range = decltype(range);
return SplitView<decay_range<Range>, false, true, Element, ValueType>{std::forward<Range>(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) { return make_view_factory([s = std::move(separator), e = std::move(escaper)](auto&& range) {
using Range = decltype(range); using Range = decltype(range);
return SplitView<decay_range<Range>, true, Element, ValueType>{std::forward<Range>(range), std::move(s), std::move(e)}; return SplitView<decay_range<Range>, true, false, Element, ValueType>{std::forward<Range>(range), std::move(s), std::move(e)};
}); });
} }

View File

@ -140,6 +140,7 @@ struct SelectionList
Buffer& buffer() const { return *m_buffer; } Buffer& buffer() const { return *m_buffer; }
size_t timestamp() const { return m_timestamp; } size_t timestamp() const { return m_timestamp; }
void force_timestamp(size_t timestamp) { m_timestamp = timestamp; }
void insert(ConstArrayView<String> strings, InsertMode mode, void insert(ConstArrayView<String> strings, InsertMode mode,
Vector<BufferCoord>* out_insert_pos = nullptr); Vector<BufferCoord>* out_insert_pos = nullptr);