@@ -251,13 +251,42 @@ local function foldexpr()
251
251
return ' ='
252
252
end
253
253
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 = {}
254
271
local function indentexpr (linenr , mode )
255
272
linenr = linenr or vim .v .lnum
256
273
mode = mode or vim .fn .mode ()
257
274
query = query or vim .treesitter .query .get (' org' , ' org_indent' )
258
- local matches = get_matches (0 )
259
275
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
261
290
end
262
291
263
292
local function foldtext ()
0 commit comments