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
VSCodeVim is a Vim emulator for Visual Studio Code. For a full list of supported Vim features, please refer to our roadmap. Our change log outlines the breaking/major/minor updates between releases. If you need to ask any questions, join us on Slack. Browse other questions tagged vim visual-studio-code vim-plugin or ask your own question. The Overflow Blog Podcast 331: One in four visitors to Stack Overflow copies code. 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.
Installation
- 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
C:Neovimbinnvim.exe
or/usr/local/bin/nvim
. - IMPORTANT 2: the setting id is
vscode-neovim.neovimExecutablePaths.win32/linux/darwin
- Important you must specify full path to neovim, like
- Important!: If you already have big & custom
init.vim
i'd recommend to wrap existing settings & plugins withif !exists('g:vscode')
check to prevent potential breakings and problems. If you have any problems - try with emptyinit.vim
first
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).
WSL
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.
Features
- Almost fully feature-complete VIM integration by utilizing neovim
- First-class VSCode insert mode. The plugin unbinds self from the
type
event 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)
Requirements
Neovim 0.5.0-nightly or greater
Important
- 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 (
f1
/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 useVSCodeNotifyRange
/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
editor.scrollBeyondLastLine
is disabled. - When you type some commands they may be substituted for the another, like
:write
will be replaced by:Write
. This is normal. - File/tab/window management (
:w
/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
h
,j
,k
andl
movement 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
editor.action.formatSelection
- It's possible to call vscode commands from neovim. See
VSCodeCall/VSCodeNotify
vim functions invscode-neovim.vim
file.VSCodeCall
is blocking request, whileVSCodeNotify
is not (see below) - Scrolling is done by VSCode side.
<C-d>/<C-u>/etc..
are slightly different - File management commands such as
e
/w
/q
etc are mapped to corresponding vscode commands and behavior may be different (see below) gd
/<C-]
are mapped toeditor.action.revealDefinition
(ShortcutF12
), also<C-]>
works in vim help filesgf
is mapped toeditor.action.revealDeclaration
gH
is mapped toeditor.action.referenceSearch.trigger
gD
/gF
are mapped toeditor.action.peekDefinition
andeditor.action.peekDeclaration
respectively (opens in peek)<C-w>gd
/<C-w>gf
are mapped toeditor.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)gh
is mapped toeditor.action.showHover
- Dot-repeat (
.
) . Works starting from0.0.52
version. Moving cursor within a change range won't break the repeat sequence. I.e. in neovim, if you typeabc<cursor>
in insert mode then move cursor toa<cursor>bc
and type1
here the repeat sequence would be1
. However in vscode it would bea1bc
. Another difference that.
repeat command when you delete some text only works from right-to-left. I.e. it will treat<Del>
key as<BS>
keys for dot repeat.
Performance/Latency problems
If you have any performance problems (cursor jitter usually) make sure you're not using these kinds of extensions: Mouse and keyboard for mac pro.
- 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:
for jj
to enable 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:
VSCodeNotify(command, ..)
/VSCodeCall(command, ..)
- invokes vscode command with optional argumentsVSCodeNotifyRange(command, line1, line2, leaveSelection ,..)
/VSCodeCallRange(command, line1, line2, leaveSelection, ..)
- produces real vscode selection from line1 to line2 and invokes vscode command. Linewise. Put 1 forleaveSelection
argument to leave vscode selection after invoking the commandVSCodeNotifyRangePos(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
Functions with Notify
in name are non-blocking, the ones with Call
are blocking. Generally use Notify unless you really need a blocking call
Examples:
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):
Jumplist
VSCode's jumplist is being used. Make sure to bind to workbench.action.navigateBack
/ workbench.action.navigateForward
if you're using custom mappings. Marks (both upper & lowercased) should be fine
Wildmenu completion
Command menu has the wildmenu completion on type. The completion options appear after 1.5s (to not bother you when you write :w
or :noh
). <Up>/<Down>
selects the option and <Tab>
accepts it. See the gif:
Multiple cursors
Multiple cursors work in:
- Insert mode
- (Optional) Visual line mode
- (Optional) Visual block mode
To spawn multiple cursors from visual line/block modes type ma
/mA
or mi
/mI
(by default). The effect differs:
- For visual line mode
mi
will start insert mode on each selected line on the first non whitespace character andma
will on the end of line - For visual block mode
mi
will start insert on each selected line before the cursor block andma
after mA
/mI
versions 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
:e[dit]
or ex
:e
without 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
ene[w]
enew
Creates new untitled document in vscodeenew!
closes current file (discard any changes) and creates new untitled document
fin[d]
- Opens vscode's quick open window. Arguments and count are not supported
w[rite]
- Without bang (
!
) saves current file - With bang opens 'save as' dialog
sav[eas]
- Opens 'save as' dialog
wa[ll]
- Saves all files. Bang is not doing anything
q[uit]
or keys <C-w> q
/ <C-w> c
- Closes the active editor
wq
- Saves and closes the active editor
qa[ll]
- Closes all editors, but doesn't quit vscode. Acts like
qall!
, so beware for a nonsaved changes
wqa[ll]
/xa[ll]
- Saves all editors & close
tabe[dit]
- Similar to
e[dit]
. Without argument opens quickopen, with argument opens the file in new tab
tabnew
- Opens new untitled file
tabf[ind]
- Opens quickopen window
tab
/tabs
- Not supported. Doesn't make sense with vscode
tabc[lose]
- Closes active editor (tab)
tabo[nly]
- Closes other tabs in vscode group (pane). This differs from vim where a
tab
is a like a new window, but doesn't make sense in vscode.
tabn[ext]
or key gt
- Switches to next (or
count
tabs if argument is given) in the active vscode group (pane)
tabp[revious]
or key gT
- Switches to previous (or
count
tabs if argument is given) in the active vscode group (pane)
tabfir[st]
- Switches to the first tab in the active editor group
tabl[ast]
- Switches to the last tab in the active edtior group
tabm[ove]
- Not supported yet
Keys ZZ
and ZQ
are bound to :wq
and q!
respectively
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 equalalways
and eadirection = 'both'
(default)
sp[lit]
or key <C-w> s
- Split editor horizontally. When argument given opens the specified file in the argument, e.g
:sp $MYVIMRC
. File must exist
vs[plit]
or key <C-w> v
- Split editor vertically. When argument given opens the specified file in the argument. File must exist
new
or key <C-w> n
- Like
sp[lit]
but creates new untitled file if no argument given
vne[w]
- Like
vs[plit]
but creates new untitled file if no argument given
<C-w> ^
- Not supported yet
vert[ical]
/lefta[bove]
/etc..
- Not supported yet
on[ly]
or key <C-w> o
- Without bang (
!
) Merges all editor groups into the one. Doesn't close editors - With bang closes all editors from all groups except current one
<C-w> j/k/h/l
- Focus group below/above/left/right
<C-w> <C-j>/<C-i>/<C-h>/<C-l>
- 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
<C-w> r/R/x
- Not supported use
<C-w> <C-j>
and similar to move editors
<C-w> w
or <C-w> <C-w>
- Focus next group. The behavior may differ than in vim
<C-w> W
or <C-w> p
- Focus previous group. The behavior may differ than in vim.
<C-w> p
is completely different than in vim
<C-w> t
- Focus first editor group (most top-left)
<C-w> b
- Focus last editor group (most bottom-right)
<C-w> H/K/J/L
- Not supported yet
<C-w> =
- Align all editors to have the same width
[count]<C-w> +
- Increase editor height by (optional) count
[count]<C-w> -
- Decrease editor height by (optional) count
[count]<C-w> >
- Increase editor width by (optional) count
[count]<C-w> <
- Decrease editor width by (optional) count
To use VSCode command 'Increase/decrease current view size'
workbench.action.increaseViewSize
workbench.action.decreaseViewSize
Copy this into init.vim
<C-w> _
- Toggle maximized editor size. Pressing again will restore the size
Insert mode special keys
Enabled by useCtrlKeysForInsertMode = true
(default true)
Key | Desc | Status |
---|---|---|
CTRL-r [0-9a-z'%#*+:.-=] | Paste from register | Works |
CTRL-a | Paste previous inserted content | Works |
CTRL-u | Delete all text till begining of line, if empty - delete newline | Bound to VSCode key |
CTRL-w | Delete word left | Bound to VSCode key |
CTRL-h | Delete character left | Bound to VSCode key |
CTRL-t | Indent lines right | Bound to VSCode indent line |
CTRL-d | Indent lines left | Bound to VSCode outindent line |
CTRL-j | Insert line | Bound to VSCode insert line after |
Other keys are not supported in insert mode
Normal mode control keys
Enabled by useCtrlKeysForNormalMode = true
(default true)
Refer to vim manual to get help what they're doing
- CTRL-a
- CTRL-b
- CTRL-c
- CTRL-d
- CTRL-e
- CTRL-f
- CTRL-i
- CTRL-o (see https://github.com/asvetliakov/vscode-neovim/issues/181#issuecomment-585264621)
- CTRL-r
- CTRL-u
- CTRL-v
- CTRL-w
- CTRL-x
- CTRL-y
- CTRL-]
- CTRL-j
- CTRL-k
- CTRL-l
- CTRL-h
- CTRL-/
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)
<Up>
/<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:
To disable existing ctrl key sequence, for example Ctrl+A add to your keybindings.json
Vim-easymotion
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!
Vim-commentary
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 editor.action.commentLine
VIM quick-scope
quick-scope plugin uses default vim HL groups by default but they are normally ignored. To fix add
to your init.vim
Known Issues
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
Hot tips to bring the awesomeness of Visual Studio Code to Vim.
Front-Matter
I want to start by saying, this is not an editor-shame article. You can use whatever text editor you want. It really doesn’t matter. I’m only writing this because I found a level of productivity in Vim that I haven’t had in any of the editors I used before (Sublime Text, Atom or VSCode).
If you’ve heard about Vim, and want to try it out, I hope this article can provide a bit of familiarity you’d find from VSCode.
Why Vim?
There are lots of reasons to use Vim, so here’s a few of mine.
Keep your hands at 10 and 2
When you are solely using the keyboard, there is going to be an inherent speed boost just from not having to physically move your hands. And hey, maybe you’re a black belt in mouse movement, and you can move back in forth with a speed invisible to the naked eye. For the rest of us simple humans, it takes time.
Let’s do some quick math.
It takes 600ms to move my hand from the “home keys” to the mouse. On average, for sake of argument, I do that once a minute while I’m writing code. Whether that be to scroll, navigate to a new file, or something similar.
600 (wasted time in ms) x 60 (times per hour) x 5 (hours I am actually coding) = 180,000ms wasted =
3. Minutes. Every. Day.
Yeah, okay, maybe that doesn’t sound so bad, but, those 3 minutes could be spent writing a function, or refactoring code, not flailing your hand about like you're Harry Potter!
Speed
My favorite quote that describes what it’s like to code in VIM:
“Code at the speed of thought”
Vim is built around the idea that you are directly communicating with your computer. You tell it what you want, and it does it for you. The biggest eye-opener for me was this little tidbit:
To delete everything in between two objects (parentheses, quotes, etc), it's as simple as:
di'
That’s just the surface of amazing shorthand things you can do with Vim.
I’m a real programmer!
Part of the journey of learning VIM is exposing yourself to how UNIX works. I’m under the impression that the more you expose yourself to things like bash, the better programmer you are going to be.
Chances are, you have a pretty sweet command line setup. Wouldn’t it be nice if your code editor and your command line worked in concert?
How do you exit Vim?
Probability is high that you’ve ever edited a file on a Linux server, and couldn’t figure out how to exit the file. Let’s say, for example, changing an SSH key on Digital Ocean. If you know VIM…you don’t have to worry about that!
The real reason I switched to Vim
Honesty time. The real catalyst for wanting to switch to Vim was watching Kyle Mathews (creator of Gatsby.js) using it during a demo.
VS Code Features and their equivalents
Convinced? Cool, here’s some tools!
Plugin System
Vim by itself is pretty barebones. In order to add plugins, we have to have a mechanism to manage them. Enter Plug:
Note: There are a few plugin managers out there. I landed on Plug for no particular reason. I like it, and I’ve had no issues with it. FYI, Vundle is deprecated.
File Search
There has been a bunch of solutions for file searching over the years, as indicated by the multitude of answers in forums. I tried a couple of different ones, but landed on this combination:
Fuzzy Finder(fzf) + Ripgrep
Fzf is a really well built/maintained fuzzy search that works in both the command line and vim.
Note: You may see Ag(Silver searcher) in a lot of articles, however the Ag related vim plugin is no longer maintained, so it's suggested to use RipGrep.
Intellisense
The auto-completion system (Intellisense) in VSCode is arguably its best feature. Lucky for us, it’s been ported over to Vim!
CoC has its own extension system, that mirrors that of VSCodes. It's easy to use and well-documented (the most important part).
Note: You may see some old articles talking about YouCompleteMe, but as far as I can tell, that isn’t maintained anymore.
Vs Code Vs Vim
File System Explorer
VSCode, like most modern text editors, comes with a file explorer. Vim’s native netrw
is alright, and I’ve seen quite a few articles saying you don’t need anything else, like here. However, I find NERDTree is too useful to not use.
Git integration
I gotta be honest here, I do most of my git stuff straight in Iterm. However, VSCode has an incredibly nice Git Diff split screen. To get that level of git integration, check out this plugin:
Additional plugins that you may want
This is some of the stuff I used in Visual Studio Code, that I wanted to bring into Vim.
Autocomplete Brackets
This nice little package will auto close those pesky brackets.
File Icons
This will add icons to stuff like NERDTree.
Prettier
Wouldn’t you know it, but the official prettier team has a vim plugin. How nice! Also, incredibly simple to set up.
Get it to work on autosave, check out this article.
Snippets
Wouldn’t you know it, using the Conquer of Completion, you can import VSCode snippets!
Check this out to show you how to do that:
Here is the React snippets package I’m using.
Additional Stuff
THE home for Vim plugins is Vim Awesome.
Great place to watch people use Vim:
Dotfiles
I have a few remapped keys to make things easier. Check out my dotfiles for all those.
Final Thoughts
My Journey
I solely use Vim now, after spending about a year to learn it. Initially, I was using it just for my personal projects, because my productivity level was low. I had to keep stopping to look up how to do something. However, I dropped VSCode completely about 4 months ago, and I don’t plan on going back.
It takes discipline
Vi Vs Visual Studio
Learning Vim can seem daunting, and frankly, it is. It requires self-imposed discipline. However, doesn’t everything in development? There is no tool/language/framework I’ve ever learned that didn’t require some level of deliberate practice.
Vim is a lifestyle choice. It will take a while to get used to it, and it WILL be frustrating at times. However, if you stick to it, I guarantee it’ll improve your workflow. If you have any additional tips or questions, please drop them below. As always, happy coding!