]> git.rmz.io Git - dotfiles.git/commitdiff
lazyvim: absorb lazyvim.util.lsp
authorSamir Benmendil <me@rmz.io>
Sun, 16 Feb 2025 17:03:46 +0000 (17:03 +0000)
committerSamir Benmendil <me@rmz.io>
Sun, 2 Mar 2025 16:05:27 +0000 (16:05 +0000)
Leaving it as is for now, I'm going to have to find more time to read
and understand what it does.

There's some clever things going on there to have one place to customize
the keymaps and apply them to all lsps that support that function.

nvim/lua/plugins/lsp/init.lua
nvim/lua/plugins/lsp/keymaps.lua
nvim/lua/plugins/picker.lua
nvim/lua/plugins/ui.lua
nvim/lua/rmz/util/init.lua
nvim/lua/rmz/util/lsp.lua [new file with mode: 0644]

index 351f0606ae2dd9b748f0817e15d080318f3615e3..d32e76c8b333359fdc3fe160ebebe95eeeb63c25 100644 (file)
@@ -95,21 +95,19 @@ return {
       end
 
       -- setup keymaps
       end
 
       -- setup keymaps
-      LazyVim.lsp.on_attach(function(client, buffer)
+      rmz.lsp.on_attach(function(client, buffer)
         require("plugins.lsp.keymaps").on_attach(client, buffer)
       end)
 
         require("plugins.lsp.keymaps").on_attach(client, buffer)
       end)
 
-      LazyVim.lsp.setup()
-      LazyVim.lsp.on_dynamic_capability(require("plugins.lsp.keymaps").on_attach)
+      rmz.lsp.setup()
+      rmz.lsp.on_dynamic_capability(require("plugins.lsp.keymaps").on_attach)
       -- TODO: should vim.diagnostics be condigured directly?
       vim.diagnostic.config(vim.deepcopy(opts.diagnostics))
 
       -- get all the servers that are available through mason-lspconfig
       -- TODO: should vim.diagnostics be condigured directly?
       vim.diagnostic.config(vim.deepcopy(opts.diagnostics))
 
       -- get all the servers that are available through mason-lspconfig
-      local have_mason, mlsp = pcall(require, "mason-lspconfig")
-      local all_mslp_servers = {}
-      if have_mason then
-        all_mslp_servers = vim.tbl_keys(require("mason-lspconfig.mappings.server").lspconfig_to_package)
-      end
+      local mlsp = require("mason-lspconfig")
+      -- TODO: use mason-lspconfig.get_available_servers()?
+      local all_mslp_servers = vim.tbl_keys(require("mason-lspconfig.mappings.server").lspconfig_to_package)
 
       local ensure_installed = {} ---@type string[]
       for server, server_opts in pairs(opts.servers) do
 
       local ensure_installed = {} ---@type string[]
       for server, server_opts in pairs(opts.servers) do
@@ -126,16 +124,14 @@ return {
         end
       end
 
         end
       end
 
-      if have_mason then
-        mlsp.setup({
-          ensure_installed = vim.tbl_deep_extend(
-            "force",
-            ensure_installed,
-            LazyVim.opts("mason-lspconfig.nvim").ensure_installed or {}
-          ),
-          handlers = { setup },
-        })
-      end
+      mlsp.setup({
+        ensure_installed = vim.tbl_deep_extend(
+          "force",
+          ensure_installed,
+          LazyVim.opts("mason-lspconfig.nvim").ensure_installed or {}
+        ),
+        handlers = { setup },
+      })
     end
   },
   { "williamboman/mason.nvim",
     end
   },
   { "williamboman/mason.nvim",
index 72cfa51cff8554d079a7f19a21836f285818b0e7..7cb2b49831aeeb16901d4bf3c447f4722c1b822a 100644 (file)
@@ -27,7 +27,7 @@ function M.get()
       { "<leader>cC", vim.lsp.codelens.refresh, desc = "Refresh & Display Codelens", mode = { "n" }, has = "codeLens" },
       { "<leader>cR", function() Snacks.rename.rename_file() end, desc = "Rename File", mode ={"n"}, has = { "workspace/didRenameFiles", "workspace/willRenameFiles" } },
       { "<leader>cr", vim.lsp.buf.rename, desc = "Rename", has = "rename" },
       { "<leader>cC", vim.lsp.codelens.refresh, desc = "Refresh & Display Codelens", mode = { "n" }, has = "codeLens" },
       { "<leader>cR", function() Snacks.rename.rename_file() end, desc = "Rename File", mode ={"n"}, has = { "workspace/didRenameFiles", "workspace/willRenameFiles" } },
       { "<leader>cr", vim.lsp.buf.rename, desc = "Rename", has = "rename" },
-      { "<leader>cA", LazyVim.lsp.action.source, desc = "Source Action", has = "codeAction" },
+      { "<leader>cA", rmz.lsp.action.source, desc = "Source Action", has = "codeAction" },
       { "]]", function() Snacks.words.jump(vim.v.count1) end, has = "documentHighlight",
         desc = "Next Reference", cond = function() return Snacks.words.is_enabled() end },
       { "[[", function() Snacks.words.jump(-vim.v.count1) end, has = "documentHighlight",
       { "]]", function() Snacks.words.jump(vim.v.count1) end, has = "documentHighlight",
         desc = "Next Reference", cond = function() return Snacks.words.is_enabled() end },
       { "[[", function() Snacks.words.jump(-vim.v.count1) end, has = "documentHighlight",
@@ -52,7 +52,7 @@ function M.has(buffer, method)
     return false
   end
   method = method:find("/") and method or "textDocument/" .. method
     return false
   end
   method = method:find("/") and method or "textDocument/" .. method
-  local clients = LazyVim.lsp.get_clients({ bufnr = buffer })
+  local clients = rmz.lsp.get_clients({ bufnr = buffer })
   for _, client in ipairs(clients) do
     if client.supports_method(method) then
       return true
   for _, client in ipairs(clients) do
     if client.supports_method(method) then
       return true
@@ -69,7 +69,7 @@ function M.resolve(buffer)
   end
   local spec = vim.tbl_extend("force", {}, M.get())
   local opts = LazyVim.opts("nvim-lspconfig")
   end
   local spec = vim.tbl_extend("force", {}, M.get())
   local opts = LazyVim.opts("nvim-lspconfig")
-  local clients = LazyVim.lsp.get_clients({ bufnr = buffer })
+  local clients = rmz.lsp.get_clients({ bufnr = buffer })
   for _, client in ipairs(clients) do
     local maps = opts.servers[client.name] and opts.servers[client.name].keys or {}
     vim.list_extend(spec, maps)
   for _, client in ipairs(clients) do
     local maps = opts.servers[client.name] and opts.servers[client.name].keys or {}
     vim.list_extend(spec, maps)
index 6f69fc2ba752a832cc253067a5c9721acf9c5c68..3878e03418a1eae187bc2a97c75213c9e0b8ca32 100644 (file)
@@ -72,8 +72,8 @@ return {
         -- { "gr", function() Snacks.picker.lsp_references() end, nowait = true, desc = "References" },
         { "gI", function() Snacks.picker.lsp_implementations() end, desc = "Goto Implementation" },
         { "gy", function() Snacks.picker.lsp_type_definitions() end, desc = "Goto T[y]pe Definition" },
         -- { "gr", function() Snacks.picker.lsp_references() end, nowait = true, desc = "References" },
         { "gI", function() Snacks.picker.lsp_implementations() end, desc = "Goto Implementation" },
         { "gy", function() Snacks.picker.lsp_type_definitions() end, desc = "Goto T[y]pe Definition" },
-        { "<leader>ss", function() Snacks.picker.lsp_symbols({ filter = LazyVim.config.kind_filter }) end, desc = "LSP Symbols", has = "documentSymbol" },
-        { "<leader>sS", function() Snacks.picker.lsp_workspace_symbols({ filter = LazyVim.config.kind_filter }) end, desc = "LSP Workspace Symbols", has = "workspace/symbols" },
+        { "<leader>ss", function() Snacks.picker.lsp_symbols({ filter = rmz.lsp.kind_filter }) end, desc = "LSP Symbols", has = "documentSymbol" },
+        { "<leader>sS", function() Snacks.picker.lsp_workspace_symbols({ filter = rmz.lsp.kind_filter }) end, desc = "LSP Workspace Symbols", has = "workspace/symbols" },
       })
     end,
   },
       })
     end,
   },
