I’ve had to return to VSCode (from Neovim) for work. Here are some thoughts on how I’m getting the most Neovim-like experience possible.
Note: I won’t be going into too much detail, as I expect the reader to also be transitioning from Neovim to VSCode.
Sidenote: Extensions aren’t so bad
I do miss declaratively defining plugins, but having a little marketplace to install everything from LSPs to themes is nice.
What’s not so nice? Having to define ALL of your extension settings in a single settings.json
. Microsoft, please give us modular config files.
Vim-er VSCode
{
"editor.cursorSurroundingLines": 10, // Scrolloff
"editor.cursorBlinking": "solid", // Static cursor
"editor.lineNumbers": "relative", // Rel line numbers
"editor.semanticHighlighting.enabled": true, // Treesitter-like highlighting
"vim.useSystemClipboard": true, // OPTIONAL
"window.restoreWindows": "none", // Blank window
"workbench.startupEditor": "none", // when opening
}
General VSCode clutter
{
"editor.minimap.enabled": false,
"editor.stickyScroll.enabled": false,
"workbench.sideBar.location": "right"
}
VSCode Text Objects: The bread and butter
If you’re a Neovim refugee as well, you know how indispensable text objects are.
VSCode Textobjects is a phenomenal reimplementation of Treesitter’s Textobjects. It’s written by a solo dev and somehow only has 5 stars, with around 115 downloads.
It supports a TON of languages and is incredibly well thought-out. After copying/merging in the config snippet, all the standard Vim text object motions are available, but for actual programming objects (classes, functions, variable names/values, loops, conditionals). It’s as close to perfect as VSCode will allow.
The toughest part is that you must manually configure all text object motions in your config file. Not so bad, you’d have to do the same in Neovim. But this is hundreds of lines long, and we only have one config file to work with.
Again, Microsoft, PLEASE let us modularize our configs.
Another limitation is that you only get the “vanilla” editing operations (visual select, replace, delete, etc). While it covers most needs, there are some constraints:
- Can’t comment out the body of a function with
gcif
- Solution: visually select the inner contents first, then comment with
vifgc
This occurs because both VS Text Objects and VSCodeVim are emulation layers. Unless they were merged, their motions remain unaware of each other.
VSCodeVim
VSCodeVim is more than just motion emulation. It includes many essential plugins built-in, some enabled by default. My favorites:
vim-easymotion (Disabled)
Quickly jump to any visible character:
{
"vim.easymotion": true,
"vim.normalModeKeyBindingsNonRecursive": [
{
"before": ["s"],
"after": ["<leader>", "<leader>", "s"]
}
]
}
vim-surround (Enabled)
Surround objects with characters (quotes, brackets, backticks, visual selection, etc). Great for turning symbol
into a "string"
(ysiw"
), or changing a list [1, 2, 3]
into a set {1, 2, 3}
(cs]}
)
Tips
- To surround your visual selection, use
S
.
This differs from
vim-surround
in Neovim, whereS
is not used by default.
- When surrounding with brackets, use:
- Opening brackets (
{
,[
,(
) to loosely wrap (leave a space) - Closing brackets (
}
,]
,)
) to tightly wrap
- Opening brackets (
[ "This is loosely wrapped" ]
["This is tightly wrapped"]
vim-commentary (Enabled)
Comment text with gc
:
CamelCaseMotion (Disabled)
Edit PascalCase, camelCase, and snake_case symbols:
ReplaceWithRegister (Disabled)
Overwrite text without losing clipboard content using gr
(Based on the vim-substitute plugin)
vim-textobj-entire (Enabled)
Quickly perform edits/actions against the entire file.
I love using yae
(from vim-textobj-entire
) to copy my whole file to my system clipboard, but don’t like how it snaps my cursor to the start of the file. My hack is to mark (and return to) my current location:
{
"vim.normalModeKeyBindingsNonRecursive": [
{
"before": ["y", "a", "e"],
"after": ["m", "z", "y", "a", "e", "`", "z"]
}
]
}
VSCodeVim Settings
Here are my recommended VSCodeVim settings, not including bindings.
- Enables all the above Vim plugins
- Sets
space
to leader - “Highlight on yank”
- Clearer searching
{
"vim.easymotion": true,
"vim.hlsearch": true,
"vim.incsearch": true,
"vim.leader": "<space>",
"vim.useSystemClipboard": true,
"vim.camelCaseMotion.enabled": true,
"vim.highlightedyank.duration": 75,
"vim.highlightedyank.enable": true,
"vim.sneak": true,
"vim.sneakUseIgnorecaseAndSmartcase": true,
"vim.replaceWithRegister": true
}
Key Bindings
Splits Management
{
"vim.normalModeKeyBindingsNonRecursive": [
{
"before": ["leader", "v"],
"commands": [":vsplit"]
},
{
"before": ["leader", "s"],
"commands": [":split"]
},
{
"before": ["leader", "h"],
"commands": ["workbench.action.focusLeftGroup"]
},
{
"before": ["leader", "k"],
"commands": ["workbench.action.focusAboveGroup"]
},
{
"before": ["leader", "j"],
"commands": ["workbench.action.focusBelowGroup"]
},
{
"before": ["leader", "l"],
"commands": ["workbench.action.focusRightGroup"]
}
]
}
Indentation
The following allows you to visually select lines and spam <
/>
to align them.
{
"vim.visualModeKeyBindings": [
{
"before": [">"],
"commands": ["editor.action.indentLines"]
},
{
"before": ["<"],
"commands": ["editor.action.outdentLines"]
}
]
}
Go-to next problem
Requires Go to Next Problem
Go to the next warning or error.
The odd “toggle vim > action > toggle vim > vim_escape” is a workaround for a VSCode behavior.
{
"vim.normalModeKeyBindingsNonRecursive": [
// Goto next problem
{
"before": ["leader", "g", "e"],
"commands": ["toggleVim", "go-to-next-problem.nextInFiles", "toggleVim", "extension.vim_escape"],
"silent": true,
"args": { "severity": ["error", "warn"] },
"when": "editorFocus"
},
// Goto previous problem
{
"before": ["leader", "g", "E"],
"commands": ["toggleVim", "go-to-next-problem.prevInFiles", "toggleVim", "extension.vim_escape"],
"silent": true,
"args": { "severity": ["error", "warn"] },
"when": "editorFocus"
}
]
}
Error lens
Inline error messages.
Requires Error Lens
{
// Personally, I remove the "info" disagnostic level, as I also use spell check plugins
// Which I don't need inline errors for.
"errorLens.enabledDiagnosticLevels": ["error", "warning"],
}
Read More
These are my thoughts so far. For deeper customization, check the VSCodeVim README.
I’ll be doing additional write-ups for my whole settings.json
and an in-depth (video?) on VSCode Text Objects.