]> git.rmz.io Git - dotfiles.git/blob - nvim/lua/rmz/util/lsp.lua
lazyvim: absorb formatting plugins
[dotfiles.git] / nvim / lua / rmz / util / lsp.lua
1 -- Absorbed from LazyVim
2 -- TODO: review, understand and adapt
3 ---@class rmz.util.lsp
4 local M = {}
5
6 ---@alias lsp.Client.filter {id?: number, bufnr?: number, name?: string, method?: string, filter?:fun(client: lsp.Client):boolean}
7
8 M.kind_filter = {
9 default = {
10 "Class",
11 "Constructor",
12 "Enum",
13 "Field",
14 "Function",
15 "Interface",
16 "Method",
17 "Module",
18 "Namespace",
19 "Package",
20 "Property",
21 "Struct",
22 "Trait",
23 },
24 markdown = false,
25 help = false,
26 -- you can specify a different filter for each filetype
27 lua = {
28 "Class",
29 "Constructor",
30 "Enum",
31 "Field",
32 "Function",
33 "Interface",
34 "Method",
35 "Module",
36 "Namespace",
37 -- "Package", -- remove package since luals uses it for control flow structures
38 "Property",
39 "Struct",
40 "Trait",
41 },
42 }
43
44 ---@param opts? lsp.Client.filter
45 function M.get_clients(opts)
46 local ret = {} ---@type vim.lsp.Client[]
47 if vim.lsp.get_clients then
48 ret = vim.lsp.get_clients(opts)
49 else
50 ---@diagnostic disable-next-line: deprecated
51 ret = vim.lsp.get_active_clients(opts)
52 if opts and opts.method then
53 ---@param client vim.lsp.Client
54 ret = vim.tbl_filter(function(client)
55 return client.supports_method(opts.method, { bufnr = opts.bufnr })
56 end, ret)
57 end
58 end
59 return opts and opts.filter and vim.tbl_filter(opts.filter, ret) or ret
60 end
61
62 ---@param on_attach fun(client:vim.lsp.Client, buffer)
63 ---@param name? string
64 function M.on_attach(on_attach, name)
65 return vim.api.nvim_create_autocmd("LspAttach", {
66 callback = function(args)
67 local buffer = args.buf ---@type number
68 local client = vim.lsp.get_client_by_id(args.data.client_id)
69 if client and (not name or client.name == name) then
70 return on_attach(client, buffer)
71 end
72 end,
73 })
74 end
75
76 ---@type table<string, table<vim.lsp.Client, table<number, boolean>>>
77 M._supports_method = {}
78
79 function M.setup()
80 local register_capability = vim.lsp.handlers["client/registerCapability"]
81 vim.lsp.handlers["client/registerCapability"] = function(err, res, ctx)
82 ---@diagnostic disable-next-line: no-unknown
83 local ret = register_capability(err, res, ctx)
84 local client = vim.lsp.get_client_by_id(ctx.client_id)
85 if client then
86 for buffer in pairs(client.attached_buffers) do
87 vim.api.nvim_exec_autocmds("User", {
88 pattern = "LspDynamicCapability",
89 data = { client_id = client.id, buffer = buffer },
90 })
91 end
92 end
93 return ret
94 end
95 M.on_attach(M._check_methods)
96 M.on_dynamic_capability(M._check_methods)
97 end
98
99 ---@param client vim.lsp.Client
100 function M._check_methods(client, buffer)
101 -- don't trigger on invalid buffers
102 if not vim.api.nvim_buf_is_valid(buffer) then
103 return
104 end
105 -- don't trigger on non-listed buffers
106 if not vim.bo[buffer].buflisted then
107 return
108 end
109 -- don't trigger on nofile buffers
110 if vim.bo[buffer].buftype == "nofile" then
111 return
112 end
113 for method, clients in pairs(M._supports_method) do
114 clients[client] = clients[client] or {}
115 if not clients[client][buffer] then
116 if client.supports_method and client.supports_method(method, { bufnr = buffer }) then
117 clients[client][buffer] = true
118 vim.api.nvim_exec_autocmds("User", {
119 pattern = "LspSupportsMethod",
120 data = { client_id = client.id, buffer = buffer, method = method },
121 })
122 end
123 end
124 end
125 end
126
127 ---@param fn fun(client:vim.lsp.Client, buffer):boolean?
128 ---@param opts? {group?: integer}
129 function M.on_dynamic_capability(fn, opts)
130 return vim.api.nvim_create_autocmd("User", {
131 pattern = "LspDynamicCapability",
132 group = opts and opts.group or nil,
133 callback = function(args)
134 local client = vim.lsp.get_client_by_id(args.data.client_id)
135 local buffer = args.data.buffer ---@type number
136 if client then
137 return fn(client, buffer)
138 end
139 end,
140 })
141 end
142
143 ---@param method string
144 ---@param fn fun(client:vim.lsp.Client, buffer)
145 function M.on_supports_method(method, fn)
146 M._supports_method[method] = M._supports_method[method] or setmetatable({}, { __mode = "k" })
147 return vim.api.nvim_create_autocmd("User", {
148 pattern = "LspSupportsMethod",
149 callback = function(args)
150 local client = vim.lsp.get_client_by_id(args.data.client_id)
151 local buffer = args.data.buffer ---@type number
152 if client and method == args.data.method then
153 return fn(client, buffer)
154 end
155 end,
156 })
157 end
158
159 ---@return _.lspconfig.options
160 function M.get_config(server)
161 local configs = require("lspconfig.configs")
162 return rawget(configs, server)
163 end
164
165 ---@return {default_config:lspconfig.Config}
166 function M.get_raw_config(server)
167 local ok, ret = pcall(require, "lspconfig.configs." .. server)
168 if ok then
169 return ret
170 end
171 return require("lspconfig.server_configurations." .. server)
172 end
173
174 function M.is_enabled(server)
175 local c = M.get_config(server)
176 return c and c.enabled ~= false
177 end
178
179 ---@param server string
180 ---@param cond fun( root_dir, config): boolean
181 function M.disable(server, cond)
182 local util = require("lspconfig.util")
183 local def = M.get_config(server)
184 ---@diagnostic disable-next-line: undefined-field
185 def.document_config.on_new_config = util.add_hook_before(def.document_config.on_new_config, function(config, root_dir)
186 if cond(root_dir, config) then
187 config.enabled = false
188 end
189 end)
190 end
191
192 M.action = setmetatable({}, {
193 __index = function(_, action)
194 return function()
195 vim.lsp.buf.code_action({
196 apply = true,
197 context = {
198 only = { action },
199 diagnostics = {},
200 },
201 })
202 end
203 end,
204 })
205
206 return M