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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>(',', '\\')
|
||||
|
|
|
@ -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)};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user