After having used YouCompleteMe, I finally decided to give one of the Language Server Protocol (LSP) implementations a spin. As an LLVM developer I’ve been following clangd’s development and wanted to try it out.

At the time of writing, several LSP implementations exist for Vim:

Setting up vim-lsp

I decided to go with vim-lsp because it’s asyncronous, written in vimscript and easy to setup. The plugin has a single dependency: async.vim which provides an abstraction layer between async job control in vim8 and neovim. If you’re using vim-plug to manage your plugins, you just need to add the following two entries to your .vimrc:

Plug 'prabirshrestha/async.vim'
Plug 'prabirshrestha/vim-lsp'

Auto-complete

Like many people, my motivation for having language support is having sensible auto-completion. There are several (asynchronous) plugins available that help with this, but for me Vim’s built-in omni-completion is more than sufficient. I do like having the menu show up when pressing TAB, for which I use VimCompletesMe.

Plug 'ajh17/vimcompletesme'

See the section below on how to hook up vim-lsp with omni-completion.

Other LSP features

The language server protocol has several features, but some server and client implmenetations only support part of the commands. Below is a table of features supported by the protocol, clangd and the vim-lsp plugin.

Editor feature lsp clangd vim-lsp
Formatting Yes Yes Yes
Completion Yes Yes Yes
Diagnostics Yes Yes Yes
Fix-its Yes Yes Yes
Go to Definition Yes Yes Yes
Signature Help Yes Yes No
Document Highlights Yes Yes No
Rename Yes Yes Yes
Source hover Yes Yes Yes
Find References Yes No Yes
Code Lens Yes No No
Document Symbols Yes No Yes
Workspace Symbols Yes No Yes

For an up-to-date status on the different features, please refer to the clangd and vim-lsp documentation:

Configure vim to use clangd

Finally, below is the configuration for hooking up clangd with vim-lsp. If the clangd binary exists, we register the LSP server and tell vim to use it for omni-completion.

if executable('clangd')
    augroup lsp_clangd
        autocmd!
        autocmd User lsp_setup call lsp#register_server({
                    \ 'name': 'clangd',
                    \ 'cmd': {server_info->['clangd']},
                    \ 'whitelist': ['c', 'cpp', 'objc', 'objcpp'],
                    \ })
        autocmd FileType c setlocal omnifunc=lsp#complete
        autocmd FileType cpp setlocal omnifunc=lsp#complete
        autocmd FileType objc setlocal omnifunc=lsp#complete
        autocmd FileType objcpp setlocal omnifunc=lsp#complete
    augroup end
endif

JSON Compilation Database

Like many tools, clangd relies on the presence of a JSON compilation database. This file is usually called compile_commands.json and lives in the build directory. By default, clangd will look for this file in the current directory and parent paths of each source file. Depending on the layout of your project, you might have to symlink it to the parent path of your current working directory, but in most cases it will just work out of the box.