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:
parent
370d10ccc7
commit
36e9e7eaf9
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>(',', '\\')
|
||||||
|
|
|
@ -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)};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user