Skip to content

Commit 433c70a

Browse files
authored
fix: cache indent matches to avoid occasional race conditions (#640)
This fixes the issue identified in #639 Fixes #639
1 parent a273800 commit 433c70a

File tree

1 file changed

+31
-2
lines changed

1 file changed

+31
-2
lines changed

lua/orgmode/org/indent.lua

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,13 +251,42 @@ local function foldexpr()
251251
return '='
252252
end
253253

254+
-- Some explanation as to the caching insanity inside of this function. The `get_matches` function
255+
-- is memoized, but that only goes so far. When a user wants to indent a large region, say with
256+
-- `norm! 0gg=G` every indent operation will call `get_matches` and get *new* matches. For the most
257+
-- part, this is fine, but on occasion the cache can end up invalidated when the indent operation doesn't
258+
-- occur fast enough. When the cache is invalidated new matches are returned and this leads to an
259+
-- issue in which the indent calculated for the line is no longer correct as it is based on bad
260+
-- data. This causes indents, especially for lists, to be incorrect as many indents are dependent on
261+
-- the previous node's indentation.
262+
--
263+
-- By caching the matches and previous line numbers matched we can effectively check if a range was
264+
-- requested for indentation and, if so, stop requesting new matches; then we only use the initial
265+
-- matches while updating the previous indent amounts as we return the new indents. We invalidate
266+
-- the cached matches when the user isn't in normal mode as it's likely they're modifying buffer
267+
-- content which requires us to get the updated matches for the changed content.
268+
--
269+
-- TLDR: The caching avoids some inconsistent race conditions with getting the Treesitter matches.
270+
local buf_indentexpr_cache = {}
254271
local function indentexpr(linenr, mode)
255272
linenr = linenr or vim.v.lnum
256273
mode = mode or vim.fn.mode()
257274
query = query or vim.treesitter.query.get('org', 'org_indent')
258-
local matches = get_matches(0)
259275

260-
return get_indent_for_match(matches, linenr, mode)
276+
local bufnr = vim.api.nvim_get_current_buf()
277+
local indentexpr_cache = buf_indentexpr_cache[bufnr] or { prev_linenr = -1 }
278+
if indentexpr_cache.prev_linenr ~= linenr - 1 or not mode:lower():find('n') then
279+
indentexpr_cache.matches = get_matches(0)
280+
end
281+
282+
local new_indent = get_indent_for_match(indentexpr_cache.matches, linenr, mode)
283+
local match = indentexpr_cache.matches[linenr]
284+
if match then
285+
match.indent = new_indent
286+
end
287+
indentexpr_cache.prev_linenr = linenr
288+
buf_indentexpr_cache[bufnr] = indentexpr_cache
289+
return new_indent
261290
end
262291

263292
local function foldtext()

0 commit comments

Comments
 (0)