local server_configs = function() return { ansiblels = {}, powershell_es = {}, gopls = {}, clangd = {}, zls = {}, -- hls = {}, tsserver = {}, -- perlls = {}, cssls = {}, html = {}, lemminx = { --{{{ settings = { xml = { format = { enabled = true, splitAttributes = true, formatComments = true, joinCDATALines = false, joinCommentLines = false, joinContentLines = false, spaceBeforeEmptyCloseTag = true, }, }, }, }, -- }}} rnix = {}, omnisharp = { -- {{{ handlers = { ['textDocument/definition'] = require('omnisharp_extended').handler, }, cmd = { 'omnisharp', 'RoslynExtensionsOptions:EnableDecompilationSupport=true' }, enable_editorconfig_support = true, enable_ms_build_load_projects_on_demand = false, enable_roslyn_analyzers = true, organize_imports_on_format = true, enable_import_completion = true, sdk_include_prereleases = true, analyze_open_documents_only = true, }, -- }}} lua_ls = { -- {{{ settings = { Lua = { workleader = { checkThirdParty = false, }, completion = { callSnippet = 'Replace', }, }, }, }, -- }}} phpactor = { -- {{{ cmd = { vim.env.HOME .. '/devel/phpactor/bin/phpactor', 'language-server' }, root_dir = function(startpath) local u = require('lspconfig.util') return u.search_ancestors(startpath, function(path) return not string.find(path, '/vendor/') and ( u.path.exists(u.path.join(path, 'composer.json')) or u.path.exists(u.path.join(path, 'sharedLibs')) or u.path.exists(u.path.join(path, '.git')) ) end) end, }, -- }}} yamlls = { -- {{{ settings = { yaml = { format = { enable = true }, validate = { enable = true }, completion = { enable = true }, hover = { enable = true }, keyOrdering = false, schemas = vim.tbl_extend('force', require('schemastore').yaml.schemas(), { kubernetes = { '**/k8s/**/!(kustomization).yaml', '**/k8s/**/!(kustomization).yml', '**/kubernetes/!(kustomization).yaml', '**/kubernetes/!(kustomization).yml', '**/kustomize/**/!(kustomization).yaml', '**/kustomize/**/!(kustomization).yml', }, }), }, }, }, -- }}} jsonls = { -- {{{ settings = { validate = { enable = true }, json = { schemas = require('schemastore').json.schemas { replace = { ['openapi.json'] = { description = 'A JSON schema for Open API documentation files', fileMatch = { 'openapi.json', 'openapi.yml', 'openapi.yaml', 'openapi/*.json' }, name = 'openapi.json', url = 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/schemas/v3.0/schema.json', versions = { ['3.1'] = 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/schemas/v3.1/schema.json', ['3.0'] = 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/schemas/v3.0/schema.json', }, }, }, }, }, }, }, -- }}} } end local function formatting_filter(c) local bl = { 'phpactor', } for _, name in ipairs(bl) do if c.name == name then return false; end end return true end local function navic_attach(client, bufnr) require('nvim-navic').attach(client, bufnr) vim.wo.winbar = "%{%v:lua.require'nvim-navic'.get_location()%}" vim.api.nvim_create_autocmd('LspDetach', { pattern = vim.api.nvim_buf_get_name(bufnr), once = true, callback = function() vim.cmd.setlocal('winbar<') end, }) end local function update_attached(bufnr) local attached = ''; for _, client in pairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do attached = attached .. ',' .. client.name end vim.b[bufnr].attached_lsps = attached end local function on_attach(args) -- {{{ local bufnr = args.buf local client = vim.lsp.get_client_by_id(args.data.client_id) if (client.server_capabilities.documentSymbolProvider) then navic_attach(client, bufnr) end -- Mappings. local function mkOpts(desc) return { noremap = true, silent = true, buffer = args.buf, desc = desc } end -- null-ls has mostly no hover and therefore trashes manpages for shell if client.supports_method('textDocument/hover') and client.name ~= 'null-ls' then vim.keymap.set('n', 'K', vim.lsp.buf.hover, mkOpts('LSP hover')) end if client.server_capabilities.signatureHelpProvider then require('lsp-overloads').setup(client, { -- UI options are mostly the same as those passed to vim.lsp.util.open_floating_preview ui = { border = 'none', -- The border to use for the signature popup window. Accepts same border values as |nvim_open_win()|. height = nil, -- Height of the signature popup window (nil allows dynamic sizing based on content of the help) width = nil, -- Width of the signature popup window (nil allows dynamic sizing based on content of the help) wrap = true, -- Wrap long lines wrap_at = nil, -- Character to wrap at for computing height when wrap enabled max_width = nil, -- Maximum signature popup width max_height = nil, -- Maximum signature popup height -- Events that will close the signature popup window: use {"CursorMoved", "CursorMovedI", "InsertCharPre"} to hide the window when typing close_events = { 'CursorMoved', 'BufHidden', 'InsertLeave' }, focusable = true, -- Make the popup float focusable focus = false, -- If focusable is also true, and this is set to true, navigating through overloads will focus into the popup window (probably not what you want) offset_x = 0, -- Horizontal offset of the floating window relative to the cursor position offset_y = 0, -- Vertical offset of the floating window relative to the cursor position floating_window_above_cur_line = false, -- Attempt to float the popup above the cursor position -- (note, if the height of the float would be greater than the space left above the cursor, it will default -- to placing the float below the cursor. The max_height option allows for finer tuning of this) }, keymaps = { next_signature = '', previous_signature = '', next_parameter = '', previous_parameter = '', }, }) vim.keymap.set('n', '', 'LspOverloadsSignature', mkOpts('LSP signature help')) vim.keymap.set('i', '', 'LspOverloadsSignature', mkOpts('LSP signature help')) end if client.server_capabilities.codeLensProvider then vim.api.nvim_create_autocmd( { 'BufEnter', 'CursorHold', 'InsertLeave' }, { callback = vim.lsp.codelens.refresh, buffer = bufnr, }) end vim.keymap.set('n', 'cl', vim.lsp.codelens.run, mkOpts('LSP Run code lens')) vim.keymap.set('n', 'ci', vim.lsp.buf.implementation, mkOpts('LSP implementation')) vim.keymap.set('n', 'cd', vim.lsp.buf.type_definition, mkOpts('LSP type definition')) vim.keymap.set('n', 'cD', vim.lsp.buf.declaration, mkOpts('LSP declaration')) vim.keymap.set('n', 'cR', vim.lsp.buf.rename, mkOpts('LSP rename')) vim.keymap.set('n', 'ca', vim.lsp.buf.code_action, mkOpts('LSP Code action')) vim.keymap.set('v', 'ca', vim.lsp.buf.code_action, mkOpts('LSP Code action')) vim.keymap.set('n', 'cci', vim.lsp.buf.incoming_calls, mkOpts('LSP incoming calls')) vim.keymap.set('n', 'cco', vim.lsp.buf.outgoing_calls, mkOpts('LSP outgoing calls')) -- vim.keymap.set('n', 'gr', vim.lsp.buf.references, mkOpts('LSP references')) vim.keymap.set('n', 'cr', 'Trouble lsp_references', mkOpts('LSP references')) vim.keymap.set('n', 'cs', 'Telescope lsp_document_symbols', mkOpts('LSP document symbols')) vim.keymap.set( { 'v', 'n' }, 'F', function() vim.lsp.buf.format({ async = true, filter = formatting_filter }) end, mkOpts('Lsp Format') ) if (formatting_filter(client)) then vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})') end if client.name == 'omnisharp' then client.server_capabilities.semanticTokensProvider.legend = { tokenModifiers = { 'static' }, tokenTypes = { 'comment', -- "comment", 'comment', -- "excluded_code", 'identifier', -- "identifier", 'keyword', -- "keyword", 'keyword', -- "keyword_control", 'number', -- "number", 'operator', -- "operator", 'operator', -- "operator_overloaded", 'preproc', -- "preprocessor_keyword", 'string', -- "string", 'whitespace', -- "whitespace", 'text', -- "text", 'static', -- "static_symbol", 'preproc', -- "preprocessor_text", 'punctuation', -- "punctuation", 'string.escape', -- "string_verbatim", 'character.special', -- "string_escape_character", 'class', -- "class_name", 'type', -- "delegate_name", 'enum', -- "enum_name", 'interface', -- "interface_name", 'namespace', -- "module_name", 'struct', -- "struct_name", 'typeParameter', -- "type_parameter_name", 'field', -- "field_name", 'enumMember', -- "enum_member_name", 'constant', -- "constant_name", 'variable', -- "local_name", 'parameter', -- "parameter_name", 'method', -- "method_name", 'method', -- "extension_method_name", 'property', -- "property_name", 'event', -- "event_name", 'namespace', -- "namespace_name", 'label', -- "label_name", 'text.literal', -- "xml_doc_comment_attribute_name", 'text.literal', -- "xml_doc_comment_attribute_quotes", 'text.literal', -- "xml_doc_comment_attribute_value", 'text.literal', -- "xml_doc_comment_cdata_section", 'text.literal', -- "xml_doc_comment_comment", 'text.literal', -- "xml_doc_comment_delimiter", 'text.literal', -- "xml_doc_comment_entity_reference", 'text.literal', -- "xml_doc_comment_name", 'text.literal', -- "xml_doc_comment_processing_instruction", 'text.literal', -- "xml_doc_comment_text", 'text.literal', -- "xml_literal_attribute_name", 'text.literal', -- "xml_literal_attribute_quotes", 'text.literal', -- "xml_literal_attribute_value", 'text.literal', -- "xml_literal_cdata_section", 'text.literal', -- "xml_literal_comment", 'text.literal', -- "xml_literal_delimiter", 'text.literal', -- "xml_literal_embedded_expression", 'text.literal', -- "xml_literal_entity_reference", 'text.literal', -- "xml_literal_name", 'text.literal', -- "xml_literal_processing_instruction", 'text.literal', -- "xml_literal_text", 'regexp', -- "regex_comment", 'regexp', -- "regex_character_class", 'regexp', -- "regex_anchor", 'regexp', -- "regex_quantifier", 'regexp', -- "regex_grouping", 'regexp', -- "regex_alternation", 'regexp', -- "regex_text", 'regexp', -- "regex_self_escaped_character", 'regexp', -- "regex_other_escape", }, } end update_attached(bufnr) end -- }}} local function make_client_capabilities() local caps = require('cmp_nvim_lsp').default_capabilities() local def = vim.lsp.protocol.make_client_capabilities() caps = vim.tbl_deep_extend('keep', caps, def) caps.workspace.didChangeWatchedFiles.dynamicRegistration = false return caps end local function config() vim.api.nvim_create_autocmd('LspAttach', { callback = on_attach, }) vim.api.nvim_create_autocmd('LspDetach', { callback = function(ev) -- vim.print(ev) update_attached(ev.buf) end, }) local capabilities = make_client_capabilities() local nvim_lsp = require('lspconfig') nvim_lsp.util.default_config = vim.tbl_extend('force', nvim_lsp.util.default_config, { capabilities = capabilities, } ) local servers = server_configs(); for name, conf in pairs(servers) do nvim_lsp[name].setup(conf) end end local function config_mason_lsp() local mlsp = require('mason-lspconfig') local servers = server_configs() local available = mlsp.get_available_servers() local ensure = {} for name, _ in pairs(servers) do if vim.tbl_contains(available, name) and name ~= 'phpactor' then ensure[#ensure + 1] = name end end mlsp.setup({ ensure_installed = ensure }) end return { { 'neovim/nvim-lspconfig', event = 'BufRead', config = config, cmd = { 'LspInfo', 'LspLog', 'LspStart' }, dependencies = { { 'folke/neodev.nvim', config = true }, { 'j-hui/fidget.nvim', config = true, tag = 'legacy' }, { 'ray-x/lsp_signature.nvim', opts = { hint_prefix = '⥊ ', floating_window = false, hint_scheme = 'Identifier', }, }, { 'williamboman/mason-lspconfig.nvim', lazy = true, dependencies = { 'williamboman/mason.nvim' }, config = config_mason_lsp, }, { 'simrat39/rust-tools.nvim', build = ':MasonInstall rust-analyzer', config = true, }, }, keys = { { 'li', 'LspInfo', desc = 'Lsp info' }, { 'll', 'LspLog', desc = 'Lsp log' }, }, }, { 'SmiteshP/nvim-navic', lazy = true }, { 'Hoffs/omnisharp-extended-lsp.nvim', lazy = true }, { 'b0o/schemastore.nvim', lazy = true }, { 'Issafalcon/lsp-overloads.nvim', lazy = true }, }