feat(lsp): refactor lsp code to use Snacks.util.lsp.on

This commit is contained in:
Folke Lemaitre
2025-10-25 16:56:19 +02:00
parent f581de8013
commit 3964433062
9 changed files with 36 additions and 120 deletions

View File

@@ -85,9 +85,9 @@ return {
copilot_cmp.setup(opts)
-- attach cmp source whenever copilot attaches
-- fixes lazy-loading issues with the copilot cmp source
LazyVim.lsp.on_attach(function()
Snacks.util.lsp.on({ name = "copilot" }, function()
copilot_cmp._on_insert_enter({})
end, "copilot")
end)
end,
specs = {
{

View File

@@ -7,10 +7,8 @@ return {
lazy = true,
init = function()
vim.g.navic_silence = true
LazyVim.lsp.on_attach(function(client, buffer)
if client:supports_method("textDocument/documentSymbol") then
require("nvim-navic").attach(client, buffer)
end
Snacks.util.lsp.on({ method = "textDocument/documentSymbol" }, function(buffer, client)
require("nvim-navic").attach(client, buffer)
end)
end,
opts = function()

View File

@@ -35,10 +35,10 @@ return {
},
setup = {
angularls = function()
LazyVim.lsp.on_attach(function(client)
Snacks.util.lsp.on({ name = "angularls" }, function(_, client)
--HACK: disable angular renaming capability due to duplicate rename popping up
client.server_capabilities.renameProvider = false
end, "angularls")
end)
end,
},
},

View File

@@ -55,7 +55,7 @@ return {
gopls = function(_, opts)
-- workaround for gopls not supporting semanticTokensProvider
-- https://github.com/golang/go/issues/54531#issuecomment-1464982242
LazyVim.lsp.on_attach(function(client, _)
Snacks.util.lsp.on({ name = "gopls" }, function(_, client)
if not client.server_capabilities.semanticTokensProvider then
local semantic = client.config.capabilities.textDocument.semanticTokens
client.server_capabilities.semanticTokensProvider = {
@@ -67,7 +67,7 @@ return {
range = true,
}
end
end, "gopls")
end)
-- end workaround
end,
},

View File

@@ -58,10 +58,10 @@ return {
},
setup = {
[ruff] = function()
LazyVim.lsp.on_attach(function(client, _)
Snacks.util.lsp.on({ name = ruff }, function(_, client)
-- Disable hover in favor of Pyright
client.server_capabilities.hoverProvider = false
end, ruff)
end)
end,
},
},

View File

@@ -154,7 +154,7 @@ return {
resolve("vtsls")
end
LazyVim.lsp.on_attach(function(client, buffer)
Snacks.util.lsp.on({ name = "vtsls" }, function(buffer, client)
client.commands["_typescript.moveToFileRefactoring"] = function(command, ctx)
---@type string, string, lsp.Range
local action, uri, range = unpack(command.arguments)
@@ -203,7 +203,7 @@ return {
end)
end)
end
end, "vtsls")
end)
-- copy typescript settings to javascript
opts.settings.javascript =
vim.tbl_deep_extend("force", {}, opts.settings.typescript, opts.settings.javascript or {})

View File

@@ -125,16 +125,15 @@ return {
LazyVim.format.register(LazyVim.lsp.formatter())
-- setup keymaps
LazyVim.lsp.on_attach(function(client, buffer)
require("lazyvim.plugins.lsp.keymaps").on_attach(client, buffer)
end)
LazyVim.lsp.setup()
LazyVim.lsp.on_dynamic_capability(require("lazyvim.plugins.lsp.keymaps").on_attach)
for server, server_opts in pairs(opts.servers) do
if type(server_opts) == "table" and server_opts.keys then
require("lazyvim.plugins.lsp.keymaps").set({ name = server ~= "*" and server or nil }, server_opts.keys)
end
end
-- inlay hints
if opts.inlay_hints.enabled then
LazyVim.lsp.on_supports_method("textDocument/inlayHint", function(client, buffer)
Snacks.util.lsp.on({ method = "textDocument/inlayHint" }, function(buffer)
if
vim.api.nvim_buf_is_valid(buffer)
and vim.bo[buffer].buftype == ""
@@ -147,7 +146,7 @@ return {
-- folds
if opts.folds.enabled then
LazyVim.lsp.on_supports_method("textDocument/foldingRange", function(client, buffer)
Snacks.util.lsp.on({ method = "textDocument/foldingRange" }, function()
if LazyVim.set_default("foldmethod", "expr") then
LazyVim.set_default("foldexpr", "v:lua.vim.lsp.foldexpr()")
end
@@ -156,7 +155,7 @@ return {
-- code lens
if opts.codelens.enabled and vim.lsp.codelens then
LazyVim.lsp.on_supports_method("textDocument/codeLens", function(client, buffer)
Snacks.util.lsp.on({ method = "textDocument/codeLens" }, function(buffer)
vim.lsp.codelens.refresh()
vim.api.nvim_create_autocmd({ "BufEnter", "CursorHold", "InsertLeave" }, {
buffer = buffer,

View File

@@ -6,6 +6,22 @@ M.moved = {
rename_file = { "Snacks.rename.rename_file" },
on_rename = { "Snacks.rename.on_rename_file" },
words = { "Snacks.words" },
on_supports_method = {
"Snacks.util.lsp.on",
fn = function(method, cb)
return Snacks.util.lsp.on({ method = method }, function(buffer, client)
cb(client, buffer)
end)
end,
},
on_attach = {
"Snacks.util.lsp.on",
fn = function(cb, name)
return Snacks.util.lsp.on({ name = name }, function(buffer, client)
cb(client, buffer)
end)
end,
},
},
terminal = {
open = { "Snacks.terminal" },

View File

@@ -1,103 +1,6 @@
---@class lazyvim.util.lsp
local M = {}
---@param on_attach fun(client:vim.lsp.Client, buffer)
---@param name? string
function M.on_attach(on_attach, name)
return vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local buffer = args.buf ---@type number
local client = vim.lsp.get_client_by_id(args.data.client_id)
if client and (not name or client.name == name) then
return on_attach(client, buffer)
end
end,
})
end
---@type table<string, table<vim.lsp.Client, table<number, boolean>>>
M._supports_method = {}
function M.setup()
local register_capability = vim.lsp.handlers["client/registerCapability"]
vim.lsp.handlers["client/registerCapability"] = function(err, res, ctx)
---@diagnostic disable-next-line: no-unknown
local ret = register_capability(err, res, ctx)
local client = vim.lsp.get_client_by_id(ctx.client_id)
if client then
for buffer in pairs(client.attached_buffers) do
vim.api.nvim_exec_autocmds("User", {
pattern = "LspDynamicCapability",
data = { client_id = client.id, buffer = buffer },
})
end
end
return ret
end
M.on_attach(M._check_methods)
M.on_dynamic_capability(M._check_methods)
end
---@param client vim.lsp.Client
function M._check_methods(client, buffer)
-- don't trigger on invalid buffers
if not vim.api.nvim_buf_is_valid(buffer) then
return
end
-- don't trigger on non-listed buffers
if not vim.bo[buffer].buflisted then
return
end
-- don't trigger on nofile buffers
if vim.bo[buffer].buftype == "nofile" then
return
end
for method, clients in pairs(M._supports_method) do
clients[client] = clients[client] or {}
if not clients[client][buffer] then
if client.supports_method and client:supports_method(method, buffer) then
clients[client][buffer] = true
vim.api.nvim_exec_autocmds("User", {
pattern = "LspSupportsMethod",
data = { client_id = client.id, buffer = buffer, method = method },
})
end
end
end
end
---@param fn fun(client:vim.lsp.Client, buffer):boolean?
---@param opts? {group?: integer}
function M.on_dynamic_capability(fn, opts)
return vim.api.nvim_create_autocmd("User", {
pattern = "LspDynamicCapability",
group = opts and opts.group or nil,
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
local buffer = args.data.buffer ---@type number
if client then
return fn(client, buffer)
end
end,
})
end
---@param method string
---@param fn fun(client:vim.lsp.Client, buffer)
function M.on_supports_method(method, fn)
M._supports_method[method] = M._supports_method[method] or setmetatable({}, { __mode = "k" })
return vim.api.nvim_create_autocmd("User", {
pattern = "LspSupportsMethod",
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
local buffer = args.data.buffer ---@type number
if client and method == args.data.method then
return fn(client, buffer)
end
end,
})
end
---@param opts? LazyFormatter| {filter?: (string|vim.lsp.get_clients.Filter)}
function M.formatter(opts)
opts = opts or {}