manager.lua 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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.command:match(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. require("lspconfig")[server_name].setup(config)
  67. buf_try_add(server_name)
  68. end)
  69. end
  70. ---Setup a language server by providing a name
  71. ---@param server_name string name of the language server
  72. ---@param user_config table? when available it will take predence over any default configurations
  73. function M.setup(server_name, user_config)
  74. vim.validate { name = { server_name, "string" } }
  75. user_config = user_config or {}
  76. if lvim_lsp_utils.is_client_active(server_name) or client_is_configured(server_name) then
  77. return
  78. end
  79. local server_mapping = require "mason-lspconfig.mappings.server"
  80. local registry = require "mason-registry"
  81. local pkg_name = server_mapping.lspconfig_to_package[server_name]
  82. if not pkg_name then
  83. local config = resolve_config(server_name, user_config)
  84. launch_server(server_name, config)
  85. return
  86. end
  87. local should_auto_install = function(name)
  88. local installer_settings = lvim.lsp.installer.setup
  89. return installer_settings.automatic_installation
  90. and not vim.tbl_contains(installer_settings.automatic_installation.exclude, name)
  91. end
  92. if not registry.is_installed(pkg_name) then
  93. if should_auto_install(server_name) then
  94. Log:debug "Automatic server installation detected"
  95. vim.notify_once(string.format("Installation in progress for [%s]", server_name), vim.log.levels.INFO)
  96. local pkg = registry.get_package(pkg_name)
  97. pkg:install():once("closed", function()
  98. if pkg:is_installed() then
  99. vim.schedule(function()
  100. vim.notify_once(string.format("Installation complete for [%s]", server_name), vim.log.levels.INFO)
  101. -- mason config is only available once the server has been installed
  102. local config = resolve_config(server_name, resolve_mason_config(server_name), user_config)
  103. launch_server(server_name, config)
  104. end)
  105. end
  106. end)
  107. return
  108. else
  109. Log:debug(server_name .. " is not managed by the automatic installer")
  110. end
  111. end
  112. local config = resolve_config(server_name, resolve_mason_config(server_name), user_config)
  113. launch_server(server_name, config)
  114. end
  115. return M