Skip to content

Commit

Permalink
fix: cache indent matches to avoid occasional race conditions (nvim-o…
Browse files Browse the repository at this point in the history
…rgmode#640)

This fixes the issue identified in nvim-orgmode#639
Fixes nvim-orgmode#639
  • Loading branch information
PriceHiller authored Jan 4, 2024
1 parent 0a28acc commit 07b9ac2
Showing 1 changed file with 31 additions and 2 deletions.
33 changes: 31 additions & 2 deletions lua/orgmode/org/indent.lua
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,42 @@ local function foldexpr()
return '='
end

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

return get_indent_for_match(matches, linenr, mode)
local bufnr = vim.api.nvim_get_current_buf()
local indentexpr_cache = buf_indentexpr_cache[bufnr] or { prev_linenr = -1 }
if indentexpr_cache.prev_linenr ~= linenr - 1 or not mode:lower():find('n') then
indentexpr_cache.matches = get_matches(0)
end

local new_indent = get_indent_for_match(indentexpr_cache.matches, linenr, mode)
local match = indentexpr_cache.matches[linenr]
if match then
match.indent = new_indent
end
indentexpr_cache.prev_linenr = linenr
buf_indentexpr_cache[bufnr] = indentexpr_cache
return new_indent
end

local function foldtext()
Expand Down

0 comments on commit 07b9ac2

Please sign in to comment.