Fix performance of diff-based reloading of buffers

It turns out diffing was pretty fast, but applying the diff was
sub-optimal as it was constantly inserting/erasing lines which
led to lots of unnecessary shifting. Fix this by manually tracking
a read/write iterator and only shifting when necessary (on keeps,
and inserts).
This commit is contained in:
Maxime Coste 2023-12-26 20:55:47 +11:00
parent 68e73d8a24
commit 43e8aadcaa

View File

@ -219,40 +219,55 @@ void Buffer::reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, F
[](const StringDataPtr& lhs, const StringDataPtr& rhs) [](const StringDataPtr& lhs, const StringDataPtr& rhs)
{ return lhs->strview() == rhs->strview(); }); { return lhs->strview() == rhs->strview(); });
auto it = m_lines.begin(); auto read_it = m_lines.begin();
auto write_it = m_lines.begin();
auto new_it = lines.begin(); auto new_it = lines.begin();
for (auto& [op, len] : diff) for (auto [op, len] : diff)
{ {
kak_assert(read_it >= write_it);
if (op == DiffOp::Keep) if (op == DiffOp::Keep)
{ {
it += len; if (read_it != write_it)
std::move(read_it, read_it + len, write_it);
write_it += len;
read_it += len;
new_it += len; new_it += len;
} }
else if (op == DiffOp::Add) else if (op == DiffOp::Add)
{ {
const LineCount cur_line = (int)(it - m_lines.begin()); const LineCount cur_line = (int)(write_it - m_lines.begin());
for (LineCount line = 0; line < len; ++line) for (LineCount line = 0; line < len; ++line)
m_current_undo_group.push_back({Modification::Insert, cur_line + line, *(new_it + (int)line)}); m_current_undo_group.push_back({Modification::Insert, cur_line + line, *(new_it + (int)line)});
m_changes.push_back({Change::Insert, cur_line, cur_line + len}); m_changes.push_back({Change::Insert, cur_line, cur_line + len});
m_lines.insert(it, new_it, new_it + len);
it = m_lines.begin() + (int)(cur_line + len); if (read_it != write_it)
{
auto count = std::min(len, static_cast<int>(read_it - write_it));
write_it = std::copy(new_it, new_it + count, write_it);
new_it += count;
if (len == count)
continue;
len -= count;
}
auto read_pos = read_it - m_lines.begin();
write_it = m_lines.insert(write_it, new_it, new_it + len) + len;
read_it = m_lines.begin() + read_pos + len;
new_it += len; new_it += len;
} }
else if (op == DiffOp::Remove) else if (op == DiffOp::Remove)
{ {
const LineCount cur_line = (int)(it - m_lines.begin()); const LineCount cur_line = (int)(write_it - m_lines.begin());
for (LineCount line = len-1; line >= 0; --line) for (LineCount line = len-1; line >= 0; --line)
m_current_undo_group.push_back({ m_current_undo_group.push_back({
Modification::Erase, cur_line + line, Modification::Erase, cur_line + line,
m_lines.get_storage(cur_line + line)}); m_lines.get_storage(cur_line + line)});
it = m_lines.erase(it, it + len); read_it += len;
m_changes.push_back({ Change::Erase, cur_line, cur_line + len }); m_changes.push_back({ Change::Erase, cur_line, cur_line + len });
} }
} }
m_lines.erase(write_it, m_lines.end());
} }
commit_undo_group(); commit_undo_group();