VSCode as a Vim editor

September 08, 2018

In 2013 I started to use Vim as my primary editor, the journey hasn’t been without frustration, but in general, I really enjoyed using it. It took me sometimes, but I found a setup that worked well for me from autocompletion to code navigation. What I love the most is Vim motions, I would not trade that feature for something else.

So, if everything was fine why did you start using VSCode?

I got mainly interested by VSCode for its debugging and refactoring features. I got used to debugging JavaScript or CSS in the browser’s dev tools, but I never found the right spot for debugging Node apps. With VSCode it is possible to debug directly in the editor, to add breakpoints or investigate the stack trace.

But, as usual, the first thing I do when I start to use a new code editor to install the Vim plugin.

VSCode Vim plugin

In a nutshell, VSCodeVim is really good. It is by far the best Vim integration I have tried outside Vim (or NeoVim) itself. It’s fast, includes plugins I love (easymotion, sneak, surround), and supports all motions and most shortcuts I used to use.

To get there I had to tweak the settings to be able to remap commands, but the great thing with this plugin is that we can remap loads of it!

For instance, I use jk to switch between insert and normal mode.

{
  "vim.insertModeKeyBindings": [
    {
      "before": ["j", "k"],
      "after": ["<Esc>"]
    }
  ]
}

You need to define that in User Settings. (Search for Preferences: Open User Settings. in the command palette (⇧⌘P).)

It is pretty straightforward, you define what mapping you want to use in before and what output you want to get into after.

I rely a lot on EasyMotion to move around (jump to a different line or a character) but I found the default setting quite annoying: I have a double press the leader key, instead of once in Vim, and then the action (for instance j to go to a line below). But, it is possible to remap it!

{
  "vim.normalModeKeyBindingsNonRecursive": [
    {
      "before": ["<leader>", "j"],
      "after": ["<leader>", "<leader>", "j"]
    }
  ]
}

You can remap not only key bindings but also execute commands. For instance to toggle a bookmark using ma:

{
  "vim.normalModeKeyBindingsNonRecursive": [
    {
      "before": ["m", "a"],
      "after": [],
      "commands": [
        {
          "command": "bookmarks.toggle",
          "args": []
        }
      ]
    }
  ]
}

I have been able to remap bindings to match my Vim memory muscles. I felt at home when starting to use VSCode.

You can find my VSCodeVim settings on this Gist.

Refactoring

VSCode has made some refactoring tasks easier than what I used to in Vim. One of my favourites is renaming a variable by pressing f2. All occurence will be updated in the project. The same happens when you rename a file or move it to another folder, its path is updated where it is used.

More recently I discovered that we could easily transform arrow function with implicit return:

const add = (a, b) => a + b;

Put the curson on (, then press ctrl+shift+r -> add braces to arrow function. It will convert it to:

const add = (a, b) => {
  return a + b;
};

Super convenient when you need to debug a function.

In general, I find it more comfortable to refactor in VSCode, the multiple cursors works better than on Vim, and I can still use visual block mode when I need it.

Debugging

So far, I have only used the “attach to process” debug mode. When you run node (or nodemon) with the —inspect flag you can attach the process in VSCode to debug the application.

In launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "attach",
      "name": "dev:server inspect",
      "port": 5858,
      "restart": true,
      "protocol": "inspector"
    }
  ]
}

It’s an excellent way of debugging Node.js apps, so you don’t need to open a Chrome dev tools to add breakpoints and put debugger statement in your code.

After some tuning, navigating in VSCode looks familiar to Vim:

  • Search a file using the fuzzy finder à la CtrlP.
  • Use the command palette to run commands when I don’t think it is necessary to memorise a key binding. For instance, I use Open In GitHub plugin to open the GitHub page in the browser to open a pull request. Instead of mapping it on a crazy multi-combo binding, I find it easier to use the command palette and search for the command.
  • Jump between windows using ctrl + hjkl. (More about it in “What I miss from Vim” section.)
  • Go to definition using gf. (In Vim I used Ternjs to have this feature, “60% of the time, it works every time”.)
  • Use the code outline to jump between symbols in a file using cmd+shift+o. I’m in love with this feature.
  • Switch between tabs using gt (next tab) or gT (previous tab). ctrl + tab comes handy when I have too many files open, which happen more often to me because of the poor window management support on VSCode.
  • Jump to the next occurrence of the variable under the cursor using *.
  • Jump to the next change using <leader>n (next change) or <leader>p (previous change).
  • And of course, my beloved Vim motions.

Language support

The VSCode community is active, so I haven’t encountered a language that wasn’t supported: JavaScript (+JSX), TypeScript, Sass, Elm, Elixir/Phoenix, or Haskell.

Autocompletion is right most of the time, except for CSS or Sass files where it struggles to suggest class names defined in a template file. It also has some issues to autocomplete snippets. If I type too fast, I will need to close and reopen the suggestion box to be able to run a snippet.

What I miss from Vim

Obviously, everything is not perfect. The main thing I miss from Vim is the window management, in VSCode the current support is really basic.

In Vim, it is possible to open several files under the same tab, so it is straightforward to create different spaces with related files and terminal open. For instance, you can open a tab containing all files related to a component.

| TAB A |
----------------------------------------------------------------------
Module.js     | Module.test.js     | Terminal that runs
              |                    | Module.test.js
              |--------------------|
              | Module.style.scss  |
              |                    |

In VSCode, tabs are all independent, so even if I can split the screen in two, I can’t switch tabs to change both files at the same time. So, I need to jump back and forth between the files to switch the tabs accordingly. Annoying.

| TAB Module.js | TAB Module.test.js | TAB Terminal
---------------------------------------------------------------------------

It creates some frustration when I try to jump quickly between files and terminals since I need to remember where are the related files are and keep things in sync manually.

What’s next?

I will keep using VSCode and haven’t planned to go back to my previous setup iTerm + NeoVim. It has (almost) everything I’m looking for: it is fast, it has good language, and autocompletion support, a built-in terminal, and I can still use Vim in it. A nice thing is that most of my colleagues use it as well, so we can share settings in a project or do remote pair programming in VSCode directly, which is fantastic.

Feel free to send feedback or ask questions on Twitter.



Written by Antoine Lehurt (a.k.a kewah). Front-end developer living in Stockholm and currently working at Acast.