manager.lua 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. local M = {}
  2. local Log = require "lvim.core.log"
  3. local fmt = string.format
  4. local lvim_lsp_utils = require "lvim.lsp.utils"
  5. local is_windows = vim.loop.os_uname().version:match "Windows"
  6. local function resolve_mason_config(server_name)
  7. local found, mason_config = pcall(require, "mason-lspconfig.server_configurations." .. server_name)
  8. if not found then
  9. Log:debug(fmt("mason configuration not found for %s", server_name))
  10. return {}
  11. end
  12. local server_mapping = require "mason-lspconfig.mappings.server"
  13. local path = require "mason-core.path"
  14. local pkg_name = server_mapping.lspconfig_to_package[server_name]
  15. local install_dir = path.package_prefix(pkg_name)
  16. local conf = mason_config(install_dir)
  17. if is_windows and conf.cmd and conf.cmd[1] then
  18. local exepath = vim.fn.exepath(conf.cmd[1])
  19. if exepath ~= "" then
  20. conf.cmd[1] = exepath
  21. end
  22. end
  23. Log:debug(fmt("resolved mason configuration for %s, got %s", server_name, vim.inspect(conf)))
  24. return conf or {}
  25. end
  26. ---Resolve the configuration for a server by merging with the default config
  27. ---@param server_name string
  28. ---@vararg any config table [optional]
  29. ---@return table
  30. local function resolve_config(server_name, ...)
  31. local defaults = {
  32. on_attach = require("lvim.lsp").common_on_attach,
  33. on_init = require("lvim.lsp").common_on_init,
  34. on_exit = require("lvim.lsp").common_on_exit,
  35. capabilities = require("lvim.lsp").common_capabilities(),
  36. }
  37. local has_custom_provider, custom_config = pcall(require, "lvim/lsp/providers/" .. server_name)
  38. if has_custom_provider then
  39. Log:debug("Using custom configuration for requested server: " .. server_name)
  40. defaults = vim.tbl_deep_extend("force", defaults, custom_config)
  41. end
  42. defaults = vim.tbl_deep_extend("force", defaults, ...)
  43. return defaults
  44. end
  45. -- manually start the server and don't wait for the usual filetype trigger from lspconfig
  46. local function buf_try_add(server_name, bufnr)
  47. bufnr = bufnr or vim.api.nvim_get_current_buf()
  48. require("lspconfig")[server_name].manager:try_add_wrapper(bufnr)
  49. end
  50. -- check if the manager autocomd has already been configured since some servers can take a while to initialize
  51. -- this helps guarding against a data-race condition where a server can get configured twice
  52. -- which seems to occur only when attaching to single-files
  53. local function client_is_configured(server_name, ft)
  54. ft = ft or vim.bo.filetype
  55. local active_autocmds = vim.api.nvim_get_autocmds { event = "FileType", pattern = ft }
  56. for _, result in ipairs(active_autocmds) do
  57. if result.desc ~= nil and result.desc:match("server " .. server_name .. " ") then
  58. Log:debug(string.format("[%q] is already configured", server_name))
  59. return true
  60. end
  61. end
  62. return false
  63. end
  64. local function launch_server(server_name, config)
  65. pcall(function()
  66. local command = config.cmd
  67. or (function()
  68. local default_config = require("lspconfig.server_configurations." .. server_name).default_config
  69. return default_config.cmd
  70. end)()
  71. -- some servers have dynamic commands defined with on_new_config
  72. if type(command) == "table" and type(command[1]) == "string" and vim.fn.executable(command[1]) ~= 1 then
  73. Log:debug(string.format("[%q] is either not installed, missing from PATH, or not executable.", server_name))
  74. return
  75. end
  76. require("lspconfig")[server_name].setup(config)
  77. buf_try_add(server_name)
  78. end)
  79. end
  80. ---Setup a language server by providing a name
  81. ---@param server_name string name of the language server
  82. ---@param user_config table? when available it will take predence over any default configurations
  83. function M.setup(server_name, user_config)
  84. vim.validate { name = { server_name, "string" } }
  85. user_config = user_config or {}
  86. if lvim_lsp_utils.is_client_active(server_name) or client_is_configured(server_name) then
  87. return
  88. end
  89. local server_mapping = require "mason-lspconfig.mappings.server"
  90. local registry = require "mason-registry"
  91. local pkg_name = server_mapping.lspconfig_to_package[server_name]
  92. if not pkg_name then
  93. local config = resolve_config(server_name, user_config)
  94. launch_server(server_name, config)
  95. return
  96. end
  97. local should_auto_install = function(name)
  98. local installer_settings = lvim.lsp.installer.setup
  99. return installer_settings.automatic_installation
  100. and not vim.tbl_contains(installer_settings.automatic_installation.exclude, name)
  101. end
  102. if not registry.is_installed(pkg_name) then
  103. if should_auto_install(server_name) then
  104. Log:debug "Automatic server installation detected"
  105. vim.notify_once(string.format("Installation in progress for [%s]", server_name), vim.log.levels.INFO)
  106. local pkg = registry.get_package(pkg_name)
  107. pkg:install():once("closed", function()
  108. if pkg:is_installed() then
  109. vim.schedule(function()
  110. vim.notify_once(string.format("Installation complete for [%s]", server_name), vim.log.levels.INFO)
  111. -- mason config is only available once the server has been installed
  112. local config = resolve_config(server_name, resolve_mason_config(server_name), user_config)
  113. launch_server(server_name, config)
  114. end)
  115. end
  116. end)
  117. else
  118. Log:debug(server_name .. " is not managed by the automatic installer")
  119. end
  120. end
  121. local config = resolve_config(server_name, resolve_mason_config(server_name), user_config)
  122. launch_server(server_name, config)
  123. end
  124. return M