mirror of
https://github.com/folke/snacks.nvim
synced 2025-07-07 21:25:11 +00:00
Optimize Snacks.util.bo with buffer option caching to improve file picker performance
Co-authored-by: spenrose <481185+spenrose@users.noreply.github.com>
This commit is contained in:
parent
e270b8c003
commit
cc21f180b0
2 changed files with 154 additions and 2 deletions
|
@ -11,6 +11,10 @@ local uv = vim.uv or vim.loop
|
|||
local key_cache = {} ---@type table<string, string>
|
||||
local langs = {} ---@type table<string, boolean>
|
||||
|
||||
-- Cache for buffer options to avoid redundant API calls
|
||||
local buf_options_cache = {} ---@type table<number, table<string, any>>
|
||||
local buf_cache_autocmd_id = nil
|
||||
|
||||
---@alias snacks.util.hl table<string, string|vim.api.keyset.highlight>
|
||||
|
||||
local hl_groups = {} ---@type table<string, vim.api.keyset.highlight>
|
||||
|
@ -89,8 +93,49 @@ end
|
|||
---@param buf number
|
||||
---@param bo vim.bo|{}
|
||||
function M.bo(buf, bo)
|
||||
for k, v in pairs(bo or {}) do
|
||||
vim.api.nvim_set_option_value(k, v, { buf = buf })
|
||||
if not bo or not next(bo) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Ensure buffer is valid
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
-- Clean up cache for invalid buffer
|
||||
buf_options_cache[buf] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Initialize cache for this buffer if not exists
|
||||
if not buf_options_cache[buf] then
|
||||
buf_options_cache[buf] = {}
|
||||
end
|
||||
|
||||
local cache = buf_options_cache[buf]
|
||||
|
||||
for k, v in pairs(bo) do
|
||||
-- Only set option if value has changed
|
||||
if cache[k] ~= v then
|
||||
local ok, err = pcall(vim.api.nvim_set_option_value, k, v, { buf = buf })
|
||||
if ok then
|
||||
cache[k] = v
|
||||
else
|
||||
-- If setting option failed, don't cache the value
|
||||
-- This ensures we'll try again next time
|
||||
cache[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Setup autocmd to clean up cache when buffers are deleted (only once)
|
||||
if not buf_cache_autocmd_id then
|
||||
buf_cache_autocmd_id = vim.api.nvim_create_autocmd("BufDelete", {
|
||||
group = vim.api.nvim_create_augroup("snacks_util_buf_cache", { clear = true }),
|
||||
callback = function(args)
|
||||
local deleted_buf = args.buf
|
||||
if buf_options_cache[deleted_buf] then
|
||||
buf_options_cache[deleted_buf] = nil
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -37,3 +37,110 @@ describe("util.normkey", function()
|
|||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
describe("util.bo", function()
|
||||
local util = require("snacks.util")
|
||||
|
||||
-- Helper to count actual API calls by mocking vim.api.nvim_set_option_value
|
||||
local api_call_count = 0
|
||||
local original_set_option = vim.api.nvim_set_option_value
|
||||
|
||||
before_each(function()
|
||||
api_call_count = 0
|
||||
vim.api.nvim_set_option_value = function(...)
|
||||
api_call_count = api_call_count + 1
|
||||
return original_set_option(...)
|
||||
end
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
vim.api.nvim_set_option_value = original_set_option
|
||||
end)
|
||||
|
||||
it("should set buffer options correctly", function()
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
util.bo(buf, { buftype = "nofile", filetype = "lua" })
|
||||
|
||||
-- Verify options were set
|
||||
assert.are.equal("nofile", vim.api.nvim_get_option_value("buftype", { buf = buf }))
|
||||
assert.are.equal("lua", vim.api.nvim_get_option_value("filetype", { buf = buf }))
|
||||
assert.are.equal(2, api_call_count)
|
||||
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
end)
|
||||
|
||||
it("should cache options and avoid redundant API calls", function()
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
-- First call should set options
|
||||
util.bo(buf, { buftype = "nofile", filetype = "lua" })
|
||||
assert.are.equal(2, api_call_count)
|
||||
|
||||
-- Second call with same options should not call API
|
||||
api_call_count = 0
|
||||
util.bo(buf, { buftype = "nofile", filetype = "lua" })
|
||||
assert.are.equal(0, api_call_count)
|
||||
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
end)
|
||||
|
||||
it("should detect changes and update only changed options", function()
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
-- Set initial options
|
||||
util.bo(buf, { buftype = "nofile", filetype = "lua" })
|
||||
assert.are.equal(2, api_call_count)
|
||||
|
||||
-- Change only one option
|
||||
api_call_count = 0
|
||||
util.bo(buf, { buftype = "nofile", filetype = "javascript" })
|
||||
assert.are.equal(1, api_call_count) -- Only filetype should be updated
|
||||
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
end)
|
||||
|
||||
it("should handle empty or nil options", function()
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
-- Should handle nil options
|
||||
util.bo(buf, nil)
|
||||
assert.are.equal(0, api_call_count)
|
||||
|
||||
-- Should handle empty table
|
||||
util.bo(buf, {})
|
||||
assert.are.equal(0, api_call_count)
|
||||
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
end)
|
||||
|
||||
it("should handle invalid buffers gracefully", function()
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
|
||||
-- Should not error with invalid buffer
|
||||
util.bo(buf, { buftype = "nofile" })
|
||||
assert.are.equal(0, api_call_count)
|
||||
end)
|
||||
|
||||
it("should clean up cache when buffer is deleted", function()
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
-- Set options to populate cache
|
||||
util.bo(buf, { buftype = "nofile" })
|
||||
assert.are.equal(1, api_call_count)
|
||||
|
||||
-- Delete buffer (this should trigger cache cleanup via autocmd)
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
|
||||
-- Processing autocmds
|
||||
vim.api.nvim_exec_autocmds("BufDelete", { buffer = buf })
|
||||
|
||||
-- Create new buffer with same ID shouldn't use old cache
|
||||
local new_buf = vim.api.nvim_create_buf(false, true)
|
||||
api_call_count = 0
|
||||
util.bo(new_buf, { buftype = "nofile" })
|
||||
assert.are.equal(1, api_call_count) -- Should call API, not use cache
|
||||
|
||||
vim.api.nvim_buf_delete(new_buf, { force = true })
|
||||
end)
|
||||
end)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue