Procházet zdrojové kódy

refactor!: migrate to mason.nvim (#2880)

kylo252 před 2 roky
rodič
revize
eefde00ae8

+ 7 - 0
lua/lvim/config/init.lua

@@ -99,6 +99,13 @@ local function handle_deprecated_settings()
       "Use vim.api.nvim_create_autocmd instead or check LunarVim#2592 to learn about the new syntax"
     )
   end
+
+  if lvim.lsp.automatic_servers_installation then
+    deprecation_notice(
+      "lvim.lsp.automatic_servers_installation",
+      "Use `lvim.lsp.installer.setup.automatic_installation` instead"
+    )
+  end
 end
 
 --- Override the configuration with a user provided one

+ 1 - 0
lua/lvim/core/builtins/init.lua

@@ -16,6 +16,7 @@ local builtins = {
   "lvim.core.notify",
   "lvim.core.lualine",
   "lvim.core.alpha",
+  "lvim.core.mason",
 }
 
 function M.config(config)

+ 2 - 2
lua/lvim/core/info.lua

@@ -111,9 +111,9 @@ local function make_auto_lsp_info(ft)
     return info_lines
   end
 
-  local available = lsp_utils.get_supported_servers_per_filetype(ft)
+  local supported = lsp_utils.get_supported_servers(ft)
   local skipped = vim.tbl_filter(function(name)
-    return vim.tbl_contains(available, name)
+    return vim.tbl_contains(supported, name)
   end, skipped_servers)
 
   if #skipped == 0 then

+ 41 - 0
lua/lvim/core/mason.lua

@@ -0,0 +1,41 @@
+local M = {}
+
+function M.config()
+  lvim.builtin.mason = {
+    ui = {
+      keymaps = {
+        toggle_package_expand = "<CR>",
+        install_package = "i",
+        update_package = "u",
+        check_package_version = "c",
+        update_all_packages = "U",
+        check_outdated_packages = "C",
+        uninstall_package = "X",
+        cancel_installation = "<C-c>",
+        apply_language_filter = "<C-f>",
+      },
+    },
+    log_level = vim.log.levels.INFO,
+    max_concurrent_installers = 4,
+
+    github = {
+      -- The template URL to use when downloading assets from GitHub.
+      -- The placeholders are the following (in order):
+      -- 1. The repository (e.g. "rust-lang/rust-analyzer")
+      -- 2. The release version (e.g. "v0.3.0")
+      -- 3. The asset name (e.g. "rust-analyzer-v0.3.0-x86_64-unknown-linux-gnu.tar.gz")
+      download_url_template = "https://github.com/%s/releases/download/%s/%s",
+    },
+  }
+end
+
+function M.setup()
+  local status_ok, mason = pcall(require, "mason")
+  if not status_ok then
+    return
+  end
+
+  mason.setup(lvim.builtin.mason)
+end
+
+return M

+ 1 - 1
lua/lvim/core/which-key.lua

@@ -160,7 +160,7 @@ M.config = function()
         w = { "<cmd>Telescope diagnostics<cr>", "Diagnostics" },
         f = { require("lvim.lsp.utils").format, "Format" },
         i = { "<cmd>LspInfo<cr>", "Info" },
-        I = { "<cmd>LspInstallInfo<cr>", "Installer Info" },
+        I = { "<cmd>Mason<cr>", "Mason Info" },
         j = {
           vim.diagnostic.goto_next,
           "Next Diagnostic",

+ 5 - 8
lua/lvim/lsp/config.lua

@@ -88,7 +88,6 @@ return {
   },
   on_attach_callback = nil,
   on_init_callback = nil,
-  automatic_servers_installation = true,
   automatic_configuration = {
     ---@usage list of servers that the automatic installer will skip
     skipped_servers = skipped_servers,
@@ -131,12 +130,8 @@ return {
   installer = {
     setup = {
       ensure_installed = {},
-      ui = {
-        icons = {
-          server_installed = "✓",
-          server_pending = "",
-          server_uninstalled = "✗",
-        },
+      automatic_installation = {
+        exclude = {},
       },
     },
   },
@@ -153,6 +148,8 @@ return {
     setup = {},
     config = {},
   },
-  ---@deprecated use automatic_configuration.skipped_servers instead
+  ---@deprecated use lvim.lsp.automatic_configuration.skipped_servers instead
   override = {},
+  ---@deprecated use lvim.lsp.installer.setup.automatic_installation instead
+  automatic_servers_installation = nil,
 }

+ 4 - 1
lua/lvim/lsp/init.lua

@@ -110,7 +110,10 @@ function M.setup()
   end)
 
   pcall(function()
-    require("nvim-lsp-installer").setup(lvim.lsp.installer.setup)
+    require("mason-lspconfig").setup(lvim.lsp.installer.setup)
+    local util = require "lspconfig.util"
+    -- automatic_installation is handled by lsp-manager
+    util.on_setup = nil
   end)
 
   require("lvim.lsp.null-ls").setup()

+ 50 - 17
lua/lvim/lsp/manager.lua

@@ -1,7 +1,30 @@
 local M = {}
 
 local Log = require "lvim.core.log"
+local fmt = string.format
 local lvim_lsp_utils = require "lvim.lsp.utils"
+local is_windows = vim.loop.os_uname().version:match "Windows"
+
+local function resolve_mason_config(server_name)
+  local found, mason_config = pcall(require, "mason-lspconfig.server_configurations." .. server_name)
+  if not found then
+    Log:debug(fmt("mason configuration not found for %s", server_name))
+    return {}
+  end
+  local server_mapping = require "mason-lspconfig.mappings.server"
+  local path = require "mason-core.path"
+  local pkg_name = server_mapping.lspconfig_to_package[server_name]
+  local install_dir = path.package_prefix(pkg_name)
+  local conf = mason_config(install_dir)
+  if is_windows and conf.cmd and conf.cmd[1] then
+    local exepath = vim.fn.exepath(conf.cmd[1])
+    if exepath ~= "" then
+      conf.cmd[1] = exepath
+    end
+  end
+  Log:debug(fmt("resolved mason configuration for %s, got %s", server_name, vim.inspect(mason_config)))
+  return mason_config or {}
+end
 
 ---Resolve the configuration for a server by merging with the default config
 ---@param server_name string
@@ -65,35 +88,45 @@ function M.setup(server_name, user_config)
     return
   end
 
-  local servers = require "nvim-lsp-installer.servers"
-  local server_available, server = servers.get_server(server_name)
+  local server_mapping = require "mason-lspconfig.mappings.server"
+  local registry = require "mason-registry"
 
-  if not server_available then
+  local pkg_name = server_mapping.lspconfig_to_package[server_name]
+  if not pkg_name then
     local config = resolve_config(server_name, user_config)
     launch_server(server_name, config)
     return
   end
 
-  local install_in_progress = false
+  local should_auto_install = function()
+    local installer_settings = lvim.lsp.installer.setup
+    return installer_settings.automatic_installation
+      and not vim.tbl_contains(installer_settings.automatic_installation.exclude, server_name)
+  end
 
-  if not server:is_installed() then
-    if lvim.lsp.automatic_servers_installation then
+  if not registry.is_installed(pkg_name) then
+    if should_auto_install(server_name) then
       Log:debug "Automatic server installation detected"
-      server:install()
-      install_in_progress = true
+      vim.notify_once(string.format("Installation in progoress for [%s] server", server_name), vim.log.levels.INFO)
+      local pkg = registry.get_package(pkg_name)
+      pkg:install():once("closed", function()
+        if pkg:is_installed() then
+          vim.schedule(function()
+            vim.notify_once(string.format("Installation complete for [%s] server", server_name), vim.log.levels.INFO)
+            -- mason config is only available once the server has been installed
+            local config = resolve_config(server_name, resolve_mason_config(server_name), user_config)
+            launch_server(server_name, config)
+          end)
+        end
+      end)
+      return
     else
-      Log:debug(server.name .. " is not managed by the automatic installer")
+      Log:debug(server_name .. " is not managed by the automatic installer")
     end
   end
 
-  server:on_ready(function()
-    if install_in_progress then
-      vim.notify(string.format("Installation complete for [%s] server", server.name), vim.log.levels.INFO)
-    end
-    install_in_progress = false
-    local config = resolve_config(server_name, server:get_default_options(), user_config)
-    launch_server(server_name, config)
-  end)
+  local config = resolve_config(server_name, resolve_mason_config(server_name), user_config)
+  launch_server(server_name, config)
 end
 
 return M

+ 1 - 10
lua/lvim/lsp/templates.lua

@@ -56,21 +56,12 @@ end
 ---The files are generated to a runtimepath: "$LUNARVIM_RUNTIME_DIR/site/after/ftplugin/template.lua"
 ---@param servers_names? table list of servers to be enabled. Will add all by default
 function M.generate_templates(servers_names)
-  servers_names = servers_names or {}
+  servers_names = servers_names or lvim_lsp_utils.get_supported_servers()
 
   Log:debug "Templates installation in progress"
 
   M.remove_template_files()
 
-  if vim.tbl_isempty(servers_names) then
-    local available_servers = require("nvim-lsp-installer.servers").get_available_servers()
-
-    for _, server in pairs(available_servers) do
-      table.insert(servers_names, server.name)
-      table.sort(servers_names)
-    end
-  end
-
   -- create the directory if it didn't exist
   if not utils.is_directory(lvim.lsp.templates_dir) then
     vim.fn.mkdir(ftplugin_dir, "p")

+ 13 - 16
lua/lvim/lsp/utils.lua

@@ -51,37 +51,34 @@ end
 
 ---Get supported filetypes per server
 ---@param server_name string can be any server supported by nvim-lsp-installer
----@return table supported filestypes as a list of strings
+---@return string[] supported filestypes as a list of strings
 function M.get_supported_filetypes(server_name)
-  local status_ok, lsp_installer_servers = pcall(require, "nvim-lsp-installer.servers")
+  local status_ok, config = pcall(require, ("lspconfig.server_configurations.%s"):format(server_name))
   if not status_ok then
     return {}
   end
 
-  local server_available, requested_server = lsp_installer_servers.get_server(server_name)
-  if not server_available then
-    return {}
-  end
-
-  return requested_server:get_supported_filetypes()
+  return config.default_config.filetypes or {}
 end
 
 ---Get supported servers per filetype
----@param filetype string
----@return table list of names of supported servers
-function M.get_supported_servers_per_filetype(filetype)
-  local filetype_server_map = require "nvim-lsp-installer._generated.filetype_map"
-  return filetype_server_map[filetype]
+---@param filter { filetype: string | string[] }?: (optional) Used to filter the list of server names.
+---@return string[] list of names of supported servers
+function M.get_supported_servers(filter)
+  local _, supported_servers = pcall(function()
+    return require("mason-lspconfig").get_available_servers(filter)
+  end)
+  return supported_servers or {}
 end
 
 ---Get all supported filetypes by nvim-lsp-installer
----@return table supported filestypes as a list of strings
+---@return string[] supported filestypes as a list of strings
 function M.get_all_supported_filetypes()
-  local status_ok, lsp_installer_filetypes = pcall(require, "nvim-lsp-installer._generated.filetype_map")
+  local status_ok, filetype_server_map = pcall(require, "mason-lspconfig.mappings.filetype")
   if not status_ok then
     return {}
   end
-  return vim.tbl_keys(lsp_installer_filetypes or {})
+  return vim.tbl_keys(filetype_server_map or {})
 end
 
 function M.setup_document_highlight(client, bufnr)

+ 5 - 1
lua/lvim/plugins.lua

@@ -7,8 +7,12 @@ local core_plugins = {
     "jose-elias-alvarez/null-ls.nvim",
   },
   { "antoinemadec/FixCursorHold.nvim" }, -- Needed while issue https://github.com/neovim/neovim/issues/12587 is still open
+  { "williamboman/mason-lspconfig.nvim" },
   {
-    "williamboman/nvim-lsp-installer",
+    "williamboman/mason.nvim",
+    config = function()
+      require("lvim.core.mason").setup()
+    end,
   },
   {
     "lunarvim/onedarker.nvim",

+ 6 - 3
snapshots/default.json

@@ -41,6 +41,12 @@
   "lualine.nvim": {
     "commit": "8d956c1"
   },
+  "mason-lspconfig.nvim": {
+    "commit": "e48a41e"
+  },
+  "mason.nvim": {
+    "commit": "6fa15d7"
+  },
   "nlsp-settings.nvim": {
     "commit": "6c4e1a4"
   },
@@ -56,9 +62,6 @@
   "nvim-dap": {
     "commit": "c0f43f4"
   },
-  "nvim-lsp-installer": {
-    "commit": "45571e1"
-  },
   "nvim-lspconfig": {
     "commit": "3479473"
   },

+ 27 - 32
tests/minimal_lsp.lua

@@ -16,7 +16,7 @@ local package_root = join_paths(temp_dir, "nvim", "site", "pack")
 local install_path = join_paths(package_root, "packer", "start", "packer.nvim")
 local compile_path = join_paths(install_path, "plugin", "packer_compiled.lua")
 
--- Choose whether to use the executable that's managed by lsp-installer
+-- Choose whether to use the executable that's managed by mason
 local use_lsp_installer = true
 
 local function load_plugins()
@@ -24,7 +24,8 @@ local function load_plugins()
     {
       "wbthomason/packer.nvim",
       "neovim/nvim-lspconfig",
-      { "williamboman/nvim-lsp-installer", disable = not use_lsp_installer },
+      "williamboman/mason-lspconfig.nvim",
+      "williamboman/mason.nvim",
     },
     config = {
       package_root = package_root,
@@ -44,9 +45,6 @@ _G.load_config = function()
   require("vim.lsp.log").set_format_func(vim.inspect)
   local nvim_lsp = require "lspconfig"
   local on_attach = function(_, bufnr)
-    local function buf_set_keymap(...)
-      vim.api.nvim_buf_set_keymap(bufnr, ...)
-    end
     local function buf_set_option(...)
       vim.api.nvim_buf_set_option(bufnr, ...)
     end
@@ -54,24 +52,26 @@ _G.load_config = function()
     buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc")
 
     -- Mappings.
-    local opts = { noremap = true, silent = true }
-    buf_set_keymap("n", "gD", "<Cmd>lua vim.lsp.buf.declaration()<CR>", opts)
-    buf_set_keymap("n", "gd", "<Cmd>lua vim.lsp.buf.definition()<CR>", opts)
-    buf_set_keymap("n", "K", "<Cmd>lua vim.lsp.buf.hover()<CR>", opts)
-    buf_set_keymap("n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", opts)
-    buf_set_keymap("n", "<C-k>", "<cmd>lua vim.lsp.buf.signature_help()<CR>", opts)
-    buf_set_keymap("n", "<space>wa", "<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>", opts)
-    buf_set_keymap("n", "<space>wr", "<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>", opts)
-    buf_set_keymap("n", "<space>wl", "<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>", opts)
-    buf_set_keymap("n", "<space>lD", "<cmd>lua vim.lsp.buf.type_definition()<CR>", opts)
-    buf_set_keymap("n", "<space>lr", "<cmd>lua vim.lsp.buf.rename()<CR>", opts)
-    buf_set_keymap("n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>", opts)
-    buf_set_keymap("n", "gl", "<cmd>lua vim.diagnostic.open_float(0,{scope='line'})<CR>", opts)
-    buf_set_keymap("n", "<space>lk", "<cmd>lua vim.diagnostic.goto_prev()<CR>", opts)
-    buf_set_keymap("n", "<space>lj", "<cmd>lua vim.diagnostic.goto_next()<CR>", opts)
-    buf_set_keymap("n", "<space>lq", "<cmd>lua vim.diagnostic.setloclist()<CR>", opts)
-    buf_set_keymap("n", "<space>li", "<cmd>LspInfo<CR>", opts)
-    buf_set_keymap("n", "<space>lI", "<cmd>LspInstallInfo<CR>", opts)
+    local opts = { buffer = bufnr, noremap = true, silent = true }
+    vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts)
+    vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
+    vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
+    vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts)
+    vim.keymap.set("n", "<C-k>", vim.lsp.buf.signature_help, opts)
+    vim.keymap.set("n", "<space>wa", vim.lsp.buf.add_workspace_folder, opts)
+    vim.keymap.set("n", "<space>wr", vim.lsp.buf.remove_workspace_folder, opts)
+    vim.keymap.set("n", "<space>wl", function()
+      print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
+    end, opts)
+    vim.keymap.set("n", "<space>lD", vim.lsp.buf.type_definition, opts)
+    vim.keymap.set("n", "<space>lr", vim.lsp.buf.rename, opts)
+    vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
+    vim.keymap.set("n", "gl", vim.diagnostic.open_float, opts)
+    vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
+    vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
+    vim.keymap.set("n", "<space>q", vim.diagnostic.setloclist, opts)
+    vim.keymap.set("n", "<space>li", "<cmd>LspInfo<CR>", opts)
+    vim.keymap.set("n", "<space>lI", "<cmd>MasonCR>", opts)
   end
 
   -- Add the server that troubles you here, e.g. "clangd", "pyright", "tsserver"
@@ -81,15 +81,6 @@ _G.load_config = function()
     on_attach = on_attach,
   }
 
-  if use_lsp_installer then
-    local server_available, server = require("nvim-lsp-installer.servers").get_server(name)
-    if not server_available then
-      server:install()
-    end
-    local default_opts = server:get_default_options()
-    setup_opts = vim.tbl_deep_extend("force", setup_opts, default_opts)
-  end
-
   if not name then
     print "You have not defined a server name, please edit minimal_init.lua"
   end
@@ -99,6 +90,10 @@ _G.load_config = function()
   end
 
   nvim_lsp[name].setup(setup_opts)
+  if use_lsp_installer then
+    require("mason-lspconfig").setup { automatic_installation = true }
+  end
+
   print [[You can find your log at $HOME/.cache/nvim/lsp.log. Please paste in a github issue under a details tag as described in the issue template.]]
 end
 

+ 5 - 1
tests/specs/lsp_spec.lua

@@ -53,10 +53,14 @@ a.describe("lsp workflow", function()
   a.it("should only include one server per generated template", function()
     require("lvim.lsp").setup()
 
+    local allowed_dupes = { "tailwindcss" }
     for _, file in ipairs(vim.fn.glob(lvim.lsp.templates_dir .. "/*.lua", 1, 1)) do
       local content = {}
       for entry in io.lines(file) do
-        table.insert(content, entry)
+        local server_name = entry:match [[.*setup%("(.*)"%)]]
+        if not vim.tbl_contains(allowed_dupes, server_name) then
+          table.insert(content, server_name)
+        end
       end
       local err_msg = ""
       if #content > 1 then