lsp_config.lua (13943B)
1 vim.diagnostic.config { 2 virtual_text = false, 3 severity_sort = true, 4 signs = true, 5 } 6 7 vim.lsp.log.set_level(vim.lsp.log_levels.WARN) 8 9 vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(vim.lsp.handlers.hover, { border = 'single' }) 10 vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(vim.lsp.handlers.hover, { border = 'single' }) 11 12 local function on_attach(client, bufnr) 13 local function set_keymap(lhs, func, desc) 14 -- vim.api.nvim_echo({ {"Setup "}, {lhs} }, true, {}) 15 vim.keymap.set("n", lhs, "", { 16 remap = false, 17 silent = true, 18 buffer = bufnr, 19 callback = func, 20 desc = desc 21 }) 22 end 23 24 local function set_autocmd(event, func) 25 vim.api.nvim_create_autocmd(event, { 26 buffer = bufnr, 27 callback = function() pcall(func) end, 28 }) 29 end 30 31 -- Mappings. 32 -- See `:help vim.lsp.*` for documentation on any of the below functions 33 if client.server_capabilities and client.server_capabilities.hoverProvider then 34 set_keymap('K', vim.lsp.buf.hover, "Hover / print docs") 35 end 36 set_keymap('<Leader>d', vim.lsp.buf.definition, "Go to definition") 37 set_keymap('<Leader>gd', vim.lsp.buf.declaration, "Go to declaration") 38 set_keymap('<Leader>td', vim.lsp.buf.type_definition, "Go to type definition") 39 set_keymap('gr', vim.lsp.buf.rename, "Rename symbol at point") 40 set_keymap('<Leader>a', vim.lsp.buf.code_action, "Code action at point") 41 set_keymap('<Leader>=', vim.lsp.buf.format, "Format the whole buffer") 42 set_keymap('gR', require "azy.builtins".lsp.references(), "Search for references") 43 set_keymap('<Leader>s', require "azy.builtins".lsp.workspace_symbols(), "Search workspace symbols") 44 45 vim.opt_local.tagfunc = "v:lua.vim.lsp.tagfunc" 46 47 set_keymap('<Leader>e', require "azy.builtins".files(vim.tbl_filter(function(p) 48 return #p > 0 and p ~= vim.fn.expand("$HOME") 49 end, vim.lsp.buf.list_workspace_folders()))) 50 51 if client.supports_method('textDocument/documentHighlight') then 52 set_autocmd("CursorHold", vim.lsp.buf.document_highlight) 53 set_autocmd("CursorMoved", vim.lsp.buf.clear_references) 54 end 55 56 if client.supports_method('textDocument/codeLens') then 57 set_autocmd({ "BufEnter", "CursorHoldI", "InsertLeave" }, vim.lsp.codelens.refresh) 58 set_keymap('<Leader>lr', vim.lsp.codelens.run, "Run codelens") 59 end 60 61 -- if client.supports_method 'textDocument/inlayHint' then 62 -- vim.lsp.inlay_hint(bufnr, true) 63 -- end 64 end 65 66 -- TexLab and LTeX things 67 68 local function texlab_attach(client, bufnr) 69 vim.api.nvim_buf_set_keymap(bufnr, 'n', '<Leader>m', '<cmd>TexlabForward<CR>', { noremap = true, silent = true }) 70 71 vim.lsp.protocol.SymbolKind = { 72 'file', 73 'sec', 74 'fold', 75 '', 76 'class', 77 'float', 78 'lib', 79 'field', 80 'label', 81 'enum', 82 'misc', 83 'cmd', 84 'thm', 85 'equ', 86 'strg', 87 'arg', 88 '', 89 '', 90 'PhD', 91 '', 92 '', 93 'item', 94 'book', 95 'artl', 96 'part', 97 'coll', 98 } 99 vim.lsp.protocol.CompletionItemKind = { 100 'string', 101 '', 102 '', 103 '', 104 'field', 105 '', 106 'class', 107 'misc', 108 '', 109 'library', 110 'thesis', 111 'argument', 112 '', 113 '', 114 'snippet', 115 'color', 116 'file', 117 '', 118 'folder', 119 '', 120 '', 121 'book', 122 'article', 123 'part', 124 'collect', 125 } 126 on_attach(client, bufnr) 127 end 128 129 local augroup = vim.api.nvim_create_augroup("User_LSP", {}) 130 131 local find_root = function(markers, file) 132 local dirs = vim.fs.find(markers, { upward = true, path = vim.fs.dirname(file) }) 133 local source = vim.fn.fnamemodify(dirs[1] or file, ":p") 134 if source:sub(-1) == "/" then 135 source = vim.fs.dirname(source) 136 end 137 return vim.fs.dirname(source) 138 end 139 140 local function setup_lsp(name, filetypes, command, ucommands, config) 141 vim.api.nvim_create_autocmd("Filetype", { 142 pattern = filetypes, 143 group = augroup, 144 callback = function(args) 145 local cpreffix = name:gsub("[-_ ]", "") 146 cpreffix = cpreffix:sub(1, 1):upper() .. cpreffix:sub(2) 147 148 -- Setup user commands if requested 149 for cname, func in pairs(ucommands) do 150 vim.api.nvim_create_user_command(cpreffix .. cname, function(...) 151 local client = vim.lsp.get_clients { name = name }[1] 152 func(client, args.buf, ...) 153 end, {}) 154 end 155 local cfg = vim.deepcopy(config) 156 cfg.name = name 157 cfg.cmd = command 158 159 -- Find the root directory 160 if not cfg.root_dir then 161 cfg.root_dir = find_root(cfg.root_markers or { ".git" }, args.file) 162 end 163 vim.lsp.start(cfg, { bufnr = args.buf }) 164 end 165 }) 166 end 167 168 local function execute_command(client, command, args, bufnr, handler) 169 return client.request("workspace/executeCommand", { command = command, arguments = args }, handler, bufnr) 170 end 171 172 -- System lsps 173 174 local capabilities = vim.lsp.protocol.make_client_capabilities() 175 capabilities.textDocument.completion.completionItem.snippetSupport = true 176 capabilities.workspace.configuration = true 177 178 local default_cfg = { capabilities = capabilities, on_attach = on_attach } 179 180 -- A bit of notes for the future me 181 -- filetypes (table) List of filetypes for this lsp 182 -- command (table|nil) command to run to start the server, defaults to server name 183 -- cfg (table) config as in vim.lsp.start_client() 184 -- root_markers (list|nil) Files that are markers for the root directory 185 -- ucommands (table) Mapping from valid command names to lua functions (as in 186 -- nvim_create_user_command, with first argument being the client) to create in the buffer where the thing is started 187 local system_lsps = { 188 lemminx = { 189 filetypes = { 'xml' }, 190 }, 191 hls = { 192 command = { "haskell-language-server-wrapper", "--lsp" }, 193 filetypes = { "haskell" }, 194 root_markers = { "Setup.hs", "stack.yaml" }, 195 }, 196 197 zls = { 198 filetypes = { "zig" }, 199 root_markers = { "build.zig", ".git" }, 200 cfg = { 201 capabilities = capabilities, 202 on_attach = on_attach, 203 settings = { 204 zls = { 205 enable_autofix = false, 206 enable_snippets = true, 207 enable_inlay_hints = true, 208 include_at_in_builtins = true, 209 inlay_hints_hide_redundant_param_names_last_token = true, 210 warn_style = true, 211 } 212 } 213 } 214 }, 215 216 htmlsp = { 217 filetypes = { "html" }, 218 root_markers = { "index.html", ".git" }, 219 command = { "vscode-html-language-server", "--stdio" }, 220 }, 221 222 lean = { 223 filetypes = { 'lean', }, 224 command = { 'lake', 'serve'}, 225 }, 226 227 cmakels = { 228 command = { "cmake-language-server" }, 229 filetypes = { "cmake" }, 230 root_markers = { "CMakeLists.txt", ".git", } 231 }, 232 233 tsserver = { 234 command = { "typescript-language-server", "--stdio" }, 235 filetypes = { "typescript", "javascript" }, 236 root_markers = { "tsconfig.json", "package.json", ".git" } 237 }, 238 239 ocamllsp = { 240 filetypes = { "ocaml" }, 241 cfg = { 242 capabilites = capabilities, 243 on_attach = on_attach, 244 settings = { 245 ocaml = { 246 server = { 247 extraEnv = { 248 OCAMLLSP_SEMANTIC_HIGHLIGHTING = "full", 249 } 250 } 251 } 252 } 253 } 254 }, 255 256 clangd = { 257 command = { "clangd", "--limit-results=0", "--suggest-missing-includes", "--all-scopes-completion", 258 "--compile-commands-dir=build/" }, 259 filetypes = { "c", "cpp" }, 260 root_markers = { "CMakeLists.txt", ".git" }, 261 }, 262 263 pylsp = { 264 filetypes = { "python" }, 265 }, 266 267 ["rnix-lsp"] = { 268 filetypes = { "nix" }, 269 }, 270 271 ["vim-language-server"] = { 272 filetypes = { "vim" }, 273 }, 274 275 gopls = { 276 filetypes = { "go" }, 277 }, 278 279 rust_analyzer = { 280 filetypes = { "rust" }, 281 root_markers = { "Cargo.toml", ".git" }, 282 command = { "rustup", "run", "nightly", "rust-analyzer" }, 283 }, 284 285 codeql = { 286 command = { 'codeql', 'execute', 'language-server', '--check-errors=ON_CHANGE' }, 287 filetypes = { 'ql' }, 288 }, 289 290 ["lua-language-server"] = (function() 291 return { 292 filetypes = { "lua" }, 293 cfg = { 294 capabilities = capabilities, 295 on_attach = on_attach, 296 settings = { 297 Lua = { 298 runtime = { 299 -- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim) 300 version = 'LuaJIT', 301 -- Setup your lua path 302 path = vim.split(package.path, ';'), 303 }, 304 hint = { 305 enable = true 306 }, 307 diagnostics = { 308 -- Get the language server to recognize the `vim` global 309 globals = { 'vim' }, 310 311 workspaceDelay = -1, 312 313 groupFileStatus = { 314 strict = "Opened", 315 strong = "Opened", 316 }, 317 318 groupSeverity = { 319 strong = "Warning", 320 strict = "Warning", 321 }, 322 }, 323 workspace = { 324 -- Make the server aware of Neovim runtime files 325 library = vim.api.nvim_get_runtime_file('', true), 326 checkThirdParty = false, 327 }, 328 -- Do not send telemetry data containing a randomized but unique identifier 329 telemetry = { 330 enable = false, 331 }, 332 completion = { 333 callSnippet = "Replace", 334 } 335 }, 336 }, 337 } 338 } 339 end)(), 340 341 texlab = { 342 command = { "texlab" }, 343 filetypes = { "tex", "bib", "latex" }, 344 root_markers = { ".latexmkrc", ".git" }, 345 ucommands = { 346 Build = function(client, bufnr) 347 local texlab_build_status = vim.tbl_add_reverse_lookup { 348 Success = 0, 349 Error = 1, 350 Failure = 2, 351 Cancelled = 3, 352 } 353 354 local params = vim.lsp.util.make_text_document_params(bufnr) 355 client.request('textDocument/build', params, function(err, result) 356 if err then 357 error(tostring(err)) 358 end 359 vim.notify('Build ' .. texlab_build_status[result.status]) 360 end, bufnr) 361 end, 362 363 Forward = function(client, bufnr) 364 local texlab_forward_status = vim.tbl_add_reverse_lookup { 365 Success = 0, 366 Error = 1, 367 Failure = 2, 368 Unconfigured = 3, 369 } 370 371 local params = { 372 textDocument = { uri = vim.uri_from_bufnr(bufnr) }, 373 position = { line = vim.fn.line '.' - 1, character = vim.fn.col '.' }, 374 } 375 376 client.request('textDocument/forwardSearch', params, function(err, result) 377 if err then 378 error(tostring(err)) 379 end 380 vim.notify('Search ' .. texlab_forward_status[result.status]) 381 end) 382 end, 383 384 CleanAuxiliary = function(client, bufnr) 385 client.request("workspace/executeCommand", 386 { command = "texlab.cleanAuxiliary", arguments = { document = vim.lsp.util.make_text_document_params(bufnr) } }, 387 function(...) end, bufnr) 388 end, 389 390 CleanArtifacts = function(client, bufnr) 391 local uri = vim.uri_from_bufnr(bufnr) 392 client.request("workspace/executeCommand", 393 { command = "texlab.cleanArtifacts", arguments = { document = { uri = uri } } }) 394 end 395 }, 396 cfg = { 397 trace = "verbose", 398 capabilities = capabilities, 399 on_attach = texlab_attach, 400 settings = { 401 texlab = { 402 build = { 403 onSave = true, 404 forwardSearchAfter = true, 405 args = { "-interaction=nonstopmode", "-f", "-synctex=1", "-shell-escape", "-xelatex", "%f" } 406 }, 407 forwardSearch = { 408 executable = "zathura", 409 args = { "-x", 410 -- First level of escaping is for string.format, second level for escaping is for texlab 411 string.format("nvim --server %s --remote-send '<cmd>edit +%%%%{line} %%%%{input}<cr>'", 412 vim.fn.serverlist()[1]), 413 "--synctex-forward", "%l:1:%f", "%p" } 414 }, 415 } 416 }, 417 }, 418 } 419 } 420 421 for lname, config in pairs(system_lsps) do 422 if not config.filetypes then 423 vim.notify(string.format("No filetypes defined for %s", lname)) 424 else 425 setup_lsp(lname, config.filetypes, config.command or { lname }, config.ucommands or {}, config.cfg or default_cfg) 426 end 427 end 428 429 require 'ltex-ls'.setup { 430 on_attach = on_attach, 431 capabilities = capabilities, 432 filetypes = { "latex", "tex", "bib", "markdown", "gitcommit", "text", "mail" }, 433 use_spellfile = true, 434 handlers = { 435 ["ltex/workspaceSpecificConfiguration"] = vim.lsp.with(require 'ltex-ls.handlers'.workspace_configuration, 436 { debug = true }) 437 }, 438 settings = { 439 ltex = { 440 checkFrequency = "save", 441 enabled = { "latex", "tex", "bib", "markdown", }, 442 language = "auto", 443 diagnosticSeverity = "information", 444 additionalRules = { 445 enablePickyRules = true, 446 motherTongue = "fr", 447 }, 448 disabledRules = { 449 en = { "REGARD", "PASSIVE_VOICE", "ACTUALLY", "REST" }, 450 fr = { "APOS_TYP", "FRENCH_WHITESPACE", "FR_SPELLING_RULE", "COMMA_PARENTHESIS_WHITESPACE" } 451 }, 452 latex = { 453 commands = { 454 ["\\MaxMC"] = "dummy", 455 ["\\SSAT"] = "dummy", 456 ["\\SAT"] = "dummy", 457 ["\\todo"] = "ignore", 458 }, 459 environments = { 460 quote = 'ignore', 461 } 462 }, 463 dictionary = (function() 464 local files = {} 465 for _, file in ipairs(vim.api.nvim_get_runtime_file("spell/*.add", true)) do 466 local lang = vim.fn.fnamemodify(file, ":t:r:r") -- Because 'spellfile' is .{encoding}.add 467 local fullpath = vim.fn.fnamemodify(file, ":p") 468 files[lang] = { ":" .. fullpath } 469 end 470 471 if files.default then 472 for lang, _ in pairs(files) do 473 if lang ~= "default" then 474 vim.list_extend(files[lang], files.default) 475 end 476 end 477 files.default = nil 478 end 479 return files 480 end)(), 481 trace = { server = "verbose" }, 482 }, 483 }, 484 }