Skip to content

Commit 3629625

Browse files
authored
perf(foldtext): cache fold highlights by state (#1077)
* perf(fold): cache fold highlights by state Navigation over folded sections in larger org files felt sluggish due to repeated treesitter queries on every cursor move. Check fold state before using cached highlights - cache hit when unchanged, full recompute only when folds toggle. * test(fold): implement several fold tests
1 parent cf75da1 commit 3629625

4 files changed

Lines changed: 444 additions & 15 deletions

File tree

lua/orgmode/colors/highlighter/foldtext.lua

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
local config = require('orgmode.config')
2-
local utils = require('orgmode.utils')
32

43
---@alias OrgFoldtextLineValue false | { col: number, hl_group: string }
54

@@ -32,26 +31,56 @@ function OrgFoldtext:_highlight(bufnr, line, value)
3231
hl_mode = 'combine',
3332
virt_text = { { config.org_ellipsis, value.hl_group } },
3433
virt_text_pos = 'overlay',
35-
ephemeral = true,
34+
ephemeral = self.highlighter._ephemeral,
3635
})
3736
end
3837

39-
function OrgFoldtext:on_line(bufnr, line)
40-
local is_current_buf = bufnr == vim.api.nvim_get_current_buf()
38+
function OrgFoldtext:on_line(bufnr, line, winid)
39+
if not config.ui.folds.colored then
40+
return
41+
end
42+
43+
-- Use provided winid for correct window context (foldclosed and col are window-local)
44+
local lnum = line + 1
45+
local is_fold_open, line_end
4146

42-
if not is_current_buf then
43-
return self:_highlight(bufnr, line, self.cache[bufnr] and self.cache[bufnr][line])
47+
if winid and winid ~= vim.api.nvim_get_current_win() then
48+
-- foldclosed() and col() are window-local, so execute in the correct window
49+
vim.api.nvim_win_call(winid, function()
50+
is_fold_open = vim.fn.foldclosed(lnum) == -1
51+
line_end = vim.fn.col({ lnum, '$' }) or 0
52+
end)
53+
else
54+
is_fold_open = vim.fn.foldclosed(lnum) == -1
55+
line_end = vim.fn.col({ lnum, '$' }) or 0
4456
end
4557

58+
local cache_entry = self.cache[bufnr] and self.cache[bufnr][line]
59+
60+
-- Cache: nil = unprocessed, false = open, {col, hl_group} = closed
61+
if cache_entry ~= nil then
62+
local was_open = cache_entry == false
63+
if was_open == is_fold_open then
64+
if cache_entry and cache_entry.col then
65+
if cache_entry.col == line_end - 1 then
66+
return self:_highlight(bufnr, line, cache_entry)
67+
end
68+
-- Line length changed, need full update
69+
else
70+
return self:_highlight(bufnr, line, cache_entry)
71+
end
72+
end
73+
end
74+
75+
-- Full update: query treesitter
4676
self.cache[bufnr] = self.cache[bufnr] or {}
47-
local is_fold_closed = vim.fn.foldclosed(line + 1) > -1
4877

49-
if not is_fold_closed then
78+
if is_fold_open then
5079
self.cache[bufnr][line] = false
51-
return
80+
return -- No ellipsis to highlight
5281
end
5382

54-
local col = vim.fn.col({ line + 1, '$' }) or 0
83+
local col = line_end
5584

5685
local hl_group = 'Comment'
5786
local captures_at_pos = vim.treesitter.get_captures_at_pos(bufnr, line, col - 2)

lua/orgmode/colors/highlighter/init.lua

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,25 @@ function OrgHighlighter:_on_win(_, win, bufnr, topline, botline)
8181
self:_parse_tree(bufnr, win, { topline, botline + 1 })
8282
if self.parsing[win] then
8383
for line = topline, botline do
84-
self:_on_line_impl(bufnr, line, true)
84+
self:_on_line_impl(bufnr, line, true, win)
8585
end
8686
return false
8787
end
8888
end
8989
end
9090

91-
function OrgHighlighter:_on_line(_, _, bufnr, line)
92-
self:_on_line_impl(bufnr, line)
91+
function OrgHighlighter:_on_line(_, winid, bufnr, line)
92+
self:_on_line_impl(bufnr, line, false, winid)
9393
end
9494

9595
---@param bufnr number
9696
---@param line number
9797
---@param use_cache? boolean
98-
function OrgHighlighter:_on_line_impl(bufnr, line, use_cache)
98+
function OrgHighlighter:_on_line_impl(bufnr, line, use_cache, winid)
9999
if self.buffers[bufnr].tree then
100100
self.markup:on_line(bufnr, line, self.buffers[bufnr].tree, use_cache)
101101
self.stars:on_line(bufnr, line)
102-
self.foldtext:on_line(bufnr, line)
102+
self.foldtext:on_line(bufnr, line, winid)
103103
end
104104
end
105105

0 commit comments

Comments
 (0)