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)
{
// 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<StringView>('\n') | gather<Vector<StringView>>();
const auto lines_after = after | split_after<StringView>('\n') | gather<Vector<StringView>>();
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;
}
}

View File

@ -16,6 +16,9 @@ UnitTest test_ranges{[] {
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>(',', '\\')
| transform(unescape<',', '\\'>), {"a,", ",b", ","});
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)));
}
template<typename Range, bool escape = false,
template<typename Range, bool escape, bool include_separator,
typename Element = ValueOf<Range>,
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<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) {
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; }
size_t timestamp() const { return m_timestamp; }
void force_timestamp(size_t timestamp) { m_timestamp = timestamp; }
void insert(ConstArrayView<String> strings, InsertMode mode,
Vector<BufferCoord>* out_insert_pos = nullptr);