Skip to main content

The LSP Revolution

Remember the days when you had to look for plugins for your editor to support your favourite programming language? Or even the language that isn’t your favourite, but which for some reason you need to write in? Well in case you didn’t notice, those days are gone. They aren’t “long gone,” but the are gone. I thought they weren’t gone, but they are. Gone. For real. Because there’s LSP.

What started as an interface between VS Code and completion engines at Microsoft, became the godsend for all of us that want to declare “dotfile bankruptcy” every couple of months. There are many implementations of LSP for many editors, and so far I tried two for neovim (coc and nvim lsp) and I’m amazed. I was a bit skeptical at first, because LSP runs on Node, and installing a JS runtime as a de facto dependency for vim feels so wrong, but… it works! I no longer need multiple plugins with complex configs! The future is here! Just look! I install two plugins for LSP in latest neovim:

Plug 'neovim/nvim-lspconfig'
Plug 'nvim-lua/completion-nvim'

which load support for LSP, which I can then configure:

autocmd BufEnter * :lua require'completion'.on_attach()

" below we configure which language servers
" we want to support, a more comprehensive
" list is here:
" https://github.com/neovim/nvim-lspconfig#configurations
autocmd BufEnter * :lua require'completion'.on_attach()

lua <<EOF
require'lspconfig'.bashls.setup { }
require'lspconfig'.ccls.setup { }
require'lspconfig'.dockerls.setup { }
require'lspconfig'.gopls.setup { }
require'lspconfig'.hls.setup { }
require'lspconfig'.html.setup { }
require'lspconfig'.jsonls.setup { }
require'lspconfig'.solargraph.setup { }
require'lspconfig'.vimls.setup { }
require'lspconfig'.yamlls.setup { }
EOF

nnoremap <silent> <c-]> <cmd>lua vim.lsp.buf.definition()<CR>
nnoremap <silent> K     <cmd>lua vim.lsp.buf.hover()<CR>
nnoremap <silent> gD    <cmd>lua vim.lsp.buf.implementation()<CR>
nnoremap <silent> <c-k> <cmd>lua vim.lsp.buf.signature_help()<CR>
nnoremap <silent> 1gD   <cmd>lua vim.lsp.buf.type_definition()<CR>
nnoremap <silent> gr    <cmd>lua vim.lsp.buf.references()<CR>
nnoremap <silent> g0    <cmd>lua vim.lsp.buf.document_symbol()<CR>
nnoremap <silent> gW    <cmd>lua vim.lsp.buf.workspace_symbol()<CR>
nnoremap <silent> gd    <cmd>lua vim.lsp.buf.declaration()<CR>

and we’re done!

OH, complicated? A bit, but the shortcuts above will work for every language supported by LSP, which essentially means every language. There are even language servers for Bash (hello, shellcheck) and YAML (one more evidence for the existence of “YAML programmers” ☝️). And once nvim detects that you’re loading a file that is supported by LSP, it will even install the appropriate server for you (well, kinda, sometimes, it’s a work in progress). Just today I had to dive into a large-ish project in Ruby, which I never worked with, and nvim told me to gem install solargraph and I was good to go. Code navigation, documentation pop-ups, jumping to definitions like crazy, it all just worked without me needing to configure plugins for Ruby.

So yeah, LSP is indeed amazing. It works on Emacs, too, and probably a bunch of other editors (not that anyone cares).

You should start using it TODAY, and shave off hundreds of lines of your init.vim or init.el.

Thanks to Gergő for introducing me to nvim-lspconfig 🙇‍♂️