diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2025-12-07 11:58:39 -0700 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2025-12-07 11:58:39 -0700 |
| commit | 77e9fbb666006e52b4da6ac7db9130f892978ff7 (patch) | |
| tree | d7a8836fd4347c4f9d9c61c75f2c933b8cca74d0 /lua/lsp.lua | |
| parent | 20239097d8906a3688201963466221ab044226b7 (diff) | |
| download | config.vim-77e9fbb666006e52b4da6ac7db9130f892978ff7.tar.gz config.vim-77e9fbb666006e52b4da6ac7db9130f892978ff7.tar.bz2 config.vim-77e9fbb666006e52b4da6ac7db9130f892978ff7.zip | |
Diffstat (limited to 'lua/lsp.lua')
| -rw-r--r-- | lua/lsp.lua | 428 |
1 files changed, 234 insertions, 194 deletions
diff --git a/lua/lsp.lua b/lua/lsp.lua index 9a34981..29b4368 100644 --- a/lua/lsp.lua +++ b/lua/lsp.lua @@ -1,60 +1,15 @@ -local vim = assert(vim) +-- my_lsp.lua +local ok_cmp, cmp = pcall(require, "cmp") +local ok_kind, lspkind = pcall(require, "lspkind") +local ok_cmp_caps, cmp_caps = pcall(require, "cmp_nvim_lsp") +local has_lspconfig, lspconfig = pcall(require, "lspconfig") -local nvim_lsp = require("lspconfig") - -nvim_lsp.bashls.setup {} -nvim_lsp.clangd.setup { - filetypes = { 'c', 'cpp', 'objc', 'objcpp', 'cuda' }, -} -nvim_lsp.jqls.setup {} -nvim_lsp.lua_ls.setup {} -nvim_lsp.ocamllsp.setup {} -nvim_lsp.perlpls.setup {} -nvim_lsp.rust_analyzer.setup {} -nvim_lsp.verible.setup {} -nvim_lsp.vimls.setup {} -nvim_lsp.zls.setup {} -nvim_lsp.texlab.setup {} -nvim_lsp.hls.setup { - settings = { - haskell = { - plugin = { - semanticTokens = { - globalOn = true - } - } - } - } -} -nvim_lsp.pylsp.setup { - settings = { - pylsp = { - plugins = { - pycodestyle = { - enabled = false - } - } - } - } -} - -local has_words_before = function() - unpack = unpack or table.unpack - local line, col = unpack(vim.api.nvim_win_get_cursor(0)) - return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil -end - -local feedkey = function(key, mode) - vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) -end - --- 2. Configure CMP +-- ── completion (cmp) ─────────────────────────────────────────────────────────── vim.opt.completeopt = { "menu", "menuone", "noselect" } - --- Don't show matching vim.opt.shortmess:append("c") -local lspkind = require("lspkind") lspkind.init({ +if ok_kind then + lspkind.init({ symbol_map = { Text = "", Method = "", @@ -82,170 +37,255 @@ local lspkind = require("lspkind") lspkind.init({ Operator = "", TypeParameter = "τ", }, -}) - -local cmp = require("cmp") - -cmp.setup({ - mapping = { - ["<C-d>"] = cmp.mapping.scroll_docs(-4, { "i", "s" }), - ["<C-u>"] = cmp.mapping.scroll_docs(4, { "i", "s" }), - ["<C-e>"] = cmp.mapping.close(), - ["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }), - ["<C-n>"] = cmp.mapping(function(fallback) - cmp.select_next_item() - -- Kill any outstanding snippets - vim.fn["vsnip#deactivate"]() - end, { "i", "s" }), - ["<C-p>"] = cmp.mapping(function(fallback) - cmp.select_prev_item() - -- Kill any outstanding snippets - vim.fn["vsnip#deactivate"]() - end, { "i", "s" }), - - ["<Tab>"] = cmp.mapping(function(fallback) - if vim.fn["vsnip#available"](1) == 1 then - feedkey("<Plug>(vsnip-expand-or-jump)", "") - elseif cmp.visible() then - cmp.mapping.confirm({ select = true })() - elseif has_words_before() then - cmp.complete() - else - fallback() -- The fallback function sends a already mapped key. In this case, it's probably `<Tab>`. - end - end, { "i", "s" }), - - ["<S-Tab>"] = cmp.mapping(function() - if cmp.visible() then - cmp.select_prev_item() - elseif vim.fn["vsnip#jumpable"](-1) == 1 then - feedkey("<Plug>(vsnip-jump-prev)", "") - end - end, { "i", "s" }), - }, + }) +end - formatting = { - format = lspkind.cmp_format({ - mode = 'symbol_text', - maxwidth = 50, - ellipsis_char = '...', - show_labelDetails = true, - before = function(_, vim_item) - local function split_silly_function_arguments(str) - if string.len(str) == 0 or string.find(str, "[(]") == nil then - return str, "" - end +local function has_words_before() + local unpack = unpack or table.unpack + local line, col = unpack(vim.api.nvim_win_get_cursor(0)) + if col == 0 then return false end + local l = vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1] or "" + return l:sub(col, col):match("%s") == nil +end - local name_start, name_end = 1, string.find(str, "[(]") - 1 - local function_name = string.sub(str, name_start, name_end) - local arguments = string.sub(str, name_end + 1) +local function feedkey(key, mode) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) +end - return function_name, arguments +if ok_cmp then + cmp.setup({ + mapping = { + ["<C-d>"] = cmp.mapping.scroll_docs(-4, { "i", "s" }), + ["<C-u>"] = cmp.mapping.scroll_docs(4, { "i", "s" }), + ["<C-e>"] = cmp.mapping.close(), + ["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }), + ["<C-n>"] = cmp.mapping(function() + cmp.select_next_item() + pcall(vim.fn["vsnip#deactivate"]) + end, { "i", "s" }), + ["<C-p>"] = cmp.mapping(function() + cmp.select_prev_item() + pcall(vim.fn["vsnip#deactivate"]) + end, { "i", "s" }), + ["<Tab>"] = cmp.mapping(function(fallback) + if vim.fn == 1 then + feedkey("<Plug>(vsnip-expand-or-jump)", "") + elseif cmp.visible() then + cmp.mapping.confirm({ select = true })() + elseif has_words_before() then + cmp.complete() + else + fallback() end - - local verbosity = 1 - local over_9000 = 2 - if vim.bo.filetype == 'java' then - verbosity = over_9000 - if vim_item.kind == "Method" then - local name, args = split_silly_function_arguments(vim_item.abbr) - vim_item.abbr = name - vim_item.menu = args .. (vim_item.menu or "") - end + end, { "i", "s" }), + ["<S-Tab>"] = cmp.mapping(function() + if cmp.visible() then + cmp.select_prev_item() + elseif vim.fn["vsnip#jumpable"](-1) == 1 then + feedkey("<Plug>(vsnip-jump-prev)", "") end - vim_item.abbr = string.sub(vim_item.abbr, 1, 20 * verbosity) - vim_item.menu = string.sub(vim_item.menu or "", 1, 20 * verbosity) - return vim_item - end - }) - }, - - sources = { - { name = "nvim_lsp" }, - { name = "path" }, - { name = "vim_vsnip" }, - { name = "buffer", keyword_length = 5 }, - }, - - sorting = { - comparators = {}, -- We stop all sorting to let the lsp do the sorting - }, + end, { "i", "s" }), + }, + formatting = ok_kind and { + format = lspkind.cmp_format({ + mode = "symbol_text", + maxwidth = 50, + ellipsis_char = "...", + show_labelDetails = true, + before = function(_, item) + local function split_args(s) + if #s == 0 or not s:find("%(") then return s, "" end + local i = s:find("%(") - 1 + return s:sub(1, i), s:sub(i + 1) + end + local verbosity = (vim.bo.filetype == "java") and 2 or 1 + if vim.bo.filetype == "java" and item.kind == "Method" then + local name, args = split_args(item.abbr) + item.abbr = name + item.menu = (args or "") .. (item.menu or "") + end + item.abbr = item.abbr:sub(1, 20 * verbosity) + item.menu = (item.menu or ""):sub(1, 20 * verbosity) + return item + end, + }), + } or nil, + sources = { + { name = "nvim_lsp" }, + { name = "path" }, + { name = "vim_vsnip" }, + { name = "buffer", keyword_length = 5 }, + }, + sorting = { comparators = {} }, -- let LSP decide + snippet = { + expand = function(args) vim.fn["vsnip#anonymous"](args.body) end, + }, + experimental = { native_menu = false, ghost_text = true }, + }) - snippet = { - expand = function(args) - vim.fn["vsnip#anonymous"](args.body) + -- zsh source on zsh buffers + vim.api.nvim_create_augroup("CmpZsh", { clear = true }) + vim.api.nvim_create_autocmd("FileType", { + group = "CmpZsh", + pattern = "zsh", + callback = function() + require("cmp").setup.buffer { sources = { { name = "zsh" } } } end, - }, - - experimental = { - native_menu = false, - ghost_text = true, - }, -}) - -vim.cmd([[ - augroup CmpZsh - au! - autocmd Filetype zsh lua require'cmp'.setup.buffer { sources = { { name = "zsh" }, } } - augroup END -]]) + }) +end +-- ── LSP shared on_attach & capabilities ─────────────────────────────────────── local M = {} --- 3. Set up CiderLSP + M.on_attach = function(client, bufnr) - vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc") - if vim.lsp.formatexpr then -- Neovim v0.6.0+ only. - vim.api.nvim_buf_set_option(bufnr, "formatexpr", "v:lua.vim.lsp.formatexpr") - end - if vim.lsp.tagfunc then - vim.api.nvim_buf_set_option(bufnr, "tagfunc", "v:lua.vim.lsp.tagfunc") + -- omnifunc, tagfunc, formatexpr + vim.bo[bufnr].omnifunc = "v:lua.vim.lsp.omnifunc" + if vim.lsp.formatexpr then vim.bo[bufnr].formatexpr = "v:lua.vim.lsp.formatexpr" end + if vim.lsp.tagfunc then vim.bo[bufnr].tagfunc = "v:lua.vim.lsp.tagfunc" end + + local map = function(mode, lhs, rhs) + vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, silent = true, noremap = true }) end + map("n", "<leader>rn", vim.lsp.buf.rename) + map("n", "<leader>ca", vim.lsp.buf.code_action) + map("n", "<M-k>", vim.lsp.buf.hover) + map("n", "g0", vim.lsp.buf.document_symbol) + map("n", "gW", vim.lsp.buf.workspace_symbol) + map("n", "gd", vim.lsp.buf.definition) + map("n", "gD", vim.lsp.buf.declaration) + map("n", "gi", vim.lsp.buf.implementation) + map("n", "gr", vim.lsp.buf.references) + map("n", "<C-k>", vim.lsp.buf.signature_help) + map("n", "g<space>", vim.lsp.buf.type_definition) + map("n", "[d", vim.diagnostic.goto_prev) + map("n", "]d", vim.diagnostic.goto_next) - local opts = { noremap = true, silent = true } - vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>rn", "<cmd>lua vim.lsp.buf.rename()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>ca", "<cmd>lua vim.lsp.buf.code_action()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "<M-k>", "<cmd>lua vim.lsp.buf.hover()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "g0", "<cmd>lua vim.lsp.buf.document_symbol()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "gW", "<cmd>lua vim.lsp.buf.workspace_symbol()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "gd", "<cmd>lua vim.lsp.buf.definition()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "<cmd>lua vim.lsp.buf.declaration()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "<C-k>", "<cmd>lua vim.lsp.buf.signature_help()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "g<space>", "<cmd>lua vim.lsp.buf.type_definition()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "[d", "<cmd>lua vim.diagnostic.goto_prev()<CR>", opts) - vim.api.nvim_buf_set_keymap(bufnr, "n", "]d", "<cmd>lua vim.diagnostic.goto_next()<CR>", opts) - - vim.api.nvim_command("augroup LSP") - vim.api.nvim_command("autocmd!") + -- document highlight (only if server supports it) if client.server_capabilities.documentHighlightProvider then - vim.api.nvim_create_autocmd("CursorHold", { - buffer = bufnr, - callback = vim.lsp.buf.document_highlight - }) - vim.api.nvim_create_autocmd("CursorHoldI", { - buffer = bufnr, - callback = vim.lsp.buf.document_highlight + local gid = vim.api.nvim_create_augroup("LspDocumentHighlight" .. bufnr, { clear = true }) + vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, { + group = gid, buffer = bufnr, callback = vim.lsp.buf.document_highlight, }) vim.api.nvim_create_autocmd("CursorMoved", { - buffer = bufnr, - callback = vim.lsp.buf.buf_clear_references + group = gid, buffer = bufnr, callback = vim.lsp.buf.clear_references, }) end - vim.api.nvim_command("augroup END") - -- When an LSP is attached, we should just set the sign column to yes in order - -- to avoid the jarring behavior of it appearing and disappearing rapidly. - vim.opt_local.signcolumn = 'yes' + vim.opt_local.signcolumn = "yes" end -vim.cmd[[hi DiagnosticUnderlineError gui=undercurl guisp=salmon]] -vim.cmd[[hi DiagnosticUnderlineWarn gui=undercurl guisp=gold]] +local capabilities = vim.lsp.protocol.make_client_capabilities() +if ok_cmp_caps then + capabilities = cmp_caps.default_capabilities(capabilities) +end -local signs = { Error = " ", Warn = " ", Hint = " ", Info = " " } -for type, icon in pairs(signs) do +-- nice diagnostics styling +vim.cmd [[hi DiagnosticUnderlineError gui=undercurl guisp=salmon]] +vim.cmd [[hi DiagnosticUnderlineWarn gui=undercurl guisp=gold]] +for type, icon in pairs({ Error = " ", Warn = " ", Hint = " ", Info = " " }) do local hl = "DiagnosticSign" .. type vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = "" }) end +-- ── Servers (one table to rule them all) ────────────────────────────────────── +local servers = { + bashls = { filetypes = { "bash", "sh" } }, + clangd = { filetypes = { "c", "cpp", "objc", "objcpp", "cuda" } }, + jqls = { filetypes = { "jq" } }, + lua_ls = { filetypes = { "lua" } }, + ocamllsp = {}, + perlpls = {}, + rust_analyzer = { + settings = { + semantic_tokens = { + modifiers = { + disabled = { "dead" }, + }, + }, + } + }, + verible = {}, + vimls = {}, + zls = {}, + texlab = {}, + hls = { + settings = { haskell = { plugin = { semanticTokens = { globalOn = true } } } } + }, + pylsp = { + settings = { pylsp = { plugins = { pycodestyle = { enabled = false } } } } + }, +} + +-- Fallback command guesses for native start (used only if lspconfig is absent) +local default_cmd = { + bashls = { "bash-language-server", "start" }, + clangd = { "clangd" }, + jqls = { "jq-lsp" }, + lua_ls = { "lua-language-server" }, + ocamllsp = { "ocamllsp" }, + perlpls = { "perlpls" }, + rust_analyzer = { "rust-analyzer" }, + verible = { "verible-verilog-ls" }, + vimls = { "vim-language-server", "--stdio" }, + zls = { "zls" }, + texlab = { "texlab" }, + hls = { "haskell-language-server-wrapper", "--lsp" }, + pylsp = { "pylsp" }, +} + +-- ── Setup: prefer native vim.lsp.config, fallback to lspconfig ─────────────── +local has_native = (vim.lsp and vim.lsp.config and vim.lsp.start) + +local function setup_with_native(name, opts) + -- Build a vim.lsp.config() object and start per-filetype + local cfg = vim.lsp.config(name, { + name = name, + cmd = opts.cmd or default_cmd[name], + root_dir = opts.root_dir, -- optional; can rely on server defaults + filetypes = opts.filetypes, + settings = opts.settings, + capabilities = capabilities, + on_attach = M.on_attach, + }) + + vim.lsp.enable(name); + + -- if cfg.cmd == nil then + -- vim.notify(("LSP %s: no cmd found; skipping native start."):format(name), vim.log.levels.WARN) + -- return + -- end + + -- Start when a matching buffer opens + -- local pat = opts.filetypes or "*" + -- vim.api.nvim_create_autocmd("FileType", { + -- pattern = pat, + -- callback = function(args) + -- -- Only start if ft matches the server's filetypes (or none specified) + -- if not cfg.filetypes or vim.tbl_contains(cfg.filetypes, vim.bo[args.buf].filetype) then + -- vim.lsp.start(cfg, { bufnr = args.buf }) + -- end + -- end, + -- }) +end + +local function setup_with_lspconfig(name, opts) + if not has_lspconfig then + vim.notify(("lspconfig not found; cannot setup %s"):format(name), vim.log.levels.WARN) + return + end + local final = vim.tbl_deep_extend("force", { + capabilities = capabilities, + on_attach = M.on_attach, + }, opts or {}) + lspconfig[name].setup(final) +end + +for name, opts in pairs(servers) do + if has_native then + setup_with_native(name, opts) + else + setup_with_lspconfig(name, opts) + end +end + return M |