Setting up Vim for JavaScript development

Configuring Vim and comparisons of JavaScript-specific Vim plugins

Published on January 09, 2016

Before you read any of this I will caveat with the fact that Vim is perfectly adequate for modern JavaScript development without plugins. The Vim runtime comes with syntax highlighting and omni-completion already, and you can even configure Vim as a task runner without plugins.

This is geared towards people who already know how to use Vim. E.g., if you understand minutiae like the difference between set autoindent and set indentexpr, and have found neither to your liking, then read on. Otherwise, I highly advise you learn a little more about what the standard Vim runtime comes with.

Installing plugins

I recommend using vim-plug to install plugins. It’s simple, works on both Vim and Neovim, and can perform operations asynchronously.

Syntax

Vim comes with a JavaScript syntax file. It is automatically loaded when you open up a JavaScript file (where the value of filetype is javascript).

By default Vim doesn’t automatically detect filetypes, so if you aren’t using vim-plug (for which the call to call plug#end() enables this), you’ll need to enable it manually by adding this to your vimrc:

filetype plugin indent on

This will enable filetype detection, running filetype specific plugins, and loading filetype specific indentation settings.

You may also need to enable syntax highlighting in your vimrc file if you’re not using vim-plug. To manually enable syntax highlighting, add the following line to your vimrc file:

syntax enable

Plugins that provide better syntax support

The standard JavaScript syntax highlighting is typically adequate, but you may want more features such as ES2015 support, or better distinguishing of keywords.

There are quite a few options:

  • pangloss/vim-javascript
    • Includes custom indent settings. These indent settings are also the ones included in the Vim default runtime now, and the one in the plugin may be trailing what comes with Vim (i.e., Vim has a newer version of the same indent file!)
    • Adds special concealing symbols so to make your code pretty at a glance
    • Last updated April 2016 with additional ES2015 support (better arrow function and highlighting among other things) and some regex performance updates.
  • sheerun/vim-polyglot
    • This is a plugin that bundles a bunch of language syntax plugins into one. It includes pangloss/vim-javascript at the latest version, as well as some other plugins like mxw/vim-jsx. Worth checking out if you don’t want to maintain syntax plugins on your own, but you should double-check to make sure you aren’t manually adding the bundled plugins outside of the pack (resulting in having the plugin twice).
  • jelera/vim-javascript-syntax
    • Does not include custom indent settings (not that you need them…)
    • Updated about once a month according to the GitHub contributors graph
  • othree/yajs.vim
    • This is a fork of jelera/vim-javascript-syntax
    • Does not include custom indent settings (again, you might not need one)
    • Updated very often to keep in line with ES specifications
  • bigfish/vim-js-context-coloring
    • This is an interesting new method of syntax highlighting. It picks out function scopes from your program by running it through a node.js binary that runs a JavaScript parser and assigns a color to the scope. Things within that scope are assigned a color. Because it requires in-depth parsing of your code, it may not color your code when it is incomplete (i.e., the syntax is not yet valid).
    • This syntax plugin can be used in combination with any of the above, and you can toggle it on and off.

With the exception of vim-js-context-coloring, all of these provide ES2015 (ES6) and JSDoc support to varying degrees, which Vim lacks by default. I personally use and recommend pangloss/vim-javascript purely because it has started active development again. I have not experienced any issues since switching to this from othree/yajs.vim.

othree/yajs.vim with es.next.syntax.vim might better suit your needs if you use ES2016 (ES7). The author also writes a few other plugins that can be used in conjunction with it (although they may work with the other syntaxes, too, depending on the syntax groups provided). I’ve used this plugin extensively in place of the default syntax and haven’t had any problems.

Using vim-plug, I recommend installing the plugin like this:

Plug 'othree/yajs.vim', { 'for': 'javascript' }

The additional requirement at the end makes sure the syntax plugin is loaded in a Vim autocommand based on filetype detection (as opposed to relying on Vim’s runtimepath based sourcing mechanism. This way the main Vim syntax plugin will have already run, and the plugin’s syntax will override it.

Plugins that provide better indentation support

Vim’s bundled JavaScript indent may be enough for you, especially if you use a strictly C-style whitespace (Vim’s C-style indent options is even called cindent). Vim, as of August 26, 2016, comes with a modified version of the pangloss/vim-javascript indent rules that does not include the reformatting gq action. This means 99% of people probably won’t need to change their indent file.

Some notable options in this case are:

  • pangloss/vim-javascript
    • Using this entire syntax plugin will provide you with a more JavaScript-y indent for things like switch/case and multi-line var declarations.
    • This indent plugin has a side-effect in that it also changes the format expression — that is, if you highlight a block and use gq to reformat it, it will re-indent the code using this plugin as well.
    • The Vim runtime (stuff that comes with Vim) may include a newer version of this indent file that does not have the modified gq.
  • gavocanov/vim-js-indent
    • I don’t recommend this one, but I’ve listed it here because you’ll probably find it and have questions. The author no longer uses it, so it should not be considered.
    • This is the indent portion of pangloss/vim-javascript, ripped out into its own plugin.
    • There are modifications to it, diverging it from pangloss/vim-javascript, notably support for the syntax group names in othree/yajs.vim. That helps it pick out keywords and when you are inside comments if you are using othree/yajs.vim.
    • The format expression from the pangloss plugin has been removed.
  • itspriddle/vim-javascript-indent
  • jiangmiao/simple-javascript-indenter
    • An indent plugin with an option for alignment under functions.
  • jason0x43/vim-js-indent
    • This is a somewhat new (last updated 2014, as of this writing) indent script that also has some TypeScript support. It is based on an older indent script by Tye Zdrojewski and the default JS-in-HTML indent logic that comes with Vim.
    • The indent logic starts with normal cindent styles and adds special cases for comments, JSDoc, arrays, and switch/case.

Your best bet is to stick with what comes with Vim by default, and only try out the others if your coding style does not match what the rest of the JavaScript community is converging towards. If there’s a quirk, it is probably better to submit an issue with the pangloss/vim-javascript repo.

During JavaScript development you may find yourself editing a lot of other filetypes that plain JavaScript.

es.next / ES2016 / ES7 support

othree has a syntax plugin that provides support for planned, but not-yet-final EcmaScript features: othree/es.next.syntax.vim. I personally don’t use it since I currently stick to the ES2015 feature set at most.

JSX support for React

If you write React and use its optional JSX syntax, adding the following plugin will provide you with syntax highlighting for those inline XML-nodes:

Plug 'mxw/vim-jsx'

This plugin requires a JavaScript syntax plugin from above to define certain JavaScript regions. It specifically mentions pangloss’ extension in the docs but actually supports any of them.

JSON

A lot of things use JSON for configuration, so I recommend elzr/vim-json for that. Check out its options, though; I don’t like some of the defaults so I turn them off, but you might want them. Install with:

Plug 'elzr/vim-json'

JSDoc syntax highlighting

If you document using JSDoc, all of the syntax plugins above support JSDoc highlighting already. There’s a plugin called othree/jsdoc-syntax.vim that pulls that support out of othree/yajs.vim, but it is only for adding JSDoc support to other languages like TypeScript.

JSDoc auto-snippets

The plugin heavenshell/vim-jsdoc can automatically insert JSDoc comments for you if your cursor is on a function definition. It’ll check for the function name, arguments, and add the doc-block comment for you.

I use this plugin quite often (I actually have a fork of it with some additions but hopefully they get merged into the upstream).

jQuery plugins

Files named jquery.*.js are typically jQuery plugins. There’s a specific syntax highlighting plugin for such files: itspriddle/vim-jquery, but it’s pretty old and you’ll have better support combining an up-to-date syntax plugin with the JavaScript libraries plugin in the next section.

JavaScript libraries

othree has a syntax plugin, othree/javascript-libraries-syntax.vim, that supports special highlighting of functions and keywords for various libraries such as jQuery, lodash, React, Handlebars, Chai, etc. For an extensive list, see the README at the plugin’s homepage.

I personally do use this plugin.

Code completion for Vim

This is what you might know as “autocomplete” or “Intellisense.”

Vim has a built-in completion feature that you can trigger by just using <C-X> (that’s the control-x key) and then another control-key sequence like <C-F> to complete filenames or <C-O> to trigger omni-completion. You should try it before trying to install a bunch of plugins to modify this behavior.

Omni completion

I won’t go into too much detail about this since it isn’t JavaScript specific.

Vim includes basic code completion built in. See this wikia article for information on how to use that. The gist is that the completion system will run a function, the omnifunc, to populate autocompletion pop-up with results.

To use the default completion function, you may need to add this to your .vimrc file:

autocmd FileType javascript setlocal omnifunc=javascriptcomplete#CompleteJS

You might not need to add it if the <C-X><C-O> trigger already works for you, it depends on what version of Vim and the Vim runtime you have.

For even better completion, consider using a plugin like Shougo/neocomplete.vim or Valloric/YouCompleteMe. On Neovim, an option is Shougo/deoplete.nvim.
These plugins will add features like automatically popping up the completion menu, caching of keywords, and integration with other sources of completion than what’s in the current Vim buffer (allowing for multiple omnifuncs).

For portability across my systems without needing a recompile, I use Shougo/neocomplete.vim. Shougo’s plugins and YouCompleteMe both offer roughly the same feature set, though, so whatever might be missing in one can probably be configured into it.

With Shougo/neocomplete.vim, using multiple sources of completion can be done by providing a list of function names like so:

  let g:neocomplete#sources#omni#functions.javascript = [
        \   'jspc#omni',
        \   'tern#Complete',
        \ ]

I won’t guarantee that the above works, since the plugins get updated regularly and neocomplete does not like omnifuncs that move the cursor around when the function is executed.

Extended omni-completion

The plugin 1995eaton/vim-better-javascript-completion provides a somewhat up-to-date JavaScript with HTML5 methods (e.g. localStorage and canvas methods).

This plugin creates a new omni-completion function, js#CompleteJS, and replaces your current JS omnifunc with that. That means you’ll have to use a completion plugin or write some VimL yourself if you want to use it in conjunction with another omnifunc like TernJS in the next section.

Code-analysis based completion via TernJS

TernJS is kind of like IntelliSense if you’ve ever used Visual Studio, or like the autocompletion for many very robust IDEs. It parses your code and extracts various symbols, like function names, variable names, and values.

The official vim plugin can also show you function signatures (what parameters parameters it expects) and can extract values from related files (e.g. CommonJS require()‘d files) if you configure it to do so (via a .tern-project).

The completion is provided to Vim’s auto-completion engine via an omnifunc, tern#Complete. Again, you’ll have to setup your omnifunc appropriately to use TernJS results instead of the default omni-completion results.

Installing via vim-plug, which can run additional commands before plugin installation, is done like this:

Plug 'ternjs/tern_for_vim', { 'do': 'npm install' }

This will install its npm dependencies for you (Tern runs a node-based analyzer in the background while you’re editing).

I use this plugin with many of its extra features turned off, just keeping the completion.

Function parameter completion

othree has a plugin called JavaScript Parameter Complete that detects when you’re inside a function argument and provides some common autocomplete suggestions for it. This is not a feature that TernJS provides, since Tern only adds existing symbols. For example, if you’re writing an event listener, it’ll suggest things like click, and mouseover for you. You can see all the suggestions it provides in its GitHub source. Install the plugin via:

Plug 'othree/jspc.vim'

On load, the jspc.vim plugin automatically detects whatever omnifunc you already have set as your default. It wraps it with the parameter completion, and falls back to your default if you are not in a parameter completion. Because of this you should specify jspc#omni instead of whatever your default completion is (typically javascriptcomplete#CompleteJS).

Code navigation

Jumping between CommonJS modules

The plugin moll/vim-node adds keybindings like for jumping to files in your CommonJS require statements.

Plug 'moll/vim-node'

CTags - Symbol based navigation

CTags are lists of all symbols in your projects (function names, variable names, filenames, etc.). Vim provides ctag support by default, with keybindings to jump to declarations and definitions, and there are a slew of plugins (e.g. ludovicchabant/vim-gutentags) that can auto-generate the tags file for you. Using majutsushi/tagbar, Shougo/unite.vim, ctrlpvim/ctrlp.vim, or a bunch of other plugins (and plugins that work with them — plugin-plugins), you can browse through those tags.

Of particular note on the generation side is ramitos/jsctags, which will generate ctags using TernJS.

Personally, I find ctags too annoying to use since the files need to be regenerated to search them (except with majutsushi/tagbar which runs ctags on every open, but only for the current file). Usually just using git grep or the_silver_searcher is adequate, and there are plugins for those, too (out of scope for this article).

Linting

While there are actually JSHint, JSLint, eslint, etc. runners for Vim, for your own sanity just use scrooloose/syntastic. It supports a variety of syntax checkers, but you may need to install them first. For example, for eslint support, which is the standard these days, npm install -g eslint first. Refer to syntastic’s wiki page on configuring various JavaScript linters.

Syntastic’s pitfalls are that it is large (it is essentially a linter framework for Vim) and it doesn’t run asynchronously (doesn’t mean it is slow though — depends on the speed of the lint program).

You could alternatively use Vim’s built-in makeprg, which can run any program and output the results to Vim, but you miss out on things like using multiple makeprgs at a time (e.g. JSCS, eslint, and the flow type checker at once) and grouping results. Syntastic actually uses makeprg under the covers, so besides the minimal overhead of configuring some variables it really isn’t any slower.

There’s osyo-manga/vim-watchdogs, which runs linters asynchronously, but the docs are only in Japanese. The plugins Shougo/vimproc.vim and tpope/vim-dispatch can run any tool async, but they aren’t easily configurable as lint-runners. If you follow modern JS design patterns, your JavaScript files should ideally be small modules so running linters asynchronously won’t provide noticeable benefit.

If you’re running Neovim, neomake is an option that’s gaining popularity. It makes full use of Neovim’s asynchronous job support.

Formatting

Vim has a built-in re-formatter for whitespace. Visually select some text and use the = key to re-indent all of it.

For minified JS, there’s a vim plugin, vim-jsbeautify, that can run your code through jsbeautifier for you. I personally don’t use this and prefer to go to the jsbeautifier website, copying and pasting it there if I need to un-minify something.

There’s also Chiel92/vim-autoformat, which supports formatters for many different languages. Of note is jscs and js-beautifier support.

My Vim setup

You can dig through my Vim configuration on GitHub. Please note that this article was written a while ago, and since then I’ve completely switched over to Neovim so a lot of the configuration may not apply to you. The plugins I use are all in the main vimrc file, and their configurations are interspersed into plugin/, ftplugin/, and after/*/ to cope with the order in which Vim loads files.