From fbbeda25f33e92742637ba50045c4bf0c5b8d0f3 Mon Sep 17 00:00:00 2001 From: Jeremy Fleischman Date: Sun, 15 Sep 2024 06:23:36 -0700 Subject: [PATCH] Add `by_bufroot` cache 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. --- doc/HELPERS.md | 17 +++++++++++++++++ lua/null-ls/helpers/cache.lua | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/doc/HELPERS.md b/doc/HELPERS.md index 8ccfbe83..2aa72c99 100644 --- a/doc/HELPERS.md +++ b/doc/HELPERS.md @@ -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). diff --git a/lua/null-ls/helpers/cache.lua b/lua/null-ls/helpers/cache.lua index 89a48a82..9795e440 100644 --- a/lua/null-ls/helpers/cache.lua +++ b/lua/null-ls/helpers/cache.lua @@ -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