Skip to content

Commit

Permalink
Add by_bufroot cache
Browse files Browse the repository at this point in the history
I'm building a [`nix fmt`](https://nix.dev/manual/nix/latest/command-ref/new-cli/nix3-fmt) formatter.

The tricky thing about `nix fmt` is that for nontrivial projects,
the entrypoint (`nix fmt`) can be slow enough to be a bad experience to
run on every save, and can even trigger [neovim's lsp
timeout](https://github.com/nvimtools/none-ls.nvim?tab=readme-ov-file#i-am-seeing-a-formatting-timeout-error-message).
It's not the that underlying formatter it invokes are slow, it's that it
can take 1-2 seconds for nix to evaluate the `flake.nix` and determine
what command `nix fmt` should run under the hood.

Since the underlying command shouldn't change very often for a given
project, I feel like it would be reasonable to cache this result per
project, hence this new `by_bufroot` callback.
  • Loading branch information
jfly committed Oct 11, 2024
1 parent 0e0a940 commit fbbeda2
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 0 deletions.
17 changes: 17 additions & 0 deletions doc/HELPERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,20 @@ be too performance-intensive to include out-of-the-box.
Note that if `callback` returns `nil`, the helper will override the return value
and instead cache `false` (so that it can determine that it already ran
`callback` once and should not run it again).

### by_bufroot(callback)

Creates a function that caches the result of `callback`, indexed by `root`. On
the first run of the created function, null-ls will call `callback` with a
`params` table. On the next run, it will directly return the cached value
without calling `callback` again.

This is useful when the return value of `callback` is not expected to change
over the lifetime of the buffer, which works well for `cwd` and
`runtime_condition` callbacks. Users can use it as a simple shortcut to improve
performance, and built-in authors can use it to add logic that would otherwise
be too performance-intensive to include out-of-the-box.

Note that if `callback` returns `nil`, the helper will override the return value
and instead cache `false` (so that it can determine that it already ran
`callback` once and should not run it again).
21 changes: 21 additions & 0 deletions lua/null-ls/helpers/cache.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,27 @@ M.by_bufnr = function(cb)
end
end

--- creates a function that caches the output of a callback, indexed by project root
---@param cb function
---@return fun(params: NullLsParams): any
M.by_bufroot = function(cb)
-- assign next available key, since we just want to avoid collisions
local key = next_key
M.cache[key] = {}
next_key = next_key + 1

return function(params)
local root = params.root
-- if we haven't cached a value yet, get it from cb
if M.cache[key][root] == nil then
-- make sure we always store a value so we know we've already called cb
M.cache[key][root] = cb(params) or false
end

return M.cache[key][root]
end
end

M._reset = function()
M.cache = {}
end
Expand Down

0 comments on commit fbbeda2

Please sign in to comment.