Large files: bound highlighter cache, scan search once, row ranges as one edit
- HighlighterEngine caches spans only for rows near the requested row and trims the cache around it past 16k entries; per-row end states are still kept to resume lexing. Jumping to the end of a 78 MB C++ file cached every row's spans (854 MB RSS, now 197 MB), and the first edit near the top spent 270 ms freeing them. - Plain and regex search read lines as views into the materialized buffer instead of Buffer::Rows(), which built (and kept) a string per line, O(lines x pieces) after edits. Plain search scans the whole text once and tracks rows by newline. - transform_rows() applies a per-row rewrite of a row range as one edit (replace_rows_text). Indent/unindent region and visual-line insert, backspace, delete, newline and yank used one piece-table edit per row, each linear in the buffer: 1.1 s per keystroke with 5,000 rows selected in a 26 MB file (now 4 ms), and minutes to indent a large region. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
@@ -65,6 +65,7 @@ HighlighterEngine::GetLine(const Buffer &buf, int row, std::uint64_t buf_version
|
||||
if (!stateful) {
|
||||
hl_->HighlightLine(buf, row, result.spans);
|
||||
cache_[row] = result;
|
||||
trim_cache_locked(row);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -86,14 +87,28 @@ HighlighterEngine::GetLine(const Buffer &buf, int row, std::uint64_t buf_version
|
||||
states_[static_cast<std::size_t>(r)] = state;
|
||||
if (r == row)
|
||||
result = lh;
|
||||
else
|
||||
else if (row - r <= kCacheNear)
|
||||
cache_.insert_or_assign(r, std::move(lh));
|
||||
// Rows further above only needed their end state (kept in states_):
|
||||
// caching their spans too held about 10x the file size in memory
|
||||
// after jumping to the end of a large file.
|
||||
}
|
||||
cache_[row] = result;
|
||||
trim_cache_locked(row);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HighlighterEngine::trim_cache_locked(const int row) const
|
||||
{
|
||||
if (cache_.size() <= kCacheMax)
|
||||
return;
|
||||
cache_.erase(cache_.begin(), cache_.lower_bound(row - kCacheNear));
|
||||
cache_.erase(cache_.upper_bound(row + kCacheNear), cache_.end());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HighlighterEngine::InvalidateFrom(int row)
|
||||
{
|
||||
|
||||
@@ -68,6 +68,13 @@ private:
|
||||
|
||||
void invalidate_from_locked(int row) const;
|
||||
|
||||
// Span cache bounds: rows within kCacheNear of a requested row are
|
||||
// cached; past kCacheMax entries, those far from the current row go.
|
||||
static constexpr int kCacheNear = 1024;
|
||||
static constexpr std::size_t kCacheMax = 16384;
|
||||
|
||||
void trim_cache_locked(int row) const;
|
||||
|
||||
// Guards the caches above.
|
||||
mutable std::mutex mtx_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user