wanderings/content/posts/vim-language-servers.md

6.3 KiB

+++ title = "Language Server Protocols in Vim" date = 2020-09-16 tags = ["vim"] +++ Language Server Protocol aka LSP is an open source initiative of ... Microsoft. Each "language server" defines every detail of a programming language's syntax and grammar, like its keywords, and how you put them together. This knowledge base is in turn used by editor helpers who can for instance lint your code or suggest completion, a field of features labeled "Intellisense".

Since Vim 8, programmers may run asynchronous code in vimscript. This feature is leveraged in some plugins that use LSP to lint or suggest while you type. Let's name vim-lsp, coc.vim and vim-ale.

Vim plugins on your own

It is worth noting that coc.vim provides a language server installer (vim-lsp also has vim-lsp-settings that should do the job, although I was not able to run it on my system). It is very likely that your operating system chips some, at least if it's a linux. But if you already have Vim running on your OS, it should be quite simple to use one of the formers with a plugin manager. If you don't have any plugin manager, be advised that Vim 8 ships its own plugin system ; you just need to unpack the plugin's main directory in ~/.vim/pack/PACK_NAME/start/. PACK_NAME can be anything you want, and its subdirectory start can be filled with as many plugins as you please. In this case, you could do for instance :

$ mkdir -p ~/.vim/pack/LSP/start
$ cd ~/.vim/pack/LSP/start
$ git clone https://github.com/neoclide/coc.nvim

And that's it ! (If you don't have git neither a terminal but still have Vim, you can still go at coc.vim and manually unpack the latest version's tarball you Vim's config dir - pack should lie next to ftplugin et al).

Vim plugins in your package manager

You might prefer to use your system's package manager to install plugins, so that they get upgraded with the rest of the system. In this case you could do for instance in Arch linux :

# pacman -S vim-ale

Once properly set up, vim-ale will lint the file you are editing in the background (using Vim 8 async processes). It will send all errors and warnings found while you type to the location window, a temporary "subwindow" that you can check back with :lw. See :help location-list-window to get more info on this feature. You can jump to the last diagnosed error with :ll, and navigate with :lla and :lne.

The nice bonus is the completion option. It lists all matching expressions with what you began typing, and as you scroll the list, Vim's preview-window pops up and gives a description of whatever you are coding - objects in python, functions in bash, etc.

I'm afraid vim-ale on its own doesn't know much about any language at all. You will have to feed it with a knowledge provider for each of the languages you care for. See the next chapter about this.

Meanwhile, I tweaked vim-ale to populate Vim's default completion sequence, remap completion shortcuts, and let the preview window be opened at the bottom with the following content into ~/.vim/plugin/ale.vim :

" vim-ale settings

" Enable ale completion where available.
" This setting must be set before ALE is loaded.
let g:ale_completion_enabled = 1
let g:ale_completion_autoimport = 1

" Lets ctrl-x ctrl-o open ale completion
set omnifunc=ale#completion#OmniFunc

" Remap tab, shift-tab and enter to useful completion shortcuts when
" completion already began :
inoremap <expr> <Tab>   pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
inoremap <expr> <cr>    pumvisible() ? "\<C-y>" : "\<cr>"

" Let preview window open on the bottom (J)
augroup previewWindowPosition
   au!
   autocmd BufWinEnter * call PreviewWindowPosition()
augroup END
function! PreviewWindowPosition()
   if &previewwindow
      wincmd J
   endif
endfunction

Installing language servers

You could install your favourite language's language server. For example under Arch :

# pacman -S bash-language-server

And then let Vim know about it. Create ~/.vim/plugin/language-servers.vim and paste :

" Support for bash
if executable('bash-language-server')
  augroup LspBash
    autocmd!
    autocmd User lsp_setup call lsp#register_server({
          \ 'name': 'bash-language-server',
          \ 'cmd': {server_info->[&shell, &shellcmdflag, 'bash-language-server start']},
          \ 'allowlist': ['sh'],
          \ })
  augroup END
endif

Arch users also get python, C/C++ (in the ccls package), and LaTeX language servers in community. I also found rls-git for Rust language server, HTML from Microsoft's VSCode, and javascript with typescript-language-server-bin in the AUR.

vim-lsp maintains a list of usual Vim LSP support scripts on its wiki. Once you've picked up your choice and installed it it's mostly a matter of copy and paste.

Well, all this sounded quite systematic ! But ... vim-ale also maintains a list of working linters here. And it turns out that before language servers, there was quite a bunch of linters doing the job all right. And vim-ale is able to run those "traditional" linters while you type. And they allow completion while you type. They also bear a smaller footprint. This discussion could go more into the details of each language support, but finally why go for the big overhead of this open source infrastructure ?

I ended up replacing language servers as follows :

language LSP implementation linter
bash bash-language-server shellcheck
C/C++ ? ccls
css vscode-css-languageserver-bin prettier
javascript typescript-language-server-bin typescript's tsserver
json vscode-json-language-server prettier
html vscode-html-languageserver-bin htmlhint
markdown ? mdl
python python-language-server autopep8
vala vala-language-server uncrustify
vimscript vim-language-server vint