index 05ab3dfe94ee69f0adc106c0c71e203a7454b4b8..261312af634b28dd235fbd356880588fe91b948f 100644 (file)
@@ -330,7 +330,7 @@ return {
     lazy = true,
     init = function()
       vim.g.navic_silence = true
     lazy = true,
     init = function()
       vim.g.navic_silence = true
-      LazyVim.lsp.on_attach(function(client, buffer)
+      rmz.lsp.on_attach(function(client, buffer)
         if client.supports_method("textDocument/documentSymbol") then
           require("nvim-navic").attach(client, buffer)
         end
         if client.supports_method("textDocument/documentSymbol") then
           require("nvim-navic").attach(client, buffer)
         end
index 9ef8cb32bd140ddf0a2d0c74d01ad0d9a346b761..7d519f1b085b6185fcc01748f512d0421d7b4222 100644 (file)
@@ -1,6 +1,7 @@
 ---@class rmz.util
 local M = {
   ui  = require("rmz.util.ui"),
 ---@class rmz.util
 local M = {
   ui  = require("rmz.util.ui"),
+  lsp = require("rmz.util.lsp"),
 }
 
 --- Deduplicates a list.
 }
 
 --- Deduplicates a list.
diff --git a/nvim/lua/rmz/util/lsp.lua b/nvim/lua/rmz/util/lsp.lua
new file mode 100644 (file)
index 0000000..7424803
--- /dev/null
@@ -0,0 +1,258 @@
+-- Absorbed from LazyVim
+-- TODO: review, understand and adapt
+---@class rmz.util.lsp
+local M = {}
+
+---@alias lsp.Client.filter {id?: number, bufnr?: number, name?: string, method?: string, filter?:fun(client: lsp.Client):boolean}
+
+M.kind_filter = {
+  default = {
+    "Class",
+    "Constructor",
+    "Enum",
+    "Field",
+    "Function",
+    "Interface",
+    "Method",
+    "Module",
+    "Namespace",
+    "Package",
+    "Property",
+    "Struct",
+    "Trait",
+  },
+  markdown = false,
+  help = false,
+  -- you can specify a different filter for each filetype
+  lua = {
+    "Class",
+    "Constructor",
+    "Enum",
+    "Field",
+    "Function",
+    "Interface",
+    "Method",
+    "Module",
+    "Namespace",
+    -- "Package", -- remove package since luals uses it for control flow structures
+    "Property",
+    "Struct",
+    "Trait",
+  },
+}
+
+---@param opts? lsp.Client.filter
+function M.get_clients(opts)
+  local ret = {} ---@type vim.lsp.Client[]
+  if vim.lsp.get_clients then
+    ret = vim.lsp.get_clients(opts)
+  else
+    ---@diagnostic disable-next-line: deprecated
+    ret = vim.lsp.get_active_clients(opts)
+    if opts and opts.method then
+      ---@param client vim.lsp.Client
+      ret = vim.tbl_filter(function(client)
+        return client.supports_method(opts.method, { bufnr = opts.bufnr })
+      end, ret)
+    end
+  end
+  return opts and opts.filter and vim.tbl_filter(opts.filter, ret) or ret
+end
+
+---@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, { bufnr = 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
+
+---@return _.lspconfig.options
+function M.get_config(server)
+  local configs = require("lspconfig.configs")
+  return rawget(configs, server)
+end
+
+---@return {default_config:lspconfig.Config}
+function M.get_raw_config(server)
+  local ok, ret = pcall(require, "lspconfig.configs." .. server)
+  if ok then
+    return ret
+  end
+  return require("lspconfig.server_configurations." .. server)
+end
+
+function M.is_enabled(server)
+  local c = M.get_config(server)
+  return c and c.enabled ~= false
+end
+
+---@param server string
+---@param cond fun( root_dir, config): boolean
+function M.disable(server, cond)
+  local util = require("lspconfig.util")
+  local def = M.get_config(server)
+  ---@diagnostic disable-next-line: undefined-field
+  def.document_config.on_new_config = util.add_hook_before(def.document_config.on_new_config, function(config, root_dir)
+    if cond(root_dir, config) then
+      config.enabled = false
+    end
+  end)
+end
+
+---@param opts? LazyFormatter| {filter?: (string|lsp.Client.filter)}
+function M.formatter(opts)
+  opts = opts or {}
+  local filter = opts.filter or {}
+  filter = type(filter) == "string" and { name = filter } or filter
+  ---@cast filter lsp.Client.filter
+  ---@type LazyFormatter
+  local ret = {
+    name = "LSP",
+    primary = true,
+    priority = 1,
+    format = function(buf)
+      M.format(LazyVim.merge({}, filter, { bufnr = buf }))
+    end,
+    sources = function(buf)
+      local clients = M.get_clients(LazyVim.merge({}, filter, { bufnr = buf }))
+      ---@param client vim.lsp.Client
+      local ret = vim.tbl_filter(function(client)
+        return client.supports_method("textDocument/formatting")
+          or client.supports_method("textDocument/rangeFormatting")
+      end, clients)
+      ---@param client vim.lsp.Client
+      return vim.tbl_map(function(client)
+        return client.name
+      end, ret)
+    end,
+  }
+  return LazyVim.merge(ret, opts) --[[@as LazyFormatter]]
+end
+
+---@alias lsp.Client.format {timeout_ms?: number, format_options?: table} | lsp.Client.filter
+
+---@param opts? lsp.Client.format
+function M.format(opts)
+  opts = vim.tbl_deep_extend(
+    "force",
+    {},
+    opts or {},
+    LazyVim.opts("nvim-lspconfig").format or {},
+    LazyVim.opts("conform.nvim").format or {}
+  )
+  local ok, conform = pcall(require, "conform")
+  -- use conform for formatting with LSP when available,
+  -- since it has better format diffing
+  if ok then
+    opts.formatters = {}
+    conform.format(opts)
+  else
+    vim.lsp.buf.format(opts)
+  end
+end
+
+M.action = setmetatable({}, {
+  __index = function(_, action)
+    return function()
+      vim.lsp.buf.code_action({
+        apply = true,
+        context = {
+          only = { action },
+          diagnostics = {},
+        },
+      })
+    end
+  end,
+})
+
+return M