LSP in Vim with the LSC Plugin
The emergence of the Language Server Protocol (LSP) and asynchronous job support has given rise to a myriad of code completion frameworks for the Vim and Neovim editors.
So many choices is both a benefit and curse; the benefit being that the savvy Vim user can craft a configuration specific to their need, the curse, especially for a novice, is the classic paradox of choice, where to start and what to choose?
Note, my choices may not necessarily suit you, but they do offer a starting point for users wishing to use LSP-based code completion, and other advanced language-aware actions, in Vim.
Feel free to refer to my dotfiles to view my particular LSP configuration.
A modern version of Vim or Neovim that supports asynchronous job control is required. That means Vim 8 or any modern version of Neovim.
Preferably, a very recent version of Vim, version 8.1.2050 or Neovim 0.4.0 at the time of this writing, is strongly recommended since the LSP hover operation of the LSC plugin can use either Vim’s Popup Window or Neovim’s Floating Window functionality if available.
I also recommend installing and updating Vim, or Neovim, using Homebrew on macOS and Linux.
For example, to install Neovim using Brew:
brew install neovim
And to update Neovim:
brew upgrade neovim
Language Server Protocol (LSP)
Created by Microsoft, LSP was originally developed for the Visual Studio Code editor to decouple code editing and presentation from language-specific actions.
Language-specific actions, such as: auto-completion, hovering and navigation, that used to be the purview of heavyweight IDEs are now available to LSP-capable editors when associated with an appropriate LSP-compliant language server. LSP transfers the responsibility of such language-specific actions out of the editor to a vendor-agnostic language server that runs as a separate background process on the host.
As an open JSON-RPC-based standard, LSP now has multi-vendor support which has led to the development of numerous language clients and servers.
Vim Omni Completion
The 2006 release of Vim 7 saw the introduction a new form of completion,
omni-completion. Omni-completion, orthogonal to existing forms of completion
dictionary completion, is performed by the defined
omnifunc and will usually offer filetype-specific completions.
<Control-x><Control-o>, the intelligence of omni-completion depends
on the sophistication of the
omnifunc in use. Vim ships with set of
rudimentary omni completion implementations. Users can install more advanced
omni completion plugins, such as: Tern
for C or C++, to improve the quality of such completions.
Nonetheless, there are a few issues with omni-completion:
completion is a synchronous operation, invoking omni-completion will block the editor until the operation is concluded
omni-completion plugins needs to be coded and maintained specifically for Vim
However, with LSP-based completion, Vim can leverage and use the same language servers used by Visual Studio Code. One can be confident that the major language servers are actively developed and maintained. On the other hand, some omni-completion plugins, such as Tern for Vim, are no longer maintained.
Also, LSP-based solutions can leverage Vim & Neovim’s asynchronous job control
to not block the editor whilst editing. Auto-completion, where completion
candidates are displayed as one types, should be asynchronous otherwise
editing may be painful due to stalls arising from the synchronous invocation of
LSP offers more than just code completion; a full-featured language server can provide context-aware navigation, code refactoring and hovering tooltips, among other capabilities.
My LSP configuration, documented below, readily allows for both non-blocking auto-completion or manually invoked omni-completion using the same language servers in both cases. You choose which type of completion suits.
Vim Completion Frameworks and LSP-clients
A Vim completion framework is responsible for collating completion candidates and displaying those choices to the user. Advanced completion frameworks often operate, by default, in asynchronous auto-completion mode.
An LSP client on the other-hand is editor tooling that supports communication with a language server employing the Language Server Protocol. As of the time of this post, October 2019, neither Vim nor Neovim provide out-of-the-box support for LSP. However, a future version of Neovim will provide LSP support as noted in this pull request. In the meantime there are multiple Vim plugins that do provide LSP support.
Certain code completions frameworks also include direct LSP support whilst others delegate such duties to a separate LSP-client plugin.
Notable code completion and LSP-client plugins for Vim and Neovim:
YouCompleteMe, a monolithic code completion engine that predates LSP and asynchronous job control in Vim, both of which have now been incorporated. For many years this was the go to code completion plugin for Vim, nowadays there are lighter weight alternatives.
deoplete by Shogo, the first asynchronous code completion framework for Neovim, but which now also supports Vim. Completion candidates are gathered from deoplete-compatible sources; note, LSP-based results require integration with the LanguageClient-neovim plugin.
ncm2 by roxma, is another asynchronous code completion framework for Vim and Neovim. Conceptually similar to deoplete, completion candidates are gathered from ncm2-compatible sources; however, LSP-based candidates require integration with either the LanguageClient-neovim or ncm2-vim-lsp plugins.
Coc, a contemporary code completion framework for Neovim and Vim with inbuilt LSP support. Being Typescript-based allows Coc to leverage existing plugins used by Visual Studio Code. Somewhat against the norm, Coc operates its own configuration and extension system.
Completor, an asynchronous completion framework for Vim, and now also Neovim-compatible, with inbuilt LSP support. Leaner and more accessible than the plugins mentioned above.
LanguageClient-neovim, an LSP client commonly used in combination with an asynchronous completion framework such as deoplete or ncm2. The name implies Neovim-only support, but nowadays it also supports Vim.
asyncomplete.vim, an ayschronous auto-completion framework written in Vimscript that supports both Vim and Neovim’s asynchronous job control APIs. Like deoplete and ncm2, LSP-based candidates require integration with an LSP-client plugin, this time with the vim-lsp plugin.
ALE, primarily an asynchronous linting and fixing plugin, but now extended to support LSP. Language servers can provide linting, hence the reason why ALE integrated LSP. ALE now includes LSP-based code completion in addition to other LSP functionality.
MUcomplete, a minimalist auto-completion plugin that leverages Vim’s existing completion infrastructure. This plugin does not support asynchronous operation.
Supertab, a tab completion plugin for Vim. This plugin simply maps the TAB key to Vim’s existing completion kinds.
VimCompletesMe, another tab completion plugin for Vim, similar to Supertab but simpler.
The LSC Plugin
After much trialling I chose LSC due to the following characteristics of the plugin:
LSC is a combination LSP-client and completion plugin that stands alone, there is no multi-plugin dance required
Implemented in pure Vimscript, this eases installation and avoids certain kinds of upgrade pain that may be experienced by Python-based alternatives
Compatible with both Vim and Neovim’s differing asynchronous job control APIs
Light in weight, less than three thousand lines of Vimscript; LSC augments Vim, it does not take over unlike certain other plugins
Pristine completions, candidates will only be sourced from the language server, there will be no mixing of candidates from other completion sources
Straightforward and concise
Referenced language servers are installed and maintained externally, similar to how the ALE plugin uses external linters and fixers
Simple configuration option to select either asynchronous auto-completion or synchronous manual completion, once the
omnifuncoption is appropriately set
Auto-completion, if enabled, will only apply to filetypes that are associated with a language server
find all referencesoperation will populate the quickfix list; no custom UIs in contrast to a few other plugins
The simplicity LSC may not suit you, especially if you wish to combine
completion candidates from multiple sources, say LSP-based candidates with
keyword-in-file candidates. In my case I already have
that allow easy switching from one completion kind to
I augment LSC with the VimCompletesMe plugin since I like using the TAB key to scroll through completion candidates, among other benefits of that plugin.
LSC Installation & Configuration
If using the vim-plug plugin manager,
add the following to your
Plug 'natebosch/vim-lsc' Plug 'ajh17/VimCompletesMe'
:PlugInstall to install the plugins. Please change the notation
appropriately if using an alternate plugin manager for Vim.
For a given filetype the LSC plugin will take care of launching, communicating
and shutting down the named language server
command. The specified language
servers, listed above, will be discussed in greater detail in the following Ruby
In addition to LSP-based auto-completion, I define and use the following four LSP actions:
|LSP Action||LSC Command||Vim Mapping|
|Go to definition for the symbol under the cursor||
|Find all references for the symbol under the cursor||
|Rename the symbol under the cursor and all references||
|Show hover tooltip for the symbol under the cursor||
I strongly recommend the following
completeopt setting when using
If you do not care for auto-completion but do wish to use LSP-based
<Control-x><Control-o>, then add the following to your
let g:lsc_enable_autocomplete = v:false
I use the ALE plugin for linting and
fixing, specifically StandardRB for
LSC diagnostics. However, if you wish to use LSP-based real-time linting, and
your language server supports it, then specify
let g:lsc_enable_diagnostics =
Lastly, I configure LSC to suppress all client/server messages; by default the LSC plugin is a little too chatty with regard to displaying all messages, even when they are not that useful.
Whilst debugging a recalcitrant language server please do enable LSC diagnostics.
Ruby Language Server
Solargraph is the prime LSP-compliant language server for Ruby.
Install Solargraph with the following command:
gem install solargraph
For LSC to function please ensure
solargraph is available in your
The intelligence of Solargraph, when operating in a
projects, can be improved by running following command in the project’s base
Solargraph will now use the documentation from the project’s Gems for improved completions.
Similarly, the quality of Solargraph completions will be
further enhanced for Rails
projects by also copying this
your project, I copied that file into the
Lastly, Solargraph is a still maturing technology, so please install updates when they become available.
somewhat buggy; for example the find all references action regularly failed
with my React projects.
Interestingly, Visual Studio Code actually uses editor-provided TypeScript
TypeScript language actions. Unfortunately, Microsoft’s stand-alone TypeScript
tsserver, is not LSP-compliant, but it does provide the core
capabilities needed by an LSP client.
Thankfully TypeFox does provide a
the Language Server Protocol with the TypeScript Language
Server package. This
Install the TypeScript Language Server with the following command:
npm install -g typescript-language-server
For LSC to function please ensure
typescript-language-server is available in
Available language servers for certain prevalent programming languages:
|Python||Python Language Server||
|Rust||Rust Language Server||
Note, I have not tested these language servers personally.
Which plugin(s) one ultimately uses is not that interesting, what is genuinely game-changing are the advanced editing capabilities that LSP provides.
This Language Server Protocol Vim screencast, by Greg Hurrell, is pertinent with respect to that point:
Hopefully this post provides enough detail to start your LSP journey in Vim.