diff options
Diffstat (limited to 'lua/lvim/lsp')
| -rw-r--r-- | lua/lvim/lsp/config.lua | 29 | ||||
| -rw-r--r-- | lua/lvim/lsp/handlers.lua | 70 | ||||
| -rw-r--r-- | lua/lvim/lsp/init.lua | 14 | ||||
| -rw-r--r-- | lua/lvim/lsp/manager.lua | 67 | ||||
| -rw-r--r-- | lua/lvim/lsp/null-ls/code_actions.lua | 81 | ||||
| -rw-r--r-- | lua/lvim/lsp/null-ls/formatters.lua | 35 | ||||
| -rw-r--r-- | lua/lvim/lsp/null-ls/init.lua | 2 | ||||
| -rw-r--r-- | lua/lvim/lsp/null-ls/linters.lua | 30 | ||||
| -rw-r--r-- | lua/lvim/lsp/null-ls/services.lua | 14 | ||||
| -rw-r--r-- | lua/lvim/lsp/templates.lua | 3 | ||||
| -rw-r--r-- | lua/lvim/lsp/utils.lua | 11 | 
11 files changed, 235 insertions, 121 deletions
| diff --git a/lua/lvim/lsp/config.lua b/lua/lvim/lsp/config.lua index ce7ed891..1fbaf3a9 100644 --- a/lua/lvim/lsp/config.lua +++ b/lua/lvim/lsp/config.lua @@ -4,16 +4,31 @@ return {      signs = {        active = true,        values = { -        { name = "LspDiagnosticsSignError", text = "" }, -        { name = "LspDiagnosticsSignWarning", text = "" }, -        { name = "LspDiagnosticsSignHint", text = "" }, -        { name = "LspDiagnosticsSignInformation", text = "" }, +        { name = "DiagnosticSignError", text = "" }, +        { name = "DiagnosticSignWarn", text = "" }, +        { name = "DiagnosticSignHint", text = "" }, +        { name = "DiagnosticSignInfo", text = "" },        },      },      virtual_text = true,      update_in_insert = false,      underline = true,      severity_sort = true, +    float = { +      focusable = false, +      style = "minimal", +      border = "rounded", +      source = "always", +      header = "", +      prefix = "", +      format = function(d) +        local t = vim.deepcopy(d) +        if d.code then +          t.message = string.format("%s [%s]", t.message, t.code):gsub("1. ", "") +        end +        return t.message +      end, +    },    },    document_highlight = true,    code_lens_refresh = true, @@ -40,10 +55,13 @@ return {    },    null_ls = {      setup = {}, +    config = {},    },    override = {      "angularls",      "ansiblels", +    "ccls", +    "csharp_ls",      "denols",      "ember",      "emmet_ls", @@ -54,10 +72,13 @@ return {      "ltex",      "phpactor",      "pylsp", +    "quick_lint_js",      "rome",      "sorbet",      "sqlls",      "sqls", +    "solang", +    "spectral",      "stylelint_lsp",      "tailwindcss",      "tflint", diff --git a/lua/lvim/lsp/handlers.lua b/lua/lvim/lsp/handlers.lua index 27ce8589..45f73e91 100644 --- a/lua/lvim/lsp/handlers.lua +++ b/lua/lvim/lsp/handlers.lua @@ -9,42 +9,10 @@ function M.setup()      underline = lvim.lsp.diagnostics.underline,      update_in_insert = lvim.lsp.diagnostics.update_in_insert,      severity_sort = lvim.lsp.diagnostics.severity_sort, +    float = lvim.lsp.diagnostics.float,    } -  if vim.fn.has "nvim-0.5.1" > 0 then -    vim.lsp.handlers["textDocument/publishDiagnostics"] = function(_, result, ctx, _) -      local uri = result.uri -      local bufnr = vim.uri_to_bufnr(uri) -      if not bufnr then -        return -      end - -      local diagnostics = result.diagnostics -      local ok, vim_diag = pcall(require, "vim.diagnostic") -      if ok then -        -- FIX: why can't we just use vim.diagnostic.get(buf_id)? -        config.signs = true -        for i, diagnostic in ipairs(diagnostics) do -          local rng = diagnostic.range -          diagnostics[i].lnum = rng["start"].line -          diagnostics[i].end_lnum = rng["end"].line -          diagnostics[i].col = rng["start"].character -          diagnostics[i].end_col = rng["end"].character -        end -        local namespace = vim.lsp.diagnostic.get_namespace(ctx.client_id) - -        vim_diag.set(namespace, bufnr, diagnostics, config) -        if not vim.api.nvim_buf_is_loaded(bufnr) then -          return -        end -        vim_diag.show(namespace, bufnr, diagnostics, config) -      else -        vim.lsp.diagnostic.save(diagnostics, bufnr, ctx.client_id) -        if not vim.api.nvim_buf_is_loaded(bufnr) then -          return -        end -        vim.lsp.diagnostic.display(diagnostics, bufnr, ctx.client_id, config) -      end -    end +  if vim.fn.has "nvim-0.6" == 1 then +    vim.diagnostic.config(config)    else      vim.lsp.handlers["textDocument/publishDiagnostics"] = function(_, _, params, client_id, _)        local uri = params.uri @@ -60,27 +28,29 @@ function M.setup()        end        vim.lsp.diagnostic.display(diagnostics, bufnr, client_id, config)      end -  end - -  vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { -    border = lvim.lsp.popup_border, -  }) -  vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, { -    border = lvim.lsp.popup_border, -  }) -end +    vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { +      border = lvim.lsp.popup_border, +    }) -local function split_by_chunk(text, chunkSize) -  local s = {} -  for i = 1, #text, chunkSize do -    s[#s + 1] = text:sub(i, i + chunkSize - 1) +    vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, { +      border = lvim.lsp.popup_border, +    })    end -  return s  end  function M.show_line_diagnostics() -  -- TODO: replace all this with vim.diagnostic.show_position_diagnostics() +  if vim.fn.has "nvim-0.6" == 1 then +    return vim.diagnostic.open_float(0, { scope = "line" }) +  end + +  local function split_by_chunk(text, chunkSize) +    local s = {} +    for i = 1, #text, chunkSize do +      s[#s + 1] = text:sub(i, i + chunkSize - 1) +    end +    return s +  end    local diagnostics = vim.lsp.diagnostic.get_line_diagnostics()    local severity_highlight = {      "LspDiagnosticsFloatingError", diff --git a/lua/lvim/lsp/init.lua b/lua/lvim/lsp/init.lua index d00f75c6..68a64d6c 100644 --- a/lua/lvim/lsp/init.lua +++ b/lua/lvim/lsp/init.lua @@ -137,10 +137,10 @@ function M.get_common_opts()  end  local LSP_DEPRECATED_SIGN_MAP = { -  ["LspDiagnosticsSignError"] = "DiagnosticSignError", -  ["LspDiagnosticsSignWarning"] = "DiagnosticSignWarn", -  ["LspDiagnosticsSignHint"] = "DiagnosticSignHint", -  ["LspDiagnosticsSignInformation"] = "DiagnosticSignInfo", +  ["DiagnosticSignError"] = "LspDiagnosticsSignError", +  ["DiagnosticSignWarn"] = "LspDiagnosticsSignWarning", +  ["DiagnosticSignHint"] = "LspDiagnosticsSignHint", +  ["DiagnosticSignInfo"] = "LspDiagnosticsSignInformation",  }  function M.setup() @@ -151,11 +151,11 @@ function M.setup()      return    end -  local is_neovim_nightly = vim.fn.has "nvim-0.5.1" > 0 +  local is_neovim_5 = vim.fn.has "nvim-0.6" ~= 1    for _, sign in ipairs(lvim.lsp.diagnostics.signs.values) do      local lsp_sign_name = LSP_DEPRECATED_SIGN_MAP[sign.name] -    if is_neovim_nightly and lsp_sign_name then +    if is_neovim_5 and lsp_sign_name then        vim.fn.sign_define(lsp_sign_name, { texthl = lsp_sign_name, text = sign.text, numhl = lsp_sign_name })      end      vim.fn.sign_define(sign.name, { texthl = sign.name, text = sign.text, numhl = sign.name }) @@ -171,7 +171,7 @@ function M.setup()    require("lvim.lsp.null-ls").setup() -  require("lvim.utils").toggle_autoformat() +  require("lvim.core.autocmds").configure_format_on_save()  end  return M diff --git a/lua/lvim/lsp/manager.lua b/lua/lvim/lsp/manager.lua index dbb7b87f..7a35f1ff 100644 --- a/lua/lvim/lsp/manager.lua +++ b/lua/lvim/lsp/manager.lua @@ -45,48 +45,65 @@ local function buf_try_add(server_name, bufnr)    require("lspconfig")[server_name].manager.try_add_wrapper(bufnr)  end +-- check if the manager autocomd has already been configured since some servers can take a while to initialize +-- this helps guarding against a data-race condition where a server can get configured twice +-- which seems to occur only when attaching to single-files +local function client_is_configured(server_name, ft) +  ft = ft or vim.bo.filetype +  local active_autocmds = vim.split(vim.fn.execute("autocmd FileType " .. ft), "\n") +  for _, result in ipairs(active_autocmds) do +    if result:match(server_name) then +      return true +    end +  end +  return false +end +  ---Setup a language server by providing a name  ---@param server_name string name of the language server  ---@param user_config table [optional] when available it will take predence over any default configurations  function M.setup(server_name, user_config)    vim.validate { name = { server_name, "string" } } -  if lvim_lsp_utils.is_client_active(server_name) then +  if lvim_lsp_utils.is_client_active(server_name) or client_is_configured(server_name) then +    Log:debug(string.format("[%q] is already configured. Ignoring repeated setup call.", server_name))      return    end -  local servers = require "nvim-lsp-installer.servers"    local config = resolve_config(server_name, user_config) + +  local servers = require "nvim-lsp-installer.servers"    local server_available, requested_server = servers.get_server(server_name) -  if server_available then -    local install_notification = false - -    if not requested_server:is_installed() then -      if lvim.lsp.automatic_servers_installation then -        Log:debug "Automatic server installation detected" -        requested_server:install() -        install_notification = true -      else -        Log:debug(requested_server.name .. " is not managed by the automatic installer") -      end -    end +  local is_overridden = vim.tbl_contains(lvim.lsp.override, server_name) -    requested_server:on_ready(function() -      if install_notification then -        vim.notify(string.format("Installation complete for [%s] server", requested_server.name), vim.log.levels.INFO) -      end -      install_notification = false -      requested_server:setup(config) -    end) -  else -    -- since it may not be installed, don't attempt to configure the LSP unless there is a custom provider -    local has_custom_provider, _ = pcall(require, "lvim/lsp/providers/" .. server_name) -    if has_custom_provider then +  if not server_available or is_overridden then +    pcall(function()        require("lspconfig")[server_name].setup(config)        buf_try_add(server_name) +    end) +    return +  end + +  local install_notification = false + +  if not requested_server:is_installed() then +    if lvim.lsp.automatic_servers_installation then +      Log:debug "Automatic server installation detected" +      requested_server:install() +      install_notification = true +    else +      Log:debug(requested_server.name .. " is not managed by the automatic installer")      end    end + +  requested_server:on_ready(function() +    if install_notification then +      vim.notify(string.format("Installation complete for [%s] server", requested_server.name), vim.log.levels.INFO) +    end +    install_notification = false +    requested_server:setup(config) +  end)  end  return M diff --git a/lua/lvim/lsp/null-ls/code_actions.lua b/lua/lvim/lsp/null-ls/code_actions.lua new file mode 100644 index 00000000..ff59fabf --- /dev/null +++ b/lua/lvim/lsp/null-ls/code_actions.lua @@ -0,0 +1,81 @@ +local M = {} + +local null_ls = require "null-ls" +local services = require "lvim.lsp.null-ls.services" +local Log = require "lvim.core.log" + +local METHOD = null_ls.methods.CODE_ACTION + +local is_registered = function(name) +  local query = { +    name = name, +    method = METHOD, +  } +  return require("null-ls.sources").is_registered(query) +end + +function M.list_registered_providers(filetype) +  local registered_providers = services.list_registered_providers_names(filetype) +  return registered_providers[METHOD] or {} +end + +function M.list_available(filetype) +  local availables = require("null-ls.sources").get_available(filetype, METHOD) +  local actors = vim.tbl_map(function(src) +    return src.name +  end, availables) +  table.sort(actors) +  return actors +end + +function M.list_configured(actions_configs) +  local actors, errors = {}, {} + +  for _, config in ipairs(actions_configs) do +    vim.validate { +      ["config.name"] = { config.name, "string" }, +    } + +    local name = config.name:gsub("-", "_") +    local actor = null_ls.builtins.code_actions[name] + +    if not actor then +      Log:error("Not a valid code_actions: " .. config.name) +      errors[name] = {} -- Add data here when necessary +    elseif is_registered(config.name) then +      Log:trace "Skipping registering  the source more than once" +    else +      local command +      if actor._opts.command then +        command = services.find_command(actor._opts.command) +      end +      if not command and actor._opts.command ~= nil then +        Log:warn("Not found: " .. actor._opts.command) +        errors[name] = {} -- Add data here when necessary +      else +        Log:debug("Using code_actions: " .. (command or config.name)) +        table.insert( +          actors, +          actor.with { +            command = command, -- could be nil +            extra_args = config.args, +            filetypes = config.filetypes, +          } +        ) +      end +    end +  end + +  return { supported = actors, unsupported = errors } +end + +function M.setup(actions_configs) +  if vim.tbl_isempty(actions_configs) then +    return +  end + +  local actions = M.list_configured(actions_configs) +  null_ls.register { sources = actions.supported } +end + +return M diff --git a/lua/lvim/lsp/null-ls/formatters.lua b/lua/lvim/lsp/null-ls/formatters.lua index 20939039..b2e191c5 100644 --- a/lua/lvim/lsp/null-ls/formatters.lua +++ b/lua/lvim/lsp/null-ls/formatters.lua @@ -4,6 +4,14 @@ local null_ls = require "null-ls"  local services = require "lvim.lsp.null-ls.services"  local Log = require "lvim.core.log" +local is_registered = function(name) +  local query = { +    name = name, +    method = require("null-ls").methods.FORMATTING, +  } +  return require("null-ls.sources").is_registered(query) +end +  function M.list_registered_providers(filetype)    local null_ls_methods = require "null-ls.methods"    local formatter_method = null_ls_methods.internal["FORMATTING"] @@ -30,24 +38,29 @@ function M.list_configured(formatter_configs)    local formatters, errors = {}, {}    for _, fmt_config in ipairs(formatter_configs) do -    local formatter_name = fmt_config.exe:gsub("-", "_") -    local formatter = null_ls.builtins.formatting[formatter_name] +    local name = fmt_config.exe:gsub("-", "_") +    local formatter = null_ls.builtins.formatting[name]      if not formatter then        Log:error("Not a valid formatter: " .. fmt_config.exe) -      errors[fmt_config.exe] = {} -- Add data here when necessary +      errors[name] = {} -- Add data here when necessary +    elseif is_registered(fmt_config.exe) then +      Log:trace "Skipping registering  the source more than once"      else        local formatter_cmd = services.find_command(formatter._opts.command)        if not formatter_cmd then          Log:warn("Not found: " .. formatter._opts.command) -        errors[fmt_config.exe] = {} -- Add data here when necessary +        errors[name] = {} -- Add data here when necessary        else          Log:debug("Using formatter: " .. formatter_cmd) -        formatters[fmt_config.exe] = formatter.with { -          command = formatter_cmd, -          extra_args = fmt_config.args, -          filetypes = fmt_config.filetypes, -        } +        table.insert( +          formatters, +          formatter.with { +            command = formatter_cmd, +            extra_args = fmt_config.args, +            filetypes = fmt_config.filetypes, +          } +        )        end      end    end @@ -60,8 +73,8 @@ function M.setup(formatter_configs)      return    end -  local formatters_by_ft = M.list_configured(formatter_configs) -  null_ls.register { sources = formatters_by_ft.supported } +  local formatters = M.list_configured(formatter_configs) +  null_ls.register { sources = formatters.supported }  end  return M diff --git a/lua/lvim/lsp/null-ls/init.lua b/lua/lvim/lsp/null-ls/init.lua index 5e8c6b11..f5e820e8 100644 --- a/lua/lvim/lsp/null-ls/init.lua +++ b/lua/lvim/lsp/null-ls/init.lua @@ -9,7 +9,7 @@ function M:setup()      return    end -  null_ls.config() +  null_ls.config(lvim.lsp.null_ls.config)    local default_opts = require("lvim.lsp").get_common_opts()    if vim.tbl_isempty(lvim.lsp.null_ls.setup or {}) then diff --git a/lua/lvim/lsp/null-ls/linters.lua b/lua/lvim/lsp/null-ls/linters.lua index ced4bf34..6a793d26 100644 --- a/lua/lvim/lsp/null-ls/linters.lua +++ b/lua/lvim/lsp/null-ls/linters.lua @@ -4,6 +4,14 @@ local null_ls = require "null-ls"  local services = require "lvim.lsp.null-ls.services"  local Log = require "lvim.core.log" +local is_registered = function(name) +  local query = { +    name = name, +    method = require("null-ls").methods.DIAGNOSTICS, +  } +  return require("null-ls.sources").is_registered(query) +end +  function M.list_registered_providers(filetype)    local null_ls_methods = require "null-ls.methods"    local linter_method = null_ls_methods.internal["DIAGNOSTICS"] @@ -21,6 +29,7 @@ function M.list_available(filetype)        table.insert(linters, provider.name)      end    end +    table.sort(linters)    return linters  end @@ -29,24 +38,29 @@ function M.list_configured(linter_configs)    local linters, errors = {}, {}    for _, lnt_config in pairs(linter_configs) do -    local linter_name = lnt_config.exe:gsub("-", "_") -    local linter = null_ls.builtins.diagnostics[linter_name] +    local name = lnt_config.exe:gsub("-", "_") +    local linter = null_ls.builtins.diagnostics[name]      if not linter then        Log:error("Not a valid linter: " .. lnt_config.exe)        errors[lnt_config.exe] = {} -- Add data here when necessary +    elseif is_registered(lnt_config.exe) then +      Log:trace "Skipping registering the source more than once"      else        local linter_cmd = services.find_command(linter._opts.command)        if not linter_cmd then          Log:warn("Not found: " .. linter._opts.command) -        errors[lnt_config.exe] = {} -- Add data here when necessary +        errors[name] = {} -- Add data here when necessary        else          Log:debug("Using linter: " .. linter_cmd) -        linters[lnt_config.exe] = linter.with { -          command = linter_cmd, -          extra_args = lnt_config.args, -          filetypes = lnt_config.filetypes, -        } +        table.insert( +          linters, +          linter.with { +            command = linter_cmd, +            extra_args = lnt_config.args, +            filetypes = lnt_config.filetypes, +          } +        )        end      end    end diff --git a/lua/lvim/lsp/null-ls/services.lua b/lua/lvim/lsp/null-ls/services.lua index 9cb29f49..9151cc39 100644 --- a/lua/lvim/lsp/null-ls/services.lua +++ b/lua/lvim/lsp/null-ls/services.lua @@ -46,15 +46,13 @@ function M.find_command(command)  end  function M.list_registered_providers_names(filetype) -  local u = require "null-ls.utils" -  local c = require "null-ls.config" +  local s = require "null-ls.sources" +  local available_sources = s.get_available(filetype)    local registered = {} -  for method, source in pairs(c.get()._methods) do -    for name, filetypes in pairs(source) do -      if u.filetype_matches(filetypes, filetype) then -        registered[method] = registered[method] or {} -        table.insert(registered[method], name) -      end +  for _, source in ipairs(available_sources) do +    for method in pairs(source.methods) do +      registered[method] = registered[method] or {} +      table.insert(registered[method], source.name)      end    end    return registered diff --git a/lua/lvim/lsp/templates.lua b/lua/lvim/lsp/templates.lua index 33c75a6e..3478f4fb 100644 --- a/lua/lvim/lsp/templates.lua +++ b/lua/lvim/lsp/templates.lua @@ -19,8 +19,7 @@ end  ---@param server_name string name of a valid language server, e.g. pyright, gopls, tsserver, etc.  ---@param dir string the full path to the desired directory  function M.generate_ftplugin(server_name, dir) -  local has_custom_provider, _ = pcall(require, "lvim/lsp/providers/" .. server_name) -  if vim.tbl_contains(lvim.lsp.override, server_name) and not has_custom_provider then +  if vim.tbl_contains(lvim.lsp.override, server_name) then      return    end diff --git a/lua/lvim/lsp/utils.lua b/lua/lvim/lsp/utils.lua index 7cc8f54f..df3846ce 100644 --- a/lua/lvim/lsp/utils.lua +++ b/lua/lvim/lsp/utils.lua @@ -22,22 +22,23 @@ function M.get_active_clients_by_ft(filetype)  end  function M.get_client_capabilities(client_id) +  local client    if not client_id then      local buf_clients = vim.lsp.buf_get_clients() -    for _, buf_client in ipairs(buf_clients) do +    for _, buf_client in pairs(buf_clients) do        if buf_client.name ~= "null-ls" then -        client_id = buf_client.id +        client = buf_client          break        end      end +  else +    client = vim.lsp.get_client_by_id(tonumber(client_id))    end -  if not client_id then +  if not client then      error "Unable to determine client_id"      return    end -  local client = vim.lsp.get_client_by_id(tonumber(client_id)) -    local enabled_caps = {}    for capability, status in pairs(client.resolved_capabilities) do      if status == true then | 
