return { 'neovim/nvim-lspconfig', dependencies = { -- Language server management plugins 'williamboman/mason.nvim', 'williamboman/mason-lspconfig.nvim', -- Completion plugins 'hrsh7th/cmp-nvim-lsp', -- Source for built-in language server client 'hrsh7th/cmp-buffer', -- Source for buffer words 'hrsh7th/cmp-path', -- Source for filesystem paths 'kbenzie/cmp-git', -- Source for Git/GitHub/GitLab 'hrsh7th/nvim-cmp', -- Completion engine combines and use the above -- TODO: https://github.com/b0o/SchemaStore.nvim -- TODO: https://github.com/jmbuhr/otter.nvim -- LSP UI plugins 'aznhe21/actions-preview.nvim', 'j-hui/fidget.nvim', 'folke/trouble.nvim', 'nvim-tree/nvim-web-devicons', 'ray-x/lsp_signature.nvim', -- Lua vim module support in lua language server 'folke/neodev.nvim', -- Snippet pluggins { 'L3MON4D3/LuaSnip', build = 'make install_jsregexp' }, 'saadparwaiz1/cmp_luasnip', -- Expose clangd extensions 'p00f/clangd_extensions.nvim', -- TODO: https://github.com/nvimtools/none-ls.nvim -- TODO: https://github.com/mfussenegger/nvim-dap -- TODO: https://github.com/rcarriga/nvim-dap-ui }, config = function() require('mason').setup() require('mason-lspconfig').setup({ automatic_installation = false, ensure_installed = { 'ansiblels', -- Ansible 'bashls', -- Bash 'clangd', -- C/C++ 'cmake', -- Cmake 'docker_compose_language_service', -- Docker Compose 'dockerls', -- Dockerfile 'esbonio', -- Sphinx 'html', -- HTML 'jsonls', -- JSON 'lemminx', -- XML 'lua_ls', -- Lua 'opencl_ls', -- OpenCL 'powershell_es', -- Powershell 'pyright', -- Python 'ruff_lsp', -- Python 'vimls', -- VimScript 'yamlls', -- YAML }, handlers = { -- Default handler, sets up everything unless a custom language server -- setup handler is defined below function(server_name) require('lspconfig')[server_name].setup({}) end, ['lua_ls'] = function() require('neodev').setup() require('lspconfig').lua_ls.setup({ settings = { Lua = { diagnostics = { disable = { 'missing-fields', }, globals = { 'vim', }, } } } }) end, ['pyright'] = function() require('lspconfig').pyright.setup({ settings = { pyright = { disableOrganizeImports = true, -- Use ruff import sorter instead }, } }) end, }, }) local cmp = require('cmp') cmp.setup({ snippet = { expand = function(args) require('luasnip').lsp_expand(args.body) end }, mapping = cmp.mapping.preset.insert({ -- Open completion menu/confirm completion [''] = cmp.mapping.complete(), [''] = cmp.mapping.confirm({ select = true }), -- Select completion from menu [''] = cmp.mapping.select_next_item(), [''] = cmp.mapping.select_prev_item(), -- Scroll documentation of selected completion item [''] = cmp.mapping.scroll_docs(4), [''] = cmp.mapping.scroll_docs(-4), }), sources = { { name = 'nvim_lsp' }, { name = 'luasnip' }, { name = 'buffer' }, { name = 'path' }, { name = 'git' }, }, window = { completion = cmp.config.window.bordered(), documentation = cmp.config.window.bordered(), }, }) require("cmp_git").setup({}) -- Customise LSP UI vim.lsp.handlers['textDocument/hover'] = vim.lsp.with( vim.lsp.handlers.hover, { border = 'rounded', title = 'Hover', } ) vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with( vim.lsp.handlers.signature_help, { border = 'rounded', title = 'Signature Help', } ) -- Customise diagnostics UI vim.diagnostic.config({ float = { border = 'rounded', -- Enable rounded border on floats }, virtual_text = false, -- Disable trailing virtual text }) -- Diagnostics mappings -- TODO: trouble.nvim mappings instead? https://youtu.be/MuUrCcvE-Yw?t=631 vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { remap = false }) vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { remap = false }) vim.keymap.set('n', 'sd', vim.diagnostic.open_float, { remap = false }) vim.keymap.set('n', 'sq', vim.diagnostic.setqflist, { remap = false }) -- Mappings created when LSP is attached to a buffer local augroup = vim.api.nvim_create_augroup('lsp', { clear = true }) vim.api.nvim_create_autocmd('LspAttach', { pattern = '*', group = augroup, callback = function(ev) local opts = { noremap = true, buffer = ev.buf } -- Fixit mapping, or close enough, actually any code action vim.keymap.set('n', 'fi', require('actions-preview').code_actions, opts) -- Goto mappings vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts) vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts) vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts) vim.keymap.set('n', 'go', vim.lsp.buf.type_definition, opts) vim.keymap.set('n', 'gr', require('telescope.builtin').lsp_references, opts) vim.keymap.set('n', 'ds', require('telescope.builtin').lsp_document_symbols, opts) vim.keymap.set('n', 'ws', require('telescope.builtin').lsp_dynamic_workspace_symbols, opts) -- Refactoring mappings vim.keymap.set('n', 'rn', vim.lsp.buf.rename, opts) -- Help mappings -- TODO: v0.10.0 |vim.lsp.start()| now maps |K| to use -- |vim.lsp.buf.hover()| if the server supports it, unless -- |'keywordprg'| was customized before calling |vim.lsp.start()|. vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts) -- Format whole buffer mapping vim.keymap.set('n', 'gq', vim.lsp.buf.format, opts) end }) -- Snippet mappings local luasnip = require('luasnip') luasnip.setup({}) vim.keymap.set({ 'i', 's' }, '', function() if luasnip.expand_or_jumpable() then luasnip.expand_or_jump() end end, { silent = true }) vim.keymap.set({ 'i', 's' }, '', function() if luasnip.jumpable(-1) then luasnip.jump(-1) end end, { silent = true }) -- Load snippets local opts = { paths = vim.fn.stdpath('config') .. '/snippets' } require('luasnip.loaders.from_snipmate').lazy_load(opts) require('luasnip.loaders.from_lua').lazy_load(opts) -- LSP UI plugins require('fidget').setup({}) require('trouble').setup({}) vim.keymap.set('n', 'tr', function() require('trouble').toggle() end, { remap = false }) require('lsp_signature').setup({ floating_window = true, hint_enable = false, toggle_key = '', toggle_key_flip_floatwin_setting = true, select_signature_key = '', }) end }