-- 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(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 -- if client.server_capabilities.documentFormattingProvider then vim.api.nvim_set_option_value('formatexpr', 'v:lua.vim.lsp.formatexpr()', { buf = bufnr }) vim.keymap.set('n', 'f', vim.lsp.buf.format, { buffer = bufnr, desc = 'format' }) vim.api.nvim_create_autocmd('BufWritePre', { buffer = bufnr, callback = function() vim.cmd.mkview({ bang = true }) require('internal.lsp').format(vim.fn.expand(':p')) end, group = vim.api.nvim_create_augroup('LspAutoFormat', {}), }) 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' }) vim.keymap.set('n', 'ga', vim.cmd.CodeActionMenu, { 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 = vim.tbl_extend('keep', caps or {}, require('cmp_nvim_lsp').default_capabilities()) -- add window/workDoneProgress capability caps = vim.tbl_extend('keep', caps or {}, require('lsp-status').capabilities) return caps end local servers = { clangd = { cmd = { 'clangd', '--clang-tidy', '--header-insertion=iwyu', '--import-insertions', '--header-insertion-decorators', } }, 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' }, -- custom firejail-wrapped 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' }, }, }, }, }, tsserver = {}, pylsp = {}, zls = {}, } 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 errhandler = function(err) vim.notify('internal.lsp.format: ' .. err, vim.log.levels.ERROR) return err end -- save folds pcall(vim.cmd.mkview, { bang = true }) -- run lsp format xpcall(vim.lsp.buf.format, errhandler) -- run organize imports for go if string.match(afile, '.go$') then xpcall( vim.lsp.buf.code_action, errhandler, { context = { only = { 'source.organizeImports' } }, apply = true } ) end -- restore folds pcall(vim.cmd.silent, { 'loadview', bank = true }) -- vim.notify('%#MoreMsg#󰃢%#Italic# fmt', vim.log.levels.INFO) vim.notify('internal.lsp.format: 󰃢', vim.log.levels.INFO) end return { my_attach = my_attach, my_exit = my_exit, capabilities = capabilities, setup = setup, format = format, }