diff --git a/nix/neovim-overlay.nix b/nix/neovim-overlay.nix index 4415ac4..da5525c 100644 --- a/nix/neovim-overlay.nix +++ b/nix/neovim-overlay.nix @@ -53,19 +53,6 @@ with final.pkgs.lib; let which-key-nvim gitsigns-nvim - # Coding - friendly-snippets - lexima-vim - (mkNvimPlugin inputs.neotab-nvim "neotab.nvim") - ts-comments-nvim - mini-ai - - # Formatting - conform-nvim - - # Linting - nvim-lint - # Treesitter nvim-treesitter-textobjects nvim-ts-autotag @@ -100,6 +87,22 @@ with final.pkgs.lib; let yaml ] )) + + # LSP + nvim-lspconfig + + # Coding + friendly-snippets + lexima-vim + (mkNvimPlugin inputs.neotab-nvim "neotab.nvim") + ts-comments-nvim + mini-ai + + # Formatting + conform-nvim + + # Linting + nvim-lint ]; extraPackages = with pkgs; [ diff --git a/nvim/lua/icons.lua b/nvim/lua/icons.lua index 60ef4dc..fc86fab 100644 --- a/nvim/lua/icons.lua +++ b/nvim/lua/icons.lua @@ -12,10 +12,10 @@ return { }, delete = '󰆴', diagnostics = { - Error = ' ', - Warn = ' ', - Hint = ' ', - Info = ' ', + Error = '', + Warn = '', + Hint = '', + Info = '', }, explorer = '󰙅', first = '󰘀', diff --git a/nvim/lua/plugins/editor/neo-tree-nvim.lua b/nvim/lua/plugins/editor/neo-tree-nvim.lua index 385f0cf..632810d 100644 --- a/nvim/lua/plugins/editor/neo-tree-nvim.lua +++ b/nvim/lua/plugins/editor/neo-tree-nvim.lua @@ -63,7 +63,10 @@ return { for _, type in ipairs({ 'Error', 'Warn', 'Info', 'Hint' }) do vim.fn.sign_define( 'DiagnosticSign' .. type, - { text = icons.diagnostics[type], texthl = 'DiagnosticSign' .. type } + { + text = icons.diagnostics[type] .. ' ', + texthl = 'DiagnosticSign' .. type, + } ) end diff --git a/nvim/lua/plugins/lsp/init.lua b/nvim/lua/plugins/lsp/init.lua new file mode 100644 index 0000000..3b5d41b --- /dev/null +++ b/nvim/lua/plugins/lsp/init.lua @@ -0,0 +1,5 @@ +local req = MarleyVim.local_require('plugins.lsp') + +return { + req('nvim-lspconfig'), +} diff --git a/nvim/lua/plugins/lsp/nvim-lspconfig.lua b/nvim/lua/plugins/lsp/nvim-lspconfig.lua new file mode 100644 index 0000000..97f4705 --- /dev/null +++ b/nvim/lua/plugins/lsp/nvim-lspconfig.lua @@ -0,0 +1,235 @@ +return { + 'nvim-lspconfig', + event = { 'BufReadPost', 'BufWritePost', 'BufNewFile' }, + before = function() + require('lz.n').trigger_load('blink.cmp') + end, + after = function() + local i = require('icons') + + local opts = { + ---@class vim.diagnostic.Opts + diagnostics = { + underline = true, + update_in_insert = false, + virtual_text = { + spacing = 4, + source = 'if_many', + prefix = function(diagnostic) + for d, icon in pairs(i.diagnostics) do + if diagnostic.severity == vim.diagnostic.severity[d:upper()] then + return icon + end + end + + return '●' + end, + }, + severity_sort = true, + signs = { + text = { + [vim.diagnostic.severity.ERROR] = i.diagnostics.Error, + [vim.diagnostic.severity.WARN] = i.diagnostics.Warn, + [vim.diagnostic.severity.HINT] = i.diagnostics.Hint, + [vim.diagnostic.severity.INFO] = i.diagnostics.Info, + }, + }, + }, + + capabilities = { + workspace = { + fileOperations = { + didRename = true, + willRename = true, + }, + }, + }, + + servers = { + lua_ls = { + settings = { + Lua = { + workspace = { checkThirdParty = false }, + codeLens = { enable = true }, + completion = { callSnippet = 'Replace' }, + doc = { privateName = { '^_' } }, + hint = { + enable = true, + setType = false, + paramType = true, + paramName = 'Disable', + semicolon = 'Disable', + arrayIndex = 'Disable', + }, + }, + }, + }, + }, + } + + -- Codelens. + vim.api.nvim_create_autocmd('User', { + pattern = 'LspSupportsMethod', + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + local buffer = args.data.buffer + + if client and args.data.method == 'textDocument/codeLens' then + vim.lsp.codelens.refresh() + + vim.api.nvim_create_autocmd( + { 'BufEnter', 'CursorHold', 'InsertLeave' }, + { + buffer = buffer, + callback = vim.lsp.codelens.refresh, + } + ) + end + end, + }) + + -- Diagnostics. + vim.diagnostic.config(vim.deepcopy(opts.diagnostics)) + + -- Server setup. + local has_blink, blink = pcall(require, 'blink.cmp') + local capabilities = vim.tbl_deep_extend( + 'force', + {}, + vim.lsp.protocol.make_client_capabilities(), + has_blink and blink.get_lsp_capabilities() or {}, + opts.capabilities + ) + + local function setup(server) + local server_opts = vim.tbl_deep_extend('force', { + capabilities = vim.deepcopy(capabilities), + }, opts.servers[server] or {}) + + if server_opts.enabled == false then + return + end + + if opts.setup and opts.setup[server] then + if opts.setup[server](server, server_opts) then + return + end + end + + require('lspconfig')[server].setup(server_opts) + end + + for server, server_opts in pairs(opts.servers) do + if server_opts.enabled ~= false then + setup(server) + end + end + + -- Set keymaps. + vim.api.nvim_create_autocmd('LspAttach', { + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + + if not client then + return + end + + local set = vim.keymap.set + + set('n', 'cl', 'LspInfo', { desc = 'lsp info' }) + set('n', 'gD', vim.lsp.buf.declaration, { desc = 'declaration' }) + set('n', 'gI', vim.lsp.buf.implementation, { desc = 'implementation' }) + + set( + 'n', + 'gr', + vim.lsp.buf.references, + { desc = 'references', nowait = true } + ) + + set( + 'n', + 'gy', + vim.lsp.buf.type_definition, + { desc = 'type definition' } + ) + + set('n', 'K', function() + return vim.lsp.buf.hover() + end, { desc = 'hover' }) + + if client.supports_method('textDocument/codeAction') then + set( + { 'n', 'v' }, + 'ca', + vim.lsp.buf.code_action, + { desc = 'code action' } + ) + end + + if client.supports_method('textDocument/codeLens') then + set( + { 'n', 'v' }, + 'cc', + vim.lsp.codelens.run, + { desc = 'run codelens' } + ) + + set( + 'n', + 'cC', + vim.lsp.codelens.refresh, + { desc = 'refresh & display codelens' } + ) + end + + if client.supports_method('textDocument/definition') then + set('n', 'gd', vim.lsp.buf.definition, { desc = 'definition' }) + end + + if client.supports_method('textDocument/documentHighlight') then + if Snacks.words.is_enabled() then + set('n', '', function() + Snacks.words.jump(vim.v.count1, true) + end, { desc = 'next reference' }) + + set('n', '', function() + Snacks.words.jump(-vim.v.count1, true) + end, { desc = 'prev reference' }) + + set('n', ']]', function() + Snacks.words.jump(vim.v.count1) + end, { desc = 'next reference' }) + + set('n', '[[', function() + Snacks.words.jump(-vim.v.count1) + end, { desc = 'prev reference' }) + end + end + + if client.supports_method('textDocument/rename') then + set('n', 'cr', vim.lsp.buf.rename, { desc = 'rename' }) + end + + if client.supports_method('textDocument/signatureHelp') then + set('i', '', function() + return vim.lsp.buf.signature_help() + end, { desc = 'signature help' }) + + set('n', 'gK', function() + vim.lsp.buf.signature_help() + end, { desc = 'signature help' }) + end + + if + client.supports_method('workspace/didRenameFiles') + or client.supports_method('workspace/willRenameFiles') + then + set('n', 'cR', function() + Snacks.rename.rename_file() + end, { desc = 'rename file' }) + end + end, + }) + end, +}