-- TODO vim.api.nvim_create_autocmd('LspAttach', { -- callback = function(args) -- local client = vim.lsp.get_client_by_id(args.data.client_id) -- local bufnr = args.buf -- end, -- }) local my_attach = function(client, bufnr) if vim.opt.diff:get() then vim.notify('not running LSP client in diff mode', vim.log.levels.WARN) vim.lsp.stop_client() return end local group = vim.api.nvim_create_augroup('LspOnAttach', {}) vim.api.nvim_create_autocmd('CursorHold', { buffer = bufnr, group = group, callback = function() if _G.diagnostic_hidden then return end -- evalutate if there's already a floating preview buffer local existing = vim.F.npcall(vim.api.nvim_buf_get_var, bufnr, 'lsp_floating_preview') if not existing or not vim.api.nvim_win_is_valid(existing) then vim.diagnostic.open_float() end end, }) require('lsp-status').on_attach(client) if client.supports_method('textDocument/documentSymbol') then require('nvim-navic').attach(client, bufnr) -- require('aerial').on_attach(client, bufnr) -- vim.keymap.set('n', 'g0', vim.lsp.buf.document_symbol, {buffer = bufnr}) end if client.supports_method('workspace/symbol') then vim.keymap.set( 'n', 'gW', vim.lsp.buf.workspace_symbol, { buffer = bufnr, desc = 'goto symbol' } ) vim.keymap.set( 'n', ';s', require('telescope.builtin').lsp_workspace_symbols, { desc = 'lsp workspace symbols' } ) end if client.supports_method('textDocument/definition') then vim.keymap.set( 'n', '', vim.lsp.buf.definition, { buffer = bufnr, desc = 'goto definition' } ) vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { buffer = bufnr, desc = 'goto definition' }) vim.keymap.set('n', 'gR', vim.lsp.buf.references, { buffer = bufnr, desc = 'goto references' }) vim.keymap.set( 'n', ';R', require('telescope.builtin').lsp_references, { desc = 'lsp references' } ) end if client.supports_method('textDocument/hover') then vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr, desc = 'hover' }) end if client.supports_method('textDocument/rename') then vim.keymap.set('n', 'grr', vim.lsp.buf.rename, { buffer = bufnr, desc = 'rename' }) end if client.supports_method('signatureHelp') then vim.keymap.set( 'n', '', vim.lsp.buf.signature_help, { buffer = bufnr, desc = 'signature help' } ) end if client.supports_method('textDocument/formatting') then vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr()') vim.keymap.set('n', 'f', vim.lsp.buf.format, { buffer = bufnr, desc = 'format' }) vim.api.nvim_create_autocmd('BufWritePre', { buffer = bufnr, group = group, callback = function() require('internal.lsp').format(vim.fn.expand(':p')) end, }) end if client.supports_method('textDocument/typeDefinition') then vim.keymap.set( 'n', 'gT', vim.lsp.buf.type_definition, { buffer = bufnr, desc = 'goto typedef' } ) end if client.supports_method('textDocument/declaration') then vim.keymap.set( 'n', 'gD', vim.lsp.buf.declaration, { buffer = bufnr, desc = 'goto declaration' } ) end if client.supports_method('textDocument/implementation') then vim.keymap.set( 'n', 'gI', vim.lsp.buf.implementation, { buffer = bufnr, desc = 'goto implementation' } ) end if client.supports_method('textDocument/codeAction') then vim.keymap.set('n', 'ga', vim.lsp.buf.code_action, { buffer = bufnr, desc = 'code action' }) end if client.supports_method('textDocument/documentHighlight') then vim.keymap.set( 'n', '8', vim.lsp.buf.document_highlight, { buffer = bufnr, desc = 'document highlight' } ) -- vim.keymap.set('n', '', vim.lsp.buf.clear_references, {buffer = bufnr, desc = 'clear references'}) vim.api.nvim_create_autocmd('CursorMoved', { callback = vim.lsp.buf.clear_references, buffer = bufnr, }) end end local my_exit = function(_, _, _) end local capabilities = function() local caps = vim.lsp.protocol.make_client_capabilities() -- enable lsp-based snippets caps.textDocument.completion.completionItem.snippetSupport = true caps.textDocument.completion.completionItem.resolveSupport = { properties = { 'documentation', 'detail', 'additionalTextEdits', }, } -- add window/workDoneProgress capability caps = vim.tbl_extend('keep', caps or {}, require('lsp-status').capabilities) return caps end local servers = { clangd = {}, elixirls = { cmd = { 'elixir-ls' }, settings = { elixirLS = { dialyzerEnabled = false, }, }, }, gopls = { settings = { gopls = { analyses = { -- composites = false, fieldalignment = true, nilness = true, shadow = false, unusedparams = true, unusedwrite = true, }, gofumpt = true, -- hoverKind = 'Structured', }, }, }, lua_ls = { -- cmd = { 'lua-language-server' }, cmd = { 'luals' }, settings = { Lua = { runtime = { version = 'LuaJIT', path = vim.split(package.path, ';'), }, diagnostics = { globals = { '_G', 'assert', 'error', 'os', 'package', 'pairs', 'ipairs', 'pcall', 'require', 'string', 'table', 'type', 'unpack', 'vim', 'xpcall', }, neededFileStatus = { ['code-style-check'] = 'Any', }, }, format = { enable = true, defaultConfig = { indent_style = 'space', indent_size = '2', }, }, workspace = { -- library = vim.api.nvim_get_runtime_file("", true), library = { [vim.env.VIMRUNTIME .. '/lua'] = true, [vim.env.VIMRUNTIME .. '/lua/vim/lsp'] = true, [vim.fn.stdpath('config') .. '/lua'] = true, }, }, telemetry = { enable = false, }, }, }, }, rust_analyzer = { -- cmd = { 'env', 'CARGO_TARGET_DIR=/tmp', 'RUST_SRC_PATH=/usr/src/rustlib/library', 'rust-analyzer' }, settings = { ['rust-analyzer'] = { checkOnSave = { command = { 'cargo', 'clippy' }, }, }, }, }, pylsp = {}, -- tsserver = { disabled = true, -- cmd = { -- 'toolbox', 'run', '--', -- 'sh', '-c', -- '. /etc/profile.d/nvm.sh && typescript-language-server --stdio' -- }, -- }, -- zls = { disabled = true }, } local setup = function() -- configure floating win handlers vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(vim.lsp.handlers.hover, { focus = false, zindex = 100, border = _G.floating_win_border, }) vim.lsp.handlers['textDocument/signature_help'] = vim.lsp.with(vim.lsp.handlers.signature_help, { border = _G.floating_win_border }) local caps = capabilities() for server, opts in pairs(servers) do if not opts.disabled then opts = vim.tbl_extend('error', opts, { on_attach = my_attach, on_exit = my_exit, capabilities = caps, }) -- lsp-status custom handlers local ok, lspstatus_ext = pcall(require('lsp-status').extensions[server]) if ok then opts = vim.tbl_extend('error', opts, { handlers = lspstatus_ext.setup(), }) end require('lspconfig')[server].setup(opts) end end end local format = function(afile) if vim.g.nofmt then return end local ok, isMatch = pcall(string.match, afile, '^/home/robert/devel/upstream') if not ok or isMatch then return end local errhandler = function(err) vim.notify('fmt failed: ' .. err, vim.log.levels.ERROR) return err end xpcall(vim.lsp.buf.format, errhandler) if string.match(afile, '.go$') then xpcall( vim.lsp.buf.code_action, errhandler, { context = { only = { 'source.organizeImports' } }, apply = true } ) end -- vim.notify('%#MoreMsg#󰃢%#Italic# fmt', vim.log.levels.INFO) vim.notify('󰃢 ', vim.log.levels.INFO) end return { my_attach = my_attach, my_exit = my_exit, capabilities = capabilities, setup = setup, format = format, }