- Vim In Visual Studio Code
- Visual Studio Code Vim Tabs
- Visual Studio Code Vim Plugin
- Visual Studio Code Vi Mode
- Vim Like Visual Studio Code
- Vim Shortcuts Visual Studio Code
- Vim Visual Studio Code Color Scheme
Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications. Visual Studio Code is free and available on your favorite platform. To install any of them, just hit Ctrl-Shift-P (Windows) inside Visual Studio code, type inst, select Extensions: Install Extension, then type vim. All three will come up, along with a few other things which are not Vim emulators for VSCode. None of those extensions are flawless.•••
I’ve been using vim for a while now, but the recent noise around Visual Studio Code had me curious, especially given some long-running frustrations with vim. I have a fairly comprehensive vim config, but it often feels fragile. vim-go, YouCompleteMe & ctags sit on top of vim to provide autocompletion: but re-compiling libraries, dealing with RPC issues and keeping it working when you just want to write code can be Not Fun. Adding proper autocompletion for additional languages—like Python and Ruby—is an exercise in patience (and spare time).
Thus, I decided to run with VS Code for a month to see whether I could live with it. I’ve read articles by those switching from editors like Atom, but nothing on a hard-swap from vim. To do this properly, I went all in:
- I aliased
codein my shell (bad habits and all that)
- Changed my shell’s
git difftoolto use
code --wait --diff $LOCAL $REMOTE.
Note that I still used vim keybindings via VSCodeVim. I can’t imagine programming another way.
Worth noting before you read on:
- When I say “vim” I specifically mean neovim: I hard-switched sometime late in 2015 (it was easy) and haven’t looked back.
- vim-go single-handedly gives vim a productivity advantage, but vscode-go isn’t too far behind. The open-godoc-in-a-vim-split (and generally better split usage) of vim-go is probably what wins out
Saying that, the autocompletion in vscode-go is richer and clearer, thanks to VS Code’s better autocompletion as-a-whole, and will get better.
Throughout this period, I realised I had two distinct ways of using an editor, each prioritizing different things:
- Short, quick edits. Has to launch fast. This is typically anything that uses
$EDITOR(git commit/rebase), short scripts, and quickly manipulating data (visual block mode + regex work well for this).
- Whole projects. Must manage editing/creating multiple files, provide Git integration, debugging across library boundaries, and running tests.
Lots of overlap, but it should be obvious where I care about launch speed vs. file management vs. deeper language support.
- VS Code’s startup speed isn’t icicle-like (think: early Atom), but it’s still slow, especially from a cold-start. ~5 seconds from
code $filenameto a text-rendered-and-extensions-loaded usable, which is about twice that of a plugin-laden neovim.
- Actual in-editor performance is good: command responsiveness, changing tabs, and jumping to declarations never feels slow.
- If you’ve started in the shell, switching to another application to edit a file or modify a multi-line shell command can feel a little clunky. I’d typically handle this by opening a new tmux split (retaining a prompt where I needed it) and then using vim to edit what I needed.
Vim In Visual Studio Code
Despite these things, it’s still capable of these tasks. vim just had a huge head-start, and is most at home in a terminal-based environment.
VS Code is really good here, and I think whole-project workflows are its strength, but it’s not perfect.
- The built-in Git support rivals vim-fugitive, moving between splits/buffers is fast, and it’s easy enough to hide. The default side-by-side diffs look good, although you don’t have as many tools to do a 3-way merge (via
:bufget, etc) as you do with vim-fugitive.
- Find-in-files is quick, although I miss some of the power of
ag.vim, which hooks into my favorite grep replacement, the-silver-searcher.
- What I miss from NERDTree is the ability to search it just like a buffer:
/filenameis incredibly useful on larger projects with more complex directory structures (looking at you, JS frameworks!). You’re also not able to navigate the filesystem up from the directory you opened, although I did see an issue for this and a plan for improvement.
I should note that opening a directory in VS Code triggers a full reload, which can be a little disruptive.
There’s a bunch of smaller things that, whilst unimportant in the scope of getting things done, are still noticeable:
- Font rendering. If you’re on macOS (nee OS X), then you’ll notice that VS Code’s font rendering (Chromium’s font rendering) is a little different from your regular terminal or other applications. Not worse; just different.
- Tab switching: good! Fast, and there’s support for vim commands like
:vspto open files in splits.
- You can use most of vim’s substitution syntax:
:s/before/after/(g)works as expected.
gc(for confirmation) doesn’t appear to work.
- EasyMotion support is included in the VSCodeVim plugin: although I’m a vim-sneak user, EasyMotion is arguably more popular among vim users and serves the same overall goals (navigating short to medium distances quickly).
<leader><leader>f<char>(in my config) allows me to easily search forwards by character.
- The native terminal needs a bunch of work to make me happy. It’s based on xterm.js, which could do with a lot more love if VS Code is going to tie itself to it. It just landed support for hyperlinks (in VS Code 1.9), but solid tmux support is still lacking and makes spending time in the terminal feel like a chore vs. iTerm.
- Configuration: I aliased
settings.jsoninto my dotfiles repository, so I can effectively sync them across machines like
.vimrc. Beyond that: VS Code is highly configurable, and although writing out JSON can be tedious, it does a lot to make changing default settings as easy as possible for you (autocompletion of settings is a nice touch).
You might also be asking: what about connecting to other machines remotely, where you only have an unadorned vim on the remote machine? That wasn’t a problem with vim thanks to the netrw plugin—you would connect to/browse the remote filesystem with your local, customized vim install—but is a shortcoming with VS Code. I wasn’t able to find a robust extension that would let me do this, although (in my case) it’s increasingly rare to SSH into a box given how software is deployed now. Still, worth keeping in mind if
vim scp://[email protected]:/path/to/script.sh is part of your regular workflow.
So, Which Editor?
I like VS Code a lot. Many of the things that frustate me are things that can be fixed, although I suspect improving startup speed will be tough (loading a browser engine underneath and all). Had I tried it 6+ months ago it’s likely I would have stuck with vim. Now, the decision is much harder.
I’m going to stick it out, because for the language I write the most (Go), the excellent autocompletion and toolchain integration beat out the other parts. If you are seeking a modern editor with 1:1 feature parity with vim, then look elsewhere: each editor brings its own things to the table, and you’ll never be pleased if you’re seeking that.Posted on 15 February 2017
Neovim integration for Visual Studio Code
For those who don't know Neovim is the fork of VIM to allow greater VIM extensibility and embeddability. The extension is using full embedded neovim instance as backend (with the exception of the insert mode and window/buffer/file management), no more half-complete VIM emulation
Please report any issues/suggestions to vscode-neovim repository
- Install vscode-neovim extension
- Install Neovim Required version 0.5.0 nightly or greater
- Tip: You can install neovim-0.5.0-nightly separately for just vscode, outside of your system's package manager installation
- Set neovim path in the extension settings and you're good to go.
- Important you must specify full path to neovim, like
- IMPORTANT 2: the setting id is
- Important you must specify full path to neovim, like
- Important!: If you already have big & custom
init.vimi'd recommend to wrap existing settings & plugins with
if !exists('g:vscode')check to prevent potential breakings and problems. If you have any problems - try with empty
Neovim 0.5+ is required. Any version lower than that won't work. Many linux distributions have an old version of neovim in their package repo - always check what version are you installing.
If you get
Unable to init vscode-neovim: command 'type' already exists message, try to uninstall other VSCode extensions, which register
type command (i.e. VSCodeVim or Overtype).
If you want to use WSL version of neovim, set
useWSL configuration toggle and specify linux path to nvim binary.
wsl.exe windows binary and
wslpath linux binary are required for this.
wslpath must be available through
$PATH linux env setting. Use
wsl --list to check for the correct default linux distribution.
- Almost fully feature-complete VIM integration by utilizing neovim
- First-class VSCode insert mode. The plugin unbinds self from the
typeevent in the insert mode, so no typing lag and freezing anymore when long completion popup appears.
- Fully working VSCode features - autocompletion/go to definition/snippets/multiple cursors/etc..
- vimrc/vim plugins/etc are supported (few plugins don't make sense with vscode, such as nerdtree)
Neovim 0.5.0-nightly or greater
- Visual modes are not producing real vscode selections (few versions had this feature previously, but it was implemented through ugly & hacky workarounds). Any vscode commands expecting selection won't work. To round the corners, invoking VSCode command picker through the default hotkeys (
ctrl/cmd+shift+p) from visual mode converts vim selection to real vscode selection. Also commenting/indenting/formatting works out of the box too. If you're using some custom mapping for calling vscode commands and depends on real vscode selection, you can use
VSCodeNotifyRangePos(the first one linewise, the latter characterwise) functions which will convert visual mode selection to vscode selection before calling the command. See this for example and mapping
- The extension for now works best if
- When you type some commands they may be substituted for the another, like
:writewill be replaced by
:Write. This is normal.
- File/tab/window management (
q/etc..) commands are substituted and mapped to vscode actions. If you're using some custom commands/custom mappings to them, you might need to rebind them to call vscode actions instead. See reference links below for examples if you want to use custom keybindings/commands. DO NOT use vim
:w, etc.. in scripts/keybindings, they won't work.
- On a Mac, the
lmovement keys may not repeat when held, to fix this open Terminal and execute the following command:
defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false
VSCode specific features and differences
- =, are mapped to
- It's possible to call vscode commands from neovim. See
VSCodeCall/VSCodeNotifyvim functions in
VSCodeCallis blocking request, while
VSCodeNotifyis not (see below)
- Scrolling is done by VSCode side.
<C-d>/<C-u>/etc..are slightly different
- File management commands such as
qetc are mapped to corresponding vscode commands and behavior may be different (see below)
<C-]are mapped to
<C-]>works in vim help files
gfis mapped to
gHis mapped to
gFare mapped to
editor.action.peekDeclarationrespectively (opens in peek)
<C-w>gfare mapped to
editor.action.revealDefinitionAside(original vim command - open new tab and go to the file under cursor, but vscode/vim window/tabs metaphors are completely different, so it's useful to do slightly different thing here)
ghis mapped to
- Dot-repeat (
.) . Works starting from
0.0.52version. Moving cursor within a change range won't break the repeat sequence. I.e. in neovim, if you type
abc<cursor>in insert mode then move cursor to
1here the repeat sequence would be
1. However in vscode it would be
a1bc. Another difference that
.repeat command when you delete some text only works from right-to-left. I.e. it will treat
<BS>keys for dot repeat.
If you have any performance problems (cursor jitter usually) make sure you're not using these kinds of extensions:
- Line number extensions (VSCode has built-in support for normal/relative line numbers)
- Indent guide extensions (VSCode has built-in indent guides)
- Brackets highlighter extensions (VSCode has built-in feature)
- Anything that renders decorators/put something into vscode gutter very often, e.g. on each cursor/line move
Such extension may be fine and work well, but combined with any extension which should control the cursor position (such as any vim extension) it may work very bad, due to shared vscode extension host between all extensions (E.g. one extension is taking the control over the host and blocking the other extension, this produces jitter).
If you're not sure, disable all other extensions except mine, reload vscode/window and see if the problem persist before reporting.
Also there are a reports that some vim settings/vim plugins increase latency and causing performance problems. Make sure you've disabled unneeded plugins. Many of them don't make sense with vscode and may cause any sort of problems. You don't need any code, highlighting, completion, lsp plugins as well any plugins that spawn windows/buffers (nerdtree and similar), fuzzy-finders plugins, etc. You might want to keep navigation/text-objects/text-editing/etc plugins - they should be fine.
Enabling jj or jk as escape keys from the insert mode
Put into your keybindings.json:
Visual Studio Code Vim Tabs
jk add also:
Determining if running in vscode in your init.vim
This should do the trick:
Invoking vscode actions from neovim
There are few helper functions that could be used to invoke any vscode commands:
VSCodeCall(command, ..)- invokes vscode command with optional arguments
VSCodeNotifyRange(command, line1, line2, leaveSelection ,..)/
VSCodeCallRange(command, line1, line2, leaveSelection, ..)- produces real vscode selection from line1 to line2 and invokes vscode command. Linewise. Put 1 for
leaveSelectionargument to leave vscode selection after invoking the command
VSCodeNotifyRangePos(command, line1, line2, pos1, pos2, leaveSelection ,..)/
VSCodeCallRangePos(command, line1, line2, pos1, pos2, leaveSelection, ..)- produces real vscode selection from line1.pos1 to line2.pos2 and invokes vscode command. Characterwise
Notify in name are non-blocking, the ones with
Call are blocking. Generally use Notify unless you really need a blocking call
Produce linewise selection and show vscode commands (default binding)
Produce characterwise selection and show vscode commands (default binding):
Run Find in files for word under cursor in vscode:
Open definition aside (default binding):
VSCode's jumplist is being used. Make sure to bind to
workbench.action.navigateForward if you're using custom mappings. Marks (both upper & lowercased) should be fine
Command menu has the wildmenu completion on type. The completion options appear after 1.5s (to not bother you when you write
<Up>/<Down> selects the option and
<Tab> accepts it. See the gif:
Multiple cursors work in:
- Insert mode
- (Optional) Visual line mode
- (Optional) Visual block mode
To spawn multiple cursors from visual line/block modes type
mI (by default). The effect differs:
- For visual line mode
miwill start insert mode on each selected line on the first non whitespace character and
mawill on the end of line
- For visual block mode
miwill start insert on each selected line before the cursor block and
mIversions account empty lines too (only for visual line mode, for visual block mode they're same as ma/mi)
See gif in action:
Custom keymaps for scrolling/window/tab/etc.. management
- See vscode-scrolling.vim for scrolling commands reference
- See vscode-file-commands.vim for file commands reference
- See vscode-tab-commands.vim for tab commands reference
- See vscode-window-commands.vim for window commands reference
File/Tab management commands
:ewithout argument and without bang (
!) - opens quickopen window
:e!without argument and with bang - opens open file dialog
:e [filename], e.g.
:e $MYVIMRC- opens a file in new tab. The file must exist
:e! [filename], e.g.
:e! $MYVIMRC- closes current file (discard any changes) and opens a file. The file must exist
enewCreates new untitled document in vscode
enew!closes current file (discard any changes) and creates new untitled document
- Opens vscode's quick open window. Arguments and count are not supported
- Without bang (
!) saves current file
- With bang opens 'save as' dialog
- Opens 'save as' dialog
- Saves all files. Bang is not doing anything
q[uit] or keys
<C-w> q /
- Closes the active editor
- Saves and closes the active editor
- Closes all editors, but doesn't quit vscode. Acts like
qall!, so beware for a nonsaved changes
- Saves all editors & close
- Similar to
e[dit]. Without argument opens quickopen, with argument opens the file in new tab
- Opens new untitled file
- Opens quickopen window
- Not supported. Doesn't make sense with vscode
- Closes active editor (tab)
- Closes other tabs in vscode group (pane). This differs from vim where a
tabis a like a new window, but doesn't make sense in vscode.
tabn[ext] or key
- Switches to next (or
counttabs if argument is given) in the active vscode group (pane)
tabp[revious] or key
- Switches to previous (or
counttabs if argument is given) in the active vscode group (pane)
- Switches to the first tab in the active editor group
- Switches to the last tab in the active edtior group
- Not supported yet
ZQ are bound to
Buffer/window management commands
Note: split size distribution is controlled by
workbench.editor.splitSizing setting. By default it's
distribute, which is mapped to vim's
eadirection = 'both' (default)
sp[lit] or key
- Split editor horizontally. When argument given opens the specified file in the argument, e.g
:sp $MYVIMRC. File must exist
vs[plit] or key
- Split editor vertically. When argument given opens the specified file in the argument. File must exist
new or key
sp[lit]but creates new untitled file if no argument given
vs[plit]but creates new untitled file if no argument given
- Not supported yet
- Not supported yet
on[ly] or key
- Without bang (
!) Merges all editor groups into the one. Doesn't close editors
- With bang closes all editors from all groups except current one
- Focus group below/above/left/right
- Move editor to group below/above/left/right. Vim doesn't have analogue mappings. Note:
<C-w> <C-i>moves editor up. Logically it should be
<C-w> <C-k>but vscode has many commands mapped to
<C-k> [key]and doesn't allow to use
<C-w> <C-k>without unbinding them first
- Not supported use
<C-w> <C-j>and similar to move editors
<C-w> w or
- Focus next group. The behavior may differ than in vim
Visual Studio Code Vim Plugin
<C-w> W or
- Focus previous group. The behavior may differ than in vim.
<C-w> pis completely different than in vim
- Focus first editor group (most top-left)
- Focus last editor group (most bottom-right)
- Not supported yet
- Align all editors to have the same width
- Increase editor height by (optional) count
- Decrease editor height by (optional) count
- Increase editor width by (optional) count
- Decrease editor width by (optional) count
To use VSCode command 'Increase/decrease current view size'
Copy this into init.vim
- Toggle maximized editor size. Pressing again will restore the size
Insert mode special keys
useCtrlKeysForInsertMode = true (default true)
|Paste from register||Works|
|Paste previous inserted content||Works|
|Delete all text till begining of line, if empty - delete newline||Bound to VSCode key|
|Delete word left||Bound to VSCode key|
|Delete character left||Bound to VSCode key|
|Indent lines right||Bound to VSCode indent line|
|Indent lines left||Bound to VSCode outindent line|
|Insert line||Bound to VSCode insert line after|
Other keys are not supported in insert mode
Normal mode control keys
useCtrlKeysForNormalMode = true (default true)
Refer to vim manual to get help what they're doing
- CTRL-o (see https://github.com/asvetliakov/vscode-neovim/issues/181#issuecomment-585264621)
Other control keys are not being sent (Usually useless with vscode)
Cmdline control keys (always enabled)
- CTRL-h (delete one character left)
- CTRL-w (delete word left)
- CTRL-u (clear line)
- CTRL-g / CTRL-t (in incsearch mode moves to next/previous result)
- CTRL-l (add next character under the cursor to incsearch)
- CTRL-n / CTRL-p (go down/up history)
<Down>(Select next/prev suggestion) (no way to make up/down to navigate through history, vscode disallows remapping)
- Tab - Select suggestion
Pass additional keys to neovim or disable existing ctrl keys mappings
To pass additional ctrl key sequence, for example add to your keybindings.json:
Visual Studio Code Vi Mode
To disable existing ctrl key sequence, for example Ctrl+A add to your keybindings.json
Vim Like Visual Studio Code
Speaking honestly, original vim-easymotion works fine and as expected.. except one thing: it really replaces your text with markers then restores back. It may work for VIM but for VS Code it leads to broken text and many errors reported while you're jumping. For this reason I created the special vim-easymotion fork which doesn't touch your text and instead use vscode text decorations. Just add my fork to your
vim-plug block or by using your favorite vim plugin installer and delete original vim-easymotion. Also overwin motions won't work (obviously) so don't use them. Happy jumping!
You can use vim-commentary if you like it. But vscode already has such functionality so why don't use it? Add to your init.vim/init.nvim
Similar to vim-commentary, gcc is comment line (accept count), use gc with motion/in visual mode.
VSCodeCommentary is just a simple function which calls
quick-scope plugin uses default vim HL groups by default but they are normally ignored. To fix add
to your init.vim
Vim Shortcuts Visual Studio Code
Vim Visual Studio Code Color Scheme
See Issues section
How it works
- VScode connects to neovim instance
- When opening a some file, a scratch buffer is created in nvim and being init with text content from vscode
- Normal/visual mode commands are being sent directly to neovim. The extension listens for buffer events and applies edits from neovim
- When entering the insert mode, the extensions stops listen for keystroke events and delegates typing mode to vscode (no neovim communication is being performed here)
- After pressing escape key from the insert mode, extension sends changes obtained from the insert mode to neovim
Credits & External Resources
- vim-altercmd - Used for rebinding default commands to call vscode command
- neovim nodejs client - NodeJS library for communicating with Neovim