diff options
Diffstat (limited to '.config/nvim/lua/lsp')
-rw-r--r-- | .config/nvim/lua/lsp/config.lua | 27 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/handlers.lua | 107 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/init.lua | 124 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/kind.lua | 31 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/manager.lua | 92 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/null-ls/formatters.lua | 38 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/null-ls/init.lua | 54 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/null-ls/linters.lua | 32 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/null-ls/services.lua | 16 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/peek.lua | 39 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/providers/jsonls.lua | 196 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/providers/sumneko_lua.lua | 19 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/providers/vuels.lua | 28 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/providers/yamlls.lua | 30 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/templates.lua | 99 | ||||
-rw-r--r-- | .config/nvim/lua/lsp/utils.lua | 62 |
16 files changed, 772 insertions, 222 deletions
diff --git a/.config/nvim/lua/lsp/config.lua b/.config/nvim/lua/lsp/config.lua new file mode 100644 index 0000000..b748b3c --- /dev/null +++ b/.config/nvim/lua/lsp/config.lua @@ -0,0 +1,27 @@ +return { + templates_dir = join_paths(get_data_dir(), "site", "after", "ftplugin"), + diagnostics = { + signs = { + active = true, + values = { + { name = "LspDiagnosticsSignError", text = "" }, + { name = "LspDiagnosticsSignWarning", text = "" }, + { name = "LspDiagnosticsSignHint", text = "" }, + { name = "LspDiagnosticsSignInformation", text = "" }, + }, + }, + virtual_text = true, + update_in_insert = false, + underline = true, + severity_sort = true, + }, + override = {}, + document_highlight = true, + popup_border = "single", + on_attach_callback = nil, + on_init_callback = nil, + automatic_servers_installation = true, + null_ls = { + setup = {}, + }, +} diff --git a/.config/nvim/lua/lsp/handlers.lua b/.config/nvim/lua/lsp/handlers.lua index 04b8477..6801045 100644 --- a/.config/nvim/lua/lsp/handlers.lua +++ b/.config/nvim/lua/lsp/handlers.lua @@ -19,11 +19,44 @@ function M.setup() end local diagnostics = result.diagnostics - vim.lsp.diagnostic.save(diagnostics, bufnr, ctx.client_id) - if not vim.api.nvim_buf_is_loaded(bufnr) then - return + 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 + + local sign_names = { + "DiagnosticSignError", + "DiagnosticSignWarn", + "DiagnosticSignInfo", + "DiagnosticSignHint", + } + for i, sign in ipairs(options.lsp.diagnostics.signs.values) do + vim.fn.sign_define( + sign_names[i], + { texthl = sign_names[i], text = sign.text, numhl = "" } + ) + 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 - vim.lsp.diagnostic.display(diagnostics, bufnr, ctx.client_id, config) end else vim.lsp.handlers["textDocument/publishDiagnostics"] = @@ -55,26 +88,53 @@ function M.setup() ) 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 + function M.show_line_diagnostics() + -- TODO: replace all this with vim.diagnostic.show_position_diagnostics() local diagnostics = vim.lsp.diagnostic.get_line_diagnostics() - local diags = vim.deepcopy(diagnostics) + local severity_highlight = { + "LspDiagnosticsFloatingError", + "LspDiagnosticsFloatingWarning", + "LspDiagnosticsFloatingInformation", + "LspDiagnosticsFloatingHint", + } + local ok, vim_diag = pcall(require, "vim.diagnostic") + if ok then + local buf_id = vim.api.nvim_win_get_buf(0) + local win_id = vim.api.nvim_get_current_win() + local cursor_position = vim.api.nvim_win_get_cursor(win_id) + severity_highlight = { + "DiagnosticFloatingError", + "DiagnosticFloatingWarn", + "DiagnosticFloatingInfo", + "DiagnosticFloatingHint", + } + diagnostics = vim_diag.get(buf_id, { lnum = cursor_position[1] - 1 }) + end + local lines = {} + local max_width = vim.fn.winwidth(0) - 5 local height = #diagnostics local width = 0 local opts = {} local close_events = { "CursorMoved", "CursorMovedI", "BufHidden", "InsertCharPre" } - local diagnostic_severities = { - "Error", - "Warning", - "Information", - "Hint", - } if height == 0 then return end local bufnr = vim.api.nvim_create_buf(false, true) - + local diag_message + table.sort(diagnostics, function(a, b) + return a.severity < b.severity + end) for i, diagnostic in ipairs(diagnostics) do local source = diagnostic.source + diag_message = diagnostic.message:gsub("[\n\r]", " ") if source then if string.find(source, "/") then source = string.sub( @@ -82,34 +142,35 @@ function M.show_line_diagnostics() string.find(diagnostic.source, "([%w-_]+)$") ) end - diags[i].message = string.format("%s: %s", source, diagnostic.message) + diag_message = string.format("%d. %s: %s", i, source, diag_message) else - diags[i].message = string.format("%s", diagnostic.message) + diag_message = string.format("%d. %s", i, diag_message) end - if diagnostic.code then - diags[i].message = string.format("%s [%s]", diags[i].message, diagnostic.code) + diag_message = string.format("%s [%s]", diag_message, diagnostic.code) end - if diags[i].message:len() > width then - width = string.len(diags[i].message) + local msgs = split_by_chunk(diag_message, max_width) + for _, diag in ipairs(msgs) do + table.insert(lines, { message = diag, severity = diagnostic.severity }) + width = math.max(diag:len(), width) end end - + height = #lines opts = vim.lsp.util.make_floating_popup_options(width, height, opts) opts["style"] = "minimal" opts["border"] = "rounded" + opts["focusable"] = true vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe") local winnr = vim.api.nvim_open_win(bufnr, false, opts) vim.api.nvim_win_set_option(winnr, "winblend", 0) vim.api.nvim_buf_set_var(bufnr, "lsp_floating_window", winnr) - for i, diag in ipairs(diags) do - local message = diag.message:gsub("[\n\r]", " ") - vim.api.nvim_buf_set_lines(bufnr, i - 1, i - 1, 0, { message }) + for i, diag in ipairs(lines) do + vim.api.nvim_buf_set_lines(bufnr, i - 1, i - 1, 0, { diag.message }) vim.api.nvim_buf_add_highlight( bufnr, -1, - "LspDiagnosticsFloating" .. diagnostic_severities[diag.severity], + severity_highlight[diag.severity], i - 1, 0, diag.message:len() diff --git a/.config/nvim/lua/lsp/init.lua b/.config/nvim/lua/lsp/init.lua index f0f7b45..8eebe0a 100644 --- a/.config/nvim/lua/lsp/init.lua +++ b/.config/nvim/lua/lsp/init.lua @@ -1,15 +1,6 @@ local M = {} local Log = require "core.log" - -function M.config() - vim.lsp.protocol.CompletionItemKind = options.lsp.completion.item_kind - - for _, sign in ipairs(options.lsp.diagnostics.signs.values) do - vim.fn.sign_define(sign.name, { texthl = sign.name, text = sign.text, numhl = sign.name }) - end - - require("lsp.handlers").setup() -end +local utils = require "utils" local function lsp_highlight_document(client) if options.lsp.document_highlight == false then @@ -19,9 +10,6 @@ local function lsp_highlight_document(client) if client.resolved_capabilities.document_highlight then vim.api.nvim_exec( [[ - hi LspReferenceRead cterm=bold ctermbg=red guibg=#353d46 - hi LspReferenceText cterm=bold ctermbg=red guibg=#353d46 - hi LspReferenceWrite cterm=bold ctermbg=red guibg=#353d46 augroup lsp_document_highlight autocmd! * <buffer> autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight() @@ -65,35 +53,31 @@ function M.common_capabilities() "additionalTextEdits", }, } - return capabilities -end -function M.get_ls_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 - if buf_client.name ~= "null-ls" then - client_id = buf_client.id - break - end - end + local status_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp") + if status_ok then + capabilities = cmp_nvim_lsp.update_capabilities(capabilities) end - if not client_id then - error "Unable to determine client_id" - end - - client = vim.lsp.get_client_by_id(tonumber(client_id)) - local enabled_caps = {} + return capabilities +end - for k, v in pairs(client.resolved_capabilities) do - if v == true then - table.insert(enabled_caps, k) +local function select_default_formater(client) + local client_formatting = client.resolved_capabilities.document_formatting + or client.resolved_capabilities.document_range_formatting + if client.name == "null-ls" or not client_formatting then + return + end + Log:debug("Checking for formatter overriding for " .. client.name) + local client_filetypes = client.config.filetypes or {} + for _, filetype in ipairs(client_filetypes) do + if not vim.tbl_isempty(lvim.lang[filetype].formatters) then + Log:debug("Formatter overriding detected. Disabling formatting capabilities for " .. client.name) + client.resolved_capabilities.document_formatting = false + client.resolved_capabilities.document_range_formatting = false + return end end - - return enabled_caps end function M.common_on_init(client, bufnr) @@ -102,55 +86,59 @@ function M.common_on_init(client, bufnr) Log:debug "Called lsp.on_init_callback" return end - - local formatters = options.lang[vim.bo.filetype].formatters - if not vim.tbl_isempty(formatters) and formatters[1]["exe"] ~= nil and formatters[1].exe ~= "" then - client.resolved_capabilities.document_formatting = false - Log:debug( - string.format("Overriding language server [%s] with format provider [%s]", client.name, formatters[1].exe) - ) - end + select_default_formater(client) end function M.common_on_attach(client, bufnr) if options.lsp.on_attach_callback then options.lsp.on_attach_callback(client, bufnr) - Log:debug "Called lsp.on_init_callback" + Log:debug "Called lsp.on_attach_callback" end lsp_highlight_document(client) add_lsp_buffer_keybindings(bufnr) - require("lsp.null-ls").setup(vim.bo.filetype) end -function M.setup(lang) - local lsp_utils = require "lsp.utils" - local lsp = options.lang[lang].lsp - if (lsp.active ~= nil and not lsp.active) or lsp_utils.is_client_active(lsp.provider) then +local function bootstrap_nlsp(opts) + opts = opts or {} + local lsp_settings_status_ok, lsp_settings = pcall(require, "nlspsettings") + if lsp_settings_status_ok then + lsp_settings.setup(opts) + end +end + +function M.get_common_opts() + return { + on_attach = M.common_on_attach, + on_init = M.common_on_init, + capabilities = M.common_capabilities(), + } +end + +function M.setup() + Log:debug "Setting up LSP support" + + local lsp_status_ok, _ = pcall(require, "lspconfig") + if not lsp_status_ok then return end - local overrides = options.lsp.override - if type(overrides) == "table" then - if vim.tbl_contains(overrides, lang) then - return - end + for _, sign in ipairs(options.lsp.diagnostics.signs.values) do + vim.fn.sign_define( + sign.name, + { texthl = sign.name, text = sign.text, numhl = sign.name } + ) + end + require("lsp.handlers").setup() + + if not utils.is_directory(options.lsp.templates_dir) then + require("lsp.templates").generate_templates() end - if lsp.provider ~= nil and lsp.provider ~= "" then - local lspconfig = require "lspconfig" + bootstrap_nlsp { config_home = utils.join_paths(get_config_dir(), "lsp-settings") } - if not lsp.setup.on_attach then - lsp.setup.on_attach = M.common_on_attach - end - if not lsp.setup.on_init then - lsp.setup.on_init = M.common_on_init - end - if not lsp.setup.capabilities then - lsp.setup.capabilities = M.common_capabilities() - end + require("lsp.null-ls").setup() - lspconfig[lsp.provider].setup(lsp.setup) - end + require("utils").toggle_autoformat() end return M diff --git a/.config/nvim/lua/lsp/kind.lua b/.config/nvim/lua/lsp/kind.lua deleted file mode 100644 index b78fd31..0000000 --- a/.config/nvim/lua/lsp/kind.lua +++ /dev/null @@ -1,31 +0,0 @@ -local M = {} - -M.icons = { - Class = " ", - Color = " ", - Constant = "ﲀ ", - Constructor = " ", - Enum = "練", - EnumMember = " ", - Event = " ", - Field = " ", - File = "", - Folder = " ", - Function = " ", - Interface = "ﰮ ", - Keyword = " ", - Method = " ", - Module = " ", - Operator = "", - Property = " ", - Reference = " ", - Snippet = " ", - Struct = " ", - Text = " ", - TypeParameter = " ", - Unit = "塞", - Value = " ", - Variable = " ", -} - -return M diff --git a/.config/nvim/lua/lsp/manager.lua b/.config/nvim/lua/lsp/manager.lua new file mode 100644 index 0000000..49771b2 --- /dev/null +++ b/.config/nvim/lua/lsp/manager.lua @@ -0,0 +1,92 @@ +local M = {} + +local Log = require "core.log" +local lsp_utils = require "lsp.utils" + +function M.init_defaults(languages) + for _, entry in ipairs(languages) do + if not options.lang[entry] then + options.lang[entry] = { + formatters = {}, + linters = {}, + lsp = {}, + } + end + end +end + +local function is_overridden(server) + local overrides = options.lsp.override + if type(overrides) == "table" then + if vim.tbl_contains(overrides, server) then + return true + end + end +end + +function M.setup_server(server_name) + vim.validate { + name = { server_name, "string" }, + } + + if lsp_utils.is_client_active(server_name) or is_overridden(server_name) then + return + end + + local lsp_installer_servers = require "nvim-lsp-installer.servers" + local server_available, requested_server = lsp_installer_servers.get_server( + server_name + ) + if server_available then + if not requested_server:is_installed() then + Log:debug(string.format("[%s] is not installed", server_name)) + if options.lsp.automatic_servers_installation then + Log:debug(string.format("Installing [%s]", server_name)) + requested_server:install() + else + return + end + end + end + + local default_config = { + on_attach = require("lsp").common_on_attach, + on_init = require("lsp").common_on_init, + capabilities = require("lsp").common_capabilities(), + } + + local status_ok, custom_config = pcall( + require, + "lsp/providers/" .. requested_server.name + ) + if status_ok then + local new_config = vim.tbl_deep_extend("force", default_config, custom_config) + Log:debug( + "Using custom configuration for requested server: " .. requested_server.name + ) + requested_server:setup(new_config) + else + Log:debug( + "Using the default configuration for requested server: " .. requested_server.name + ) + requested_server:setup(default_config) + end +end + +function M.setup(servers) + local status_ok, _ = pcall(require, "nvim-lsp-installer") + if not status_ok then + return + end + + --- allow using a single value + if type(servers) == "string" then + servers = { servers } + end + + for _, server in ipairs(servers) do + M.setup_server(server) + end +end + +return M diff --git a/.config/nvim/lua/lsp/null-ls/formatters.lua b/.config/nvim/lua/lsp/null-ls/formatters.lua index 651d6f1..0d3505f 100644 --- a/.config/nvim/lua/lsp/null-ls/formatters.lua +++ b/.config/nvim/lua/lsp/null-ls/formatters.lua @@ -1,29 +1,14 @@ local M = {} -local formatters_by_ft = {} local null_ls = require "null-ls" local services = require "lsp.null-ls.services" -local Log = require("core.log") - -local function list_names(formatters, option) - option = option or {} - local filter = option.filter or "supported" - - return vim.tbl_keys(formatters[filter]) -end +local Log = require "core.log" function M.list_supported_names(filetype) - if not formatters_by_ft[filetype] then - return {} - end - return list_names(formatters_by_ft[filetype], { filter = "supported" }) -end - -function M.list_unsupported_names(filetype) - if not formatters_by_ft[filetype] then - return {} - end - return list_names(formatters_by_ft[filetype], { filter = "unsupported" }) + local null_ls_methods = require "null-ls.methods" + local formatter_method = null_ls_methods.internal["FORMATTING"] + local registered_providers = services.list_registered_providers_names(filetype) + return registered_providers[formatter_method] or {} end function M.list_available(filetype) @@ -45,15 +30,15 @@ function M.list_configured(formatter_configs) local formatter = null_ls.builtins.formatting[fmt_config.exe] if not formatter then - Log:error("Not a valid formatter:" .. fmt_config.exe) + Log:error("Not a valid formatter: " .. fmt_config.exe) errors[fmt_config.exe] = {} -- Add data here when necessary else local formatter_cmd = services.find_command(formatter._opts.command) if not formatter_cmd then - Log:warn("Not found:" .. formatter._opts.command) + Log:warn("Not found: " .. formatter._opts.command) errors[fmt_config.exe] = {} -- Add data here when necessary else - Log:debug("Using formatter:" .. formatter_cmd) + Log:debug("Using formatter: " .. formatter_cmd) formatters[fmt_config.exe] = formatter.with { command = formatter_cmd, extra_args = fmt_config.args, @@ -65,12 +50,13 @@ function M.list_configured(formatter_configs) return { supported = formatters, unsupported = errors } end -function M.setup(filetype, option) - if not options.lang[filetype] or (formatters_by_ft[filetype] and not option.force_reload) then +function M.setup(formatter_configs, filetype) + if vim.tbl_isempty(formatter_configs) then return end - formatters_by_ft[filetype] = M.list_configured(options.lang[filetype].formatters) + local formatters_by_ft = {} + formatters_by_ft[filetype] = M.list_configured(formatter_configs) null_ls.register { sources = formatters_by_ft[filetype].supported } end diff --git a/.config/nvim/lua/lsp/null-ls/init.lua b/.config/nvim/lua/lsp/null-ls/init.lua index d12b40a..571fb6b 100644 --- a/.config/nvim/lua/lsp/null-ls/init.lua +++ b/.config/nvim/lua/lsp/null-ls/init.lua @@ -1,44 +1,26 @@ local M = {} -function M.list_supported_provider_names(filetype) - local names = {} - - local formatters = require "lsp.null-ls.formatters" - local linters = require "lsp.null-ls.linters" - - vim.list_extend(names, formatters.list_supported_names(filetype)) - vim.list_extend(names, linters.list_supported_names(filetype)) - - return names -end - -function M.list_unsupported_provider_names(filetype) - local names = {} - - local formatters = require "lsp.null-ls.formatters" - local linters = require "lsp.null-ls.linters" - - vim.list_extend(names, formatters.list_unsupported_names(filetype)) - vim.list_extend(names, linters.list_unsupported_names(filetype)) - - return names -end - --- TODO: for linters and formatters with spaces and '-' replace with '_' -function M.setup(filetype, option) - option = option or {} - - local ok, _ = pcall(require, "null-ls") - if not ok then - require("core.log"):error "Missing null-ls dependency" +local Log = require "core.log" +local formatters = require "lsp.null-ls.formatters" +local linters = require "lsp.null-ls.linters" + +function M:setup() + local status_ok, null_ls = pcall(require, "null-ls") + if not status_ok then + Log:error "Missing null-ls dependency" return end - local formatters = require "lsp.null-ls.formatters" - local linters = require "lsp.null-ls.linters" - - formatters.setup(filetype, option) - linters.setup(filetype, option) + null_ls.config() + require("lspconfig")["null-ls"].setup(options.lsp.null_ls.setup) + for _, filetype in pairs(options.lang) do + if filetype.formatters then + formatters.setup(filetype.formatters, filetype) + end + if filetype.linters then + linters.setup(filetype.linters, filetype) + end + end end return M diff --git a/.config/nvim/lua/lsp/null-ls/linters.lua b/.config/nvim/lua/lsp/null-ls/linters.lua index 0f6cbc1..e9e92e9 100644 --- a/.config/nvim/lua/lsp/null-ls/linters.lua +++ b/.config/nvim/lua/lsp/null-ls/linters.lua @@ -1,29 +1,14 @@ local M = {} -local linters_by_ft = {} local null_ls = require "null-ls" local services = require "lsp.null-ls.services" -local Log = require("core.log") - -local function list_names(linters, option) - option = option or {} - local filter = option.filter or "supported" - - return vim.tbl_keys(linters[filter]) -end +local Log = require "core.log" function M.list_supported_names(filetype) - if not linters_by_ft[filetype] then - return {} - end - return list_names(linters_by_ft[filetype], { filter = "supported" }) -end - -function M.list_unsupported_names(filetype) - if not linters_by_ft[filetype] then - return {} - end - return list_names(linters_by_ft[filetype], { filter = "unsupported" }) + local null_ls_methods = require "null-ls.methods" + local linter_method = null_ls_methods.internal["DIAGNOSTICS"] + local registered_providers = services.list_registered_providers_names(filetype) + return registered_providers[linter_method] or {} end function M.list_available(filetype) @@ -65,12 +50,13 @@ function M.list_configured(linter_configs) return { supported = linters, unsupported = errors } end -function M.setup(filetype, option) - if not options.lang[filetype] or (linters_by_ft[filetype] and not option.force_reload) then +function M.setup(linter_configs, filetype) + if vim.tbl_isempty(linter_configs) then return end - linters_by_ft[filetype] = M.list_configured(options.lang[filetype].linters) + local linters_by_ft = {} + linters_by_ft[filetype] = M.list_configured(linter_configs) null_ls.register { sources = linters_by_ft[filetype].supported } end diff --git a/.config/nvim/lua/lsp/null-ls/services.lua b/.config/nvim/lua/lsp/null-ls/services.lua index a1e3a06..c62fc70 100644 --- a/.config/nvim/lua/lsp/null-ls/services.lua +++ b/.config/nvim/lua/lsp/null-ls/services.lua @@ -28,6 +28,7 @@ local local_providers = { prettier_d_slim = { find = from_node_modules }, eslint_d = { find = from_node_modules }, eslint = { find = from_node_modules }, + stylelint = { find = from_node_modules }, } function M.find_command(command) @@ -44,4 +45,19 @@ function M.find_command(command) return nil end +function M.list_registered_providers_names(filetype) + local u = require "null-ls.utils" + local c = require "null-ls.config" + 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 + end + end + return registered +end + return M diff --git a/.config/nvim/lua/lsp/peek.lua b/.config/nvim/lua/lsp/peek.lua index dbc6741..151c967 100644 --- a/.config/nvim/lua/lsp/peek.lua +++ b/.config/nvim/lua/lsp/peek.lua @@ -12,7 +12,8 @@ local function create_floating_file(location, opts) -- Set some defaults opts = opts or {} - local close_events = opts.close_events or { "CursorMoved", "CursorMovedI", "BufHidden", "InsertCharPre" } + local close_events = opts.close_events + or { "CursorMoved", "CursorMovedI", "BufHidden", "InsertCharPre" } -- location may be LocationLink or Location local uri = location.targetUri or location.uri @@ -29,7 +30,10 @@ local function create_floating_file(location, opts) local contents = vim.api.nvim_buf_get_lines( bufnr, range.start.line, - math.min(range["end"].line + 1 + (opts.context or 10), range.start.line + (opts.max_height or 15)), -- Don't let the window be more that 15 lines long(height) + math.min( + range["end"].line + 1 + (opts.context or 10), + range.start.line + (opts.max_height or 15) + ), -- Don't let the window be more that 15 lines long(height) false ) local width, height = vim.lsp.util._make_floating_popup_size(contents, opts) @@ -47,16 +51,17 @@ local function create_floating_file(location, opts) -- Set some autocmds to close the window vim.api.nvim_command( - "autocmd QuitPre <buffer> ++nested ++once lua pcall(vim.api.nvim_win_close, " .. winnr .. ", true)" + "autocmd QuitPre <buffer> ++nested ++once lua pcall(vim.api.nvim_win_close, " + .. winnr + .. ", true)" ) vim.lsp.util.close_preview_autocmd(close_events, winnr) return bufnr, winnr end -local function preview_location_callback(_, method, result) +local function preview_location_callback(result) if result == nil or vim.tbl_isempty(result) then - print("peek: No location found: " .. method) return nil end @@ -74,6 +79,14 @@ local function preview_location_callback(_, method, result) end end +local function preview_location_callback_old_signature(_, _, result) + return preview_location_callback(result) +end + +local function preview_location_callback_new_signature(_, result) + return preview_location_callback(result) +end + function M.open_file() -- Get the file currently open in the floating window local filepath = vim.fn.expand "%:." @@ -129,10 +142,22 @@ function M.Peek(what) else -- Make a new request and then create the new window in the callback local params = vim.lsp.util.make_position_params() - local success, _ = pcall(vim.lsp.buf_request, 0, "textDocument/" .. what, params, preview_location_callback) + local preview_callback = preview_location_callback_old_signature + if vim.fn.has "nvim-0.5.1" > 0 then + preview_callback = preview_location_callback_new_signature + end + local success, _ = pcall( + vim.lsp.buf_request, + 0, + "textDocument/" .. what, + params, + preview_callback + ) if not success then print( - 'peek: Error calling LSP method "textDocument/' .. what .. '". The current language lsp might not support it.' + 'peek: Error calling LSP method "textDocument/' + .. what + .. '". The current language lsp might not support it.' ) end end diff --git a/.config/nvim/lua/lsp/providers/jsonls.lua b/.config/nvim/lua/lsp/providers/jsonls.lua new file mode 100644 index 0000000..70ad7e1 --- /dev/null +++ b/.config/nvim/lua/lsp/providers/jsonls.lua @@ -0,0 +1,196 @@ + +local default_schemas = nil +local status_ok, jsonls_settings = pcall(require, "nlspsettings.jsonls") +if status_ok then + default_schemas = jsonls_settings.get_default_schemas() +end + +local schemas = { + { + description = "TypeScript compiler configuration file", + fileMatch = { + "tsconfig.json", + "tsconfig.*.json", + }, + url = "https://json.schemastore.org/tsconfig.json", + }, + { + description = "Lerna config", + fileMatch = { "lerna.json" }, + url = "https://json.schemastore.org/lerna.json", + }, + { + description = "Babel configuration", + fileMatch = { + ".babelrc.json", + ".babelrc", + "babel.config.json", + }, + url = "https://json.schemastore.org/babelrc.json", + }, + { + description = "ESLint config", + fileMatch = { + ".eslintrc.json", + ".eslintrc", + }, + url = "https://json.schemastore.org/eslintrc.json", + }, + { + description = "Bucklescript config", + fileMatch = { "bsconfig.json" }, + url = "https://raw.githubusercontent.com/rescript-lang/rescript-compiler/8.2.0/docs/docson/build-schema.json", + }, + { + description = "Prettier config", + fileMatch = { + ".prettierrc", + ".prettierrc.json", + "prettier.config.json", + }, + url = "https://json.schemastore.org/prettierrc", + }, + { + description = "Vercel Now config", + fileMatch = { "now.json" }, + url = "https://json.schemastore.org/now", + }, + { + description = "Stylelint config", + fileMatch = { + ".stylelintrc", + ".stylelintrc.json", + "stylelint.config.json", + }, + url = "https://json.schemastore.org/stylelintrc", + }, + { + description = "A JSON schema for the ASP.NET LaunchSettings.json files", + fileMatch = { "launchsettings.json" }, + url = "https://json.schemastore.org/launchsettings.json", + }, + { + description = "Schema for CMake Presets", + fileMatch = { + "CMakePresets.json", + "CMakeUserPresets.json", + }, + url = "https://raw.githubusercontent.com/Kitware/CMake/master/Help/manual/presets/schema.json", + }, + { + description = "Configuration file as an alternative for configuring your repository in the settings page.", + fileMatch = { + ".codeclimate.json", + }, + url = "https://json.schemastore.org/codeclimate.json", + }, + { + description = "LLVM compilation database", + fileMatch = { + "compile_commands.json", + }, + url = "https://json.schemastore.org/compile-commands.json", + }, + { + description = "Config file for Command Task Runner", + fileMatch = { + "commands.json", + }, + url = "https://json.schemastore.org/commands.json", + }, + { + description = "AWS CloudFormation provides a common language for you to describe and provision all the infrastructure resources in your cloud environment.", + fileMatch = { + "*.cf.json", + "cloudformation.json", + }, + url = "https://raw.githubusercontent.com/awslabs/goformation/v5.2.9/schema/cloudformation.schema.json", + }, + { + description = "The AWS Serverless Application Model (AWS SAM, previously known as Project Flourish) extends AWS CloudFormation to provide a simplified way of defining the Amazon API Gateway APIs, AWS Lambda functions, and Amazon DynamoDB tables needed by your serverless application.", + fileMatch = { + "serverless.template", + "*.sam.json", + "sam.json", + }, + url = "https://raw.githubusercontent.com/awslabs/goformation/v5.2.9/schema/sam.schema.json", + }, + { + description = "Json schema for properties json file for a GitHub Workflow template", + fileMatch = { + ".github/workflow-templates/**.properties.json", + }, + url = "https://json.schemastore.org/github-workflow-template-properties.json", + }, + { + description = "golangci-lint configuration file", + fileMatch = { + ".golangci.toml", + ".golangci.json", + }, + url = "https://json.schemastore.org/golangci-lint.json", + }, + { + description = "JSON schema for the JSON Feed format", + fileMatch = { + "feed.json", + }, + url = "https://json.schemastore.org/feed.json", + versions = { + ["1"] = "https://json.schemastore.org/feed-1.json", + ["1.1"] = "https://json.schemastore.org/feed.json", + }, + }, + { + description = "Packer template JSON configuration", + fileMatch = { + "packer.json", + }, + url = "https://json.schemastore.org/packer.json", + }, + { + description = "NPM configuration file", + fileMatch = { + "package.json", + }, + url = "https://json.schemastore.org/package.json", + }, + { + description = "JSON schema for Visual Studio component configuration files", + fileMatch = { + "*.vsconfig", + }, + url = "https://json.schemastore.org/vsconfig.json", + }, + { + description = "Resume json", + fileMatch = { "resume.json" }, + url = "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json", + }, +} + +local function extend(tab1, tab2) + for _, value in ipairs(tab2) do + table.insert(tab1, value) + end + return tab1 +end + +local extended_schemas = extend(schemas, default_schemas) + +local opts = { + settings = { + json = { + schemas = extended_schemas, + }, + }, + commands = { + Format = { + function() + vim.lsp.buf.range_formatting({}, { 0, 0 }, { vim.fn.line "$", 0 }) + end, + }, + }, +} + +return opts diff --git a/.config/nvim/lua/lsp/providers/sumneko_lua.lua b/.config/nvim/lua/lsp/providers/sumneko_lua.lua new file mode 100644 index 0000000..4fee1fd --- /dev/null +++ b/.config/nvim/lua/lsp/providers/sumneko_lua.lua @@ -0,0 +1,19 @@ +local opts = { + settings = { + Lua = { + diagnostics = { + globals = { "vim", "lvim" }, + }, + workspace = { + library = { + [require("utils").join_paths(get_runtime_dir(), "lvim", "lua")] = true, + [vim.fn.expand "$VIMRUNTIME/lua"] = true, + [vim.fn.expand "$VIMRUNTIME/lua/vim/lsp"] = true, + }, + maxPreload = 100000, + preloadFileSize = 10000, + }, + }, + }, +} +return opts diff --git a/.config/nvim/lua/lsp/providers/vuels.lua b/.config/nvim/lua/lsp/providers/vuels.lua new file mode 100644 index 0000000..0d93c61 --- /dev/null +++ b/.config/nvim/lua/lsp/providers/vuels.lua @@ -0,0 +1,28 @@ +local opts = { + setup = { + root_dir = function(fname) + local util = require "lspconfig/util" + return util.root_pattern "package.json"(fname) + or util.root_pattern "vue.config.js"(fname) + or vim.fn.getcwd() + end, + init_options = { + config = { + vetur = { + completion = { + autoImport = true, + tagCasing = "kebab", + useScaffoldSnippets = true, + }, + useWorkspaceDependencies = true, + validation = { + script = true, + style = true, + template = true, + }, + }, + }, + }, + }, +} +return opts diff --git a/.config/nvim/lua/lsp/providers/yamlls.lua b/.config/nvim/lua/lsp/providers/yamlls.lua new file mode 100644 index 0000000..156a35b --- /dev/null +++ b/.config/nvim/lua/lsp/providers/yamlls.lua @@ -0,0 +1,30 @@ +local opts = { + settings = { + yaml = { + hover = true, + completion = true, + validate = true, + schemaStore = { + enable = true, + url = "https://www.schemastore.org/api/json/catalog.json", + }, + schemas = { + kubernetes = { + "daemon.{yml,yaml}", + "manager.{yml,yaml}", + "restapi.{yml,yaml}", + "role.{yml,yaml}", + "role_binding.{yml,yaml}", + "*onfigma*.{yml,yaml}", + "*ngres*.{yml,yaml}", + "*ecre*.{yml,yaml}", + "*eployment*.{yml,yaml}", + "*ervic*.{yml,yaml}", + "kubectl-edit*.yaml", + }, + }, + }, + }, +} + +return opts diff --git a/.config/nvim/lua/lsp/templates.lua b/.config/nvim/lua/lsp/templates.lua new file mode 100644 index 0000000..004187e --- /dev/null +++ b/.config/nvim/lua/lsp/templates.lua @@ -0,0 +1,99 @@ +local M = {} + +local Log = require "core.log" +local utils = require "utils" +local get_supported_filetypes = require("lsp.utils").get_supported_filetypes + +local ftplugin_dir = options.lsp.templates_dir + +local join_paths = _G.join_paths + +function M.remove_template_files() + -- remove any outdated files + for _, file in ipairs(vim.fn.glob(ftplugin_dir .. "/*.lua", 1, 1)) do + vim.fn.delete(file) + end +end + +---Checks if a server is ignored by default because of a conflict +---Only TSServer is enabled by default for the javascript-family +---@param server_name string +function M.is_ignored(server_name, filetypes) + --TODO: this is easy to be made configurable once stable + filetypes = filetypes or get_supported_filetypes(server_name) + + if vim.tbl_contains(filetypes, "javascript") then + if server_name == "tsserver" or server_name == "tailwindcss" then + return false + else + return true + end + end + + local blacklist = { + "jedi_language_server", + "pylsp", + "sqlls", + "sqls", + "angularls", + "ansiblels", + } + return vim.tbl_contains(blacklist, server_name) +end + +---Generates an ftplugin file based on the server_name in the selected directory +---@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) + -- we need to go through lspconfig to get the corresponding filetypes currently + local filetypes = get_supported_filetypes(server_name) or {} + if not filetypes then + return + end + + if M.is_ignored(server_name, filetypes) then + return + end + + -- print("got associated filetypes: " .. vim.inspect(filetypes)) + + for _, filetype in ipairs(filetypes) do + local filename = join_paths(dir, filetype .. ".lua") + local setup_cmd = string.format([[require("lsp.manager").setup(%q)]], server_name) + -- print("using setup_cmd: " .. setup_cmd) + -- overwrite the file completely + utils.write_file(filename, setup_cmd .. "\n", "a") + end +end + +---Generates ftplugin files based on a list of server_names +---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 {} + + 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) + end + end + + -- create the directory if it didn't exist + if not utils.is_directory(options.lsp.templates_dir) then + vim.fn.mkdir(ftplugin_dir, "p") + end + + for _, server in ipairs(servers_names) do + M.generate_ftplugin(server, ftplugin_dir) + end + Log:debug "Templates installation is complete" +end + +return M diff --git a/.config/nvim/lua/lsp/utils.lua b/.config/nvim/lua/lsp/utils.lua index e024a0c..e0046db 100644 --- a/.config/nvim/lua/lsp/utils.lua +++ b/.config/nvim/lua/lsp/utils.lua @@ -10,19 +10,65 @@ function M.is_client_active(name) return false end --- FIXME: this should return a list instead -function M.get_active_client_by_ft(filetype) - if not options.lang[filetype] or not options.lang[filetype].lsp then - return nil - end +function M.disable_formatting_capability(client) + -- FIXME: figure out a reasonable way to do this + client.resolved_capabilities.document_formatting = false + require("core.log"):debug( + string.format( + "Turning off formatting capability for language server [%s] ", + client.name + ) + ) +end +function M.get_active_client_by_ft(filetype) + local matches = {} local clients = vim.lsp.get_active_clients() for _, client in pairs(clients) do - if client.name == options.lang[filetype].lsp.provider then - return client + local supported_filetypes = client.config.filetypes or {} + if client.name ~= "null-ls" and vim.tbl_contains(supported_filetypes, filetype) then + table.insert(matches, client) + end + end + return matches +end + +function M.get_ls_capabilities(client_id) + if not client_id then + local buf_clients = vim.lsp.buf_get_clients() + for _, buf_client in ipairs(buf_clients) do + if buf_client.name ~= "null-ls" then + client_id = buf_client.id + break + end + end + end + if not client_id 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 + table.insert(enabled_caps, capability) + end + end + + return enabled_caps +end + +function M.get_supported_filetypes(server_name) + -- print("got filetypes query request for: " .. server_name) + local configs = require "lspconfig/configs" + pcall(require, ("lspconfig/" .. server_name)) + for _, config in pairs(configs) do + if config.name == server_name then + return config.document_config.default_config.filetypes or {} end end - return nil end return M |