mirror of
https://github.com/folke/snacks.nvim
synced 2025-12-23 08:47:57 +00:00
feat(util): add LSP utility module with dynamic capability handlers
Add `Snacks.util.lsp.on()` to register handlers that fire when LSP clients attach with specific capabilities. Supports filtering by: - LSP method/capability - Client name - Buffer ID - Any vim.lsp.get_clients() filter Features: - Handles both LspAttach and client/registerCapability events - Ensures handlers only fire once per buffer - Lazy-loaded via Snacks.util metatable This provides a foundation for LSP-aware features like conditional keymaps. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f75eaf1e18
commit
7a63ba5d37
2 changed files with 95 additions and 1 deletions
|
|
@ -1,5 +1,15 @@
|
||||||
---@class snacks.util
|
---@class snacks.util
|
||||||
local M = {}
|
---@field spawn snacks.spawn
|
||||||
|
---@field lsp snacks.lsp
|
||||||
|
local M = setmetatable({}, {
|
||||||
|
---@param M snacks.util
|
||||||
|
__index = function(M, k)
|
||||||
|
if vim.tbl_contains({ "spawn", "lsp" }, k) then
|
||||||
|
M[k] = require("snacks.util." .. k)
|
||||||
|
end
|
||||||
|
return rawget(M, k)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
M.meta = {
|
M.meta = {
|
||||||
desc = "Utility functions for Snacks _(library)_",
|
desc = "Utility functions for Snacks _(library)_",
|
||||||
|
|
|
||||||
84
lua/snacks/util/lsp.lua
Normal file
84
lua/snacks/util/lsp.lua
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
---@class snacks.lsp
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
---@alias snacks.lsp.handler.cb fun(buf: number, client: vim.lsp.Client):any?
|
||||||
|
|
||||||
|
---@class snacks.lsp.Handler
|
||||||
|
---@field filter vim.lsp.get_clients.Filter
|
||||||
|
---@field cb snacks.lsp.handler.cb
|
||||||
|
---@field done table<number, boolean>
|
||||||
|
|
||||||
|
local _handlers = {} ---@type snacks.lsp.Handler[]
|
||||||
|
|
||||||
|
local did_setup = false
|
||||||
|
|
||||||
|
---@param filter vim.lsp.get_clients.Filter
|
||||||
|
local function _handle(filter)
|
||||||
|
---@param h snacks.lsp.Handler
|
||||||
|
local handlers = vim.tbl_filter(function(h)
|
||||||
|
---@diagnostic disable-next-line: no-unknown
|
||||||
|
for k, v in pairs(filter) do
|
||||||
|
if h.filter[k] ~= nil and h.filter[k] ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end, _handlers)
|
||||||
|
|
||||||
|
if #handlers == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, state in ipairs(handlers) do
|
||||||
|
local f = vim.deepcopy(state.filter)
|
||||||
|
f = vim.tbl_extend("force", f, filter)
|
||||||
|
local clients = vim.lsp.get_clients(f)
|
||||||
|
for _, client in ipairs(clients) do
|
||||||
|
for buf in pairs(client.attached_buffers) do
|
||||||
|
if not state.done[buf] then
|
||||||
|
state.done[buf] = true
|
||||||
|
local ok, err = pcall(state.cb, buf, client)
|
||||||
|
if not ok then
|
||||||
|
vim.schedule(function()
|
||||||
|
Snacks.notify.error(("Error in handler:\n%s\n```lua\n%s\n```"):format(err, vim.inspect(state.filter)))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup()
|
||||||
|
if did_setup then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
did_setup = true
|
||||||
|
local register_capability = vim.lsp.handlers["client/registerCapability"]
|
||||||
|
vim.lsp.handlers["client/registerCapability"] = function(err, res, ctx)
|
||||||
|
---@cast res lsp.RegistrationParams
|
||||||
|
local ret = register_capability(err, res, ctx) ---@type any
|
||||||
|
vim.schedule(function()
|
||||||
|
for _, m in ipairs(res.registrations or {}) do
|
||||||
|
_handle({ method = m.method, id = ctx.client_id })
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
vim.api.nvim_create_autocmd("LspAttach", {
|
||||||
|
group = vim.api.nvim_create_augroup("snacks.lsp.on_attach", { clear = true }),
|
||||||
|
callback = function(ev)
|
||||||
|
_handle({ id = ev.data.client_id, buffer = ev.buf })
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param filter vim.lsp.get_clients.Filter
|
||||||
|
---@param cb snacks.lsp.handler.cb
|
||||||
|
function M.on(filter, cb)
|
||||||
|
setup()
|
||||||
|
table.insert(_handlers, { filter = filter, cb = cb, done = {} })
|
||||||
|
_handle(filter)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
Loading…
Add table
Add a link
Reference in a new issue