Neovim to VS Code

https://didoesdigital.com/blog/neovim-to-vs-code/

I finally switched from Neovim to Visual Studio Code.

This is a vim power user’s guide to getting started with VS Code. It’s particularly relevant for front-end developers. I’ll cover the main reasons I switched, the joy of using the VSCode Neovim extension, some VS Code features, and how to use vim and VS Code in parallel. If you want to skip the backstory, jump to Getting started.

Motivation

I’d been talking about switching from vim to a graphical editor for years because I envied the ease with which other devs working on the front end could get set up with new tools. For any new code base or framework, I found that in the vim ecosystem it was a project just to get things working. Here are some specific areas…

Vim plugins and support

Edit: Front-end plugins often don’t exist in the vim ecosystem or they require particular (hard-to-find and hard-to-configure) settings to work for your setup. The plugins and their documentation/wikis are sprawled across GitHub wiki, READMEs, vim :help, and so on. Debugging vim plugins and using vimscript is a pain compared to the wealth of support in VS Code community and relative ease of reading JavaScript.

Vim and language servers

Anything depending on language servers like linting, formatting, and tags was awkward and difficult in vim. Coc seemed like the main option for quite a while and I found it difficult to wrangle (but there are better, newer options than Coc now that I never explored). Tom MacWright writes about the pain of TypeScript with Neovim in his post, A day using zed.

Notably, I found that my back-end oriented colleagues didn’t have as many issues using vim in a VS Code company, and my hunch is that TypeScript was a big factor.

Extensions

Meanwhile, my VS Code-using colleagues would switch seamlessly between code bases, and maybe install an extension here or there. There was a very well standardised and searchable extension ecosystem with huge numbers of people using and contributing to the extensions.

The size of the community and ecosystem in VS Code is appealing. Working with Neovim has always been the niche option. My coworkers would occasionally share neat extensions like Import Cost and Tailwind’s CSS Intellisense and prettier plugin, and I would just miss out.

Switching projects and tech stack as a vim user

To give you an idea, each of the following required some amount of manual hackery to work in vim: styled-components, Emotion, Tailwind, eslint, prettier, JSX and React, TypeScript, properly structured monorepos, poorly structured monorepos, and so on. Not always huge or insurmountable tasks, but certainly more than my colleagues faced.

Similarly, repos and onboarding docs often came with a .vscode folder with settings, so being the odd one out would take extra time to get set up.

Switching editor

All in all, I was eager to try a graphical editor popular among my coworkers that would support vim style modal editing. I didn’t consider any other editors because one goal was to be able to pair program with my coworkers and I wanted to be able to use the same editor as them. But with the power of vim!

Switching costs

Every time I tried to switch previously, however, I found the cost of switching too high. As an extremely efficient power user of vim, using VS Code felt like typing with sausages for fingers. Every operation that I previously did with my hands without my brain consciously instructing them would suddenly require multiple new steps:

  • make some guesses about what the operation was called
  • see if there was a tool built into VS Code for it
  • see if there was a setting to operate it
  • see if there was an extension for it
  • configure each of these
  • find the keyboard shortcut or create a key binding for it
  • write down the keyboard shortcut to help me learn it

… then finally do the thing I was trying to do!

When you’re used to doing things with your hands without thinking, it feels arduous to use a mouse to perform tasks and to search for commands, let alone work out how to do it all.

Why I finally switched

The main prompt to finally switch was my new work place was using devcontainers in VS Code and I didn’t want to waste time figuring out how to make that work with my vim setup.

Now I’ll get into how I actually made the switch!

Getting started

VSCode Neovim extension

Firstly, I set up the VSCode Neovim extension. It uses “a fully embedded Neovim instance”, not just emulation. It uses VSCode’s native functionality for insert mode and editor commands, “making the best use of both editors”.

I created a separate Neovim init file just for VS Code called vscode.vim next to my actual Neovim init file for vim, which is called .vimrc (for historical reasons, but yours might be init.vim).

A lot of vim behaviours work seamlessly out of the box with the VSCode Neovim extension, and it does a good job of documenting where it does not and what differences you need to consider.

When I started using it, I commented out most of my new vscode.vim file then walked through and turned parts on as needed.

I kept my leader key defined as space:

" Leader key is space bar:
let mapleader = " "

Vim plugins in VS Code

I disabled most of my usual vim plugins in vscode.vim so I didn’t have to worry too much about conflicts and to push myself to work out the native VS Code alternatives.

I kept my vim Plug setup, tpope plugins like vim-surround and vim-subvert, and junegunn plugins like vim-easy-align. Over time when I missed a vim plugin with vim specific behaviour, I would toggle them back on one at a time.

Using VS Code behaviour in vim code

I have a handful of vim mappings defined in vscode.vim to help overcome my hard-wired muscle memory. For example:

" If I reflexively hit space a, run ⌘⇧f instead:
nmap <leader>a <Cmd>call VSCodeCall('workbench.action.findInFiles')<CR>
" If I reflexively hit space e, run ⌘⇧e instead:
nmap <leader>e <Cmd>call VSCodeCall('workbench.view.explorer')<CR>
" If I reflexively hit space s, run ⌥⇧f instead:
nmap <leader>s <Cmd>call VSCodeCall('editor.action.formatDocument')<CR>

This is handy for key bindings that use vim behaviour like the leader key, or for incorporating into more complex vim commands.

To figure out what to put in the VSCodeCall(), you can search the VS Code command palette. Once you have found the name of the command, search for the command name in the VS Code Keyboard Shortcuts (⌘K ⌘S). Then right-click and pick “Copy Command ID”. Pop that text into your vscode.vim inside the VSCodeCall().

Migrating simple shortcuts to VS Code

For simpler behaviour, you can add VS Code key bindings using VS Code directly.

For example, I used to use ⌥j in Neovim in iTerm to move a line down:

" ⌥j on macOS types ∆. We use it here to move a line down:
nnoremap ∆ :m .+1<CR>

To add this shortcut in VS Code:

  • I can use ⌘K ⌘S to open Keyboard Shortcuts.
  • I search for the command with “move lines down” and discover it’s called moveLinesDownAction.
  • With that command highlighted, I press ⌘K ⌘A to add a new key binding.
  • VS Code asks me to type the keyboard shortcut (⌥j) and press Enter.
  • I can then “Change When Expression” ⌘K ⌘E if I like e.g. editorTextFocus && !editorReadonly.

To add multiple shortcuts to a single command, you can select a command and press ⌘K ⌘A again.

If you prefer editing JSON, you can also search the command palette for Preferences: Open Keyboard Shortcuts (JSON) to open the keybindings.json file and add your key binding there:

[
  {
    "key": "alt+j",
    "command": "editor.action.moveLinesDownAction",
    "when": "editorTextFocus && !editorReadonly"
  }
]

Reducing the UI clutter

If you’re used to a very minimal vim setup, you might find the VS Code User Interface (and its myriad unlabelled icons) overwhelming.

I turned off the activity bar (toggleActivityBarVisibility) and instead learned shortcuts to each of the sections inside the side bar that I need to make them appear on demand:

  • View: Toggle Primary Side Bar Visibility: ⌘B
  • View: Show Explorer: ⌘⇧E
  • Search: Find in Files: ⌘⇧F
  • View: Show Extensions: ⌘⇧X

For the rare cases that I want the activity bar back, I added ⌃\ as a shortcut to toggle the “Activity Bar” visibility:

[
  {
    "key": "ctrl+\\",
    "command": "workbench.action.toggleActivityBarVisibility"
  }
]

I toggle on View: Toggle Panel Visibility (⌘J) whenever I need to see the terminal and then collapse it again.

Because of how I toggle these panels, I tend to keep them obscenely large so that when the panels are open, I can read full file paths in the explorer and see the full terminal output.

I also leave the sticky scroll (@command:editor.action.toggleStickyScroll) off most of the time and toggle it on using a custom shortcut ⌘Y when I need it:

[
  {
    "key": "cmd+y",
    "command": "editor.action.toggleStickyScroll"
  }
]
The dialog element is highlighted with a hover widget showing the type of the element with a link to the MDN reference. Visible details include the VS Code project title, the 3 open files, the file path breadcrumbs, relative line numbers, and the status bar.

A zoomed in but minimal VS Code interface with a hover widget shown

Enjoying VS Code features

I want to share some of my favourite features in VS Code so far!

Refactoring shortcuts mostly just work, including refactoring TypeScript. Symbol renaming works across files. Extract function can be handy. I had previously had mixed success achieving such things in vim with Coc.

Auto Imports are super convenient e.g. ⌘. to bring up the “Quick Fix” list of JS/TS refactorings:

The object type Metadata is highlighted with a quick fix popover offering to add import from next or add import from next/types

Magical quick fix imports

Organize Imports (⇧⌥O) is like Prettier for imports.

I also enjoy how keyboard shortcuts are listed in the command palette and popovers. The visibility is handy for learning or reminding yourself of new shortcuts.

I don’t need to manually create tags files to navigate codebases. Shortcuts like go to definition, implementation, and references work well, and I can even use vim shortcuts like gd or ⌃] to operate them.

GitHub Copilot came to VS Code as the Copilot extension before Copilot came to Neovim but it did get to Neovim too, eventually.

If you’ve read this far you’re probably aware that vim has modal editing meaning in “normal” mode I can navigate around using regular keys, delete or copy text, and so on, while in “insert” mode I can type new text.

Using GitHub Copilot to predict text can sometimes be distracting if it’s predicting something irrelevant while I’m trying to think. So I hit escape to leave insert mode while I ponder what I need to write in peace. When I’m ready to type, I go back to insert mode. I understand that other people just ignore copilot’s suggestions. Some folk also like to pop Copilot out into a new tab with multiple additional options using Ctrl+Enter.

VS Code reload window

Sometimes if the VSCode Neovim extension hits an error for whatever reason, it can get confused and the vim behaviour gets out of sync so you’re effectively typing all over the place. At this point, absolutely do not press the vim undo shortcut as it also has no idea what’s going on and mangles the text further. Instead, call VS Code’s reload window command. It reboots the extension without losing what you have typed, even if the file is unsaved. Or maybe try restarting the Neovim extension from the command palette.

The reload window command does not affect any processes you have running in any VS Code terminals. I’ve also seen VS Code users run this command at the drop of a hat any time something is vaguely misbehaving so I don’t think this is necessarily a failure of the Neovim extension.

Power users

I’ve noticed with Neovim that the tool itself attracts more of a keyboard-oriented, power user community. The learning curve requires that you are really committed to figuring out how to work efficiently. While I know a lot of people that use VS Code, I don’t know many that have delved deeply into all of its features. So when I ask for ideas on how to do something that I’m used to doing fast in vim, they often don’t know how to do that task efficiently in VS Code. They’re content with using their mouse, searching the command palette every time, navigating manually through the file explorer, or just typing things out slowly.

This contributed to the high switching cost for me that meant I didn’t make the switch years ago. I hope this post can help you make the switch faster if you want.

Using both in parallel

Sometimes things are still just easier in Neovim. For substitutions or complex macros, I sometimes switch to Neovim.

I also continue to use Neovim for editing my personal notes files. No one is stopping you from using multiple editors!

VS Code features I don’t use

I still use git/tig in iTerm for the majority of git operations. I like the single-line staging you can do with tig. I’ve also yet to see any compelling reason to use the VS Code git features, and skipping them cuts down on how much VS Code I have to learn at once.

By certain flukes, I haven’t needed to resolve any merge conflicts in the last few months. I expect I’d still use vimdiff or opendiff or whatever, but it might depend on how much time/capacity I have to learn and navigate a new interface next time I have to deal with large conflicts.

Settings

Here are some settings I’ve changed in VS Code to get you started:

  • Set Editor: Line Numbers (@id:editor.lineNumbers) to “relative” in Settings to make vim line navigation easier.
  • Set Terminal › Integrated: Confirm On Exit to control whether to confirm when the window closes if there are active terminal sessions.

Extensions

Here are some extensions I’ve found useful:

Random tips

  • ⌘K ⌘S to open Keyboard Shortcuts
  • ⌘. to bring up the quick fix suggestions
  • K to show the hover widget, which can be handy for seeing documentation, types, function signature, or errors
  • ⌃space to “invoke Intellisense” and bring up a suggestions window
  • You can ⌘ click all sorts of things to follow them somewhere useful.

I also collected a bunch more shortcuts in that format (shortcut then concise, personal description) to refer back to when I forget them. I encourage you to do the same!

I like using space 1, space 2, and so on to jump to specific editor tabs. You can set this up in your vscode.vim:

" Leader key is space bar:
let mapleader = " "
nmap <leader>1 <Cmd>call VSCodeCall('workbench.action.openEditorAtIndex1')<CR>
nmap <leader>2 <Cmd>call VSCodeCall('workbench.action.openEditorAtIndex2')<CR>
nmap <leader>3 <Cmd>call VSCodeCall('workbench.action.openEditorAtIndex3')<CR>

Depending on your setup, you might consider globally ignoring .vscode/settings.json files in your global config .gitignore file so that you can customise whatever you like without affecting your team’s settings:

# Ignore VS Code workspace settings
.vscode/settings.json

Some things I haven’t worked out yet

If you have solutions to these problems, please let me know!

  • In VS Code the Neovim extension lets you run substitution commands, but if you want to edit a previously run substitution you can’t just press the up key to see the previous substitution and tweak it. If you know how to make this work, please let me know! Update! ⌃p/⌃n work for this. Thank you, weaksauce on Lobsters.
  • I used to toggle relative line numbers on and off when screen sharing with colleagues to make it easier for them to follow along. While I can adjust the setting in VS Code settings, I haven’t worked out how to toggle it quickly with a key binding yet. Update! There are some extensions to toggle or cycle settings, such as Toggle by Peng Lv that Brian Schiller shared with me. Here’s how I have configured the Toggle extension to switch the line numbers setting from “relative” to “on”:
    {
      "key": "alt-l",
      "command": "toggle",
      "when": "editorTextFocus",
      "args": {
        "id": "lineNumbers",
        "value": [
          {
            "editor.lineNumbers": "on"
          },
          {
            "editor.lineNumbers": "relative"
          }
        ]
      }
    },
    
  • On rare occasions, cit to change inside a HTML tag eats up more than just the current tag but a level or two higher. I haven’t worked out why yet.
  • Can I have a cspell user dictionary that doesn’t clutter up my user settings.json file with words, and doesn’t live in a specific project/workspace?
  • Only in MDX files, sometimes an unsaved duplicate of the current file will appear in a new tab. I’m not sure how and when this happens!
  • My colleagues say “press F12” or whatever and I have no idea what that means because I use vim shortcuts. Just kidding!

If you have feedback, send me an email at [email protected]

{
"by": "ellieh",
"descendants": 0,
"id": 40245111,
"score": 2,
"time": 1714721162,
"title": "Neovim to VS Code",
"type": "story",
"url": "https://didoesdigital.com/blog/neovim-to-vs-code/"
}
{
"author": "Diana MacDonald",
"date": "2024-05-02T04:00:00.000Z",
"description": "A vim power user’s guide to getting started with VS Code.",
"image": "https://didoesdigital.com/images/DiDoesDigital-cover.png",
"logo": "https://logo.clearbit.com/didoesdigital.com",
"publisher": "Diana MacDonald",
"title": "Neovim to VS Code · DiDoesDigital",
"url": "https://didoesdigital.com/blog/neovim-to-vs-code/"
}
{
"url": "https://didoesdigital.com/blog/neovim-to-vs-code/",
"title": "Neovim to VS Code · DiDoesDigital",
"description": "I finally switched from Neovim to Visual Studio Code. This is a vim power user’s guide to getting started with VS Code. It’s particularly relevant for front-end developers. I’ll cover the main reasons I...",
"links": [
"https://didoesdigital.com/blog/neovim-to-vs-code/"
],
"image": "https://didoesdigital.com/images/DiDoesDigital-cover.png",
"content": "<div><p>I finally switched from <a target=\"_blank\" href=\"https://neovim.io/\">Neovim</a> to <a target=\"_blank\" href=\"https://code.visualstudio.com/\">Visual Studio Code</a>.</p>\n<p>This is a vim power user’s guide to getting started with VS Code. It’s particularly relevant for front-end developers. I’ll cover the main reasons I switched, the joy of using the <a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=asvetliakov.vscode-neovim\">VSCode Neovim extension</a>, some VS Code features, and how to use vim and VS Code in parallel. If you want to skip the backstory, jump to <a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#getting-started\">Getting started</a>.</p>\n<h2 id=\"motivation\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#motivation\">Motivation</a></h2>\n<p>I’d been talking about switching from vim to a graphical editor for years because I envied the ease with which other devs working on the front end could get set up with new tools. For any new code base or framework, I found that in the vim ecosystem it was a project just to get things working. Here are some specific areas…</p>\n<h3 id=\"vim-plugins-and-support\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#vim-plugins-and-support\">Vim plugins and support</a></h3>\n<p><em>Edit:</em> Front-end plugins often don’t exist in the vim ecosystem or they require particular (hard-to-find and hard-to-configure) settings to work for your setup. The plugins and their documentation/wikis are sprawled across GitHub wiki, READMEs, <code>vim :help</code>, and so on. Debugging vim plugins and using vimscript is a pain compared to the wealth of support in VS Code community and relative ease of reading JavaScript.</p>\n<h3 id=\"vim-and-language-servers\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#vim-and-language-servers\">Vim and language servers</a></h3>\n<p>Anything depending on language servers like linting, formatting, and <code>tags</code> was awkward and difficult in vim. <a target=\"_blank\" href=\"https://github.com/neoclide/coc.nvim\">Coc</a> seemed like the main option for quite a while and I found it difficult to wrangle (but there are better, newer options than Coc now that I never explored). Tom MacWright writes about the pain of TypeScript with Neovim in his post, <a target=\"_blank\" href=\"https://macwright.com/2024/04/24/a-day-with-zed\">A day using zed</a>.</p>\n<p>Notably, I found that my back-end oriented colleagues didn’t have as many issues using vim in a VS Code company, and my hunch is that TypeScript was a big factor.</p>\n<h3 id=\"extensions\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#extensions\">Extensions</a></h3>\n<p>Meanwhile, my VS Code-using colleagues would switch seamlessly between code bases, and maybe install an extension here or there. There was a very well standardised and searchable extension ecosystem with huge numbers of people using and contributing to the extensions.</p>\n<p>The size of the community and ecosystem in VS Code is appealing. Working with Neovim has always been the niche option. My coworkers would occasionally share neat extensions like <a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost\">Import Cost</a> and <a target=\"_blank\" href=\"https://tailwindcss.com/docs/editor-setup\">Tailwind’s CSS Intellisense and prettier plugin</a>, and I would just miss out.</p>\n<h3 id=\"switching-projects-and-tech-stack-as-a-vim-user\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#switching-projects-and-tech-stack-as-a-vim-user\">Switching projects and tech stack as a vim user</a></h3>\n<p>To give you an idea, each of the following required some amount of manual hackery to work in vim: <a target=\"_blank\" href=\"https://styled-components.com/\">styled-components</a>, <a target=\"_blank\" href=\"https://emotion.sh/docs/introduction\">Emotion</a>, <a target=\"_blank\" href=\"https://tailwindcss.com/\">Tailwind</a>, <a target=\"_blank\" href=\"https://eslint.org/\">eslint</a>, <a target=\"_blank\" href=\"https://prettier.io/\">prettier</a>, <a target=\"_blank\" href=\"https://react.dev/learn/writing-markup-with-jsx\">JSX</a> and <a target=\"_blank\" href=\"https://react.dev/learn\">React</a>, <a target=\"_blank\" href=\"https://www.typescriptlang.org/\">TypeScript</a>, properly structured <a target=\"_blank\" href=\"https://monorepo.tools/\">monorepos</a>, poorly structured monorepos, and so on. Not always huge or insurmountable tasks, but certainly more than my colleagues faced.</p>\n<p>Similarly, repos and onboarding docs often came with a <code>.vscode</code> folder with settings, so being the odd one out would take extra time to get set up.</p>\n<h2 id=\"switching-editor\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#switching-editor\">Switching editor</a></h2>\n<p>All in all, I was eager to try a graphical editor popular among my coworkers that would support vim style modal editing. I didn’t consider any other editors because one goal was to be able to pair program with my coworkers and I wanted to be able to use the same editor as them. But with the power of vim!</p>\n<h3 id=\"switching-costs\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#switching-costs\">Switching costs</a></h3>\n<p>Every time I tried to switch previously, however, I found the cost of switching too high. As an extremely efficient power user of vim, using VS Code felt like typing with sausages for fingers. Every operation that I previously did with my hands without my brain consciously instructing them would suddenly require multiple new steps:</p>\n<ul>\n<li>make some guesses about what the operation was called</li>\n<li>see if there was a tool built into VS Code for it</li>\n<li>see if there was a setting to operate it</li>\n<li>see if there was an extension for it</li>\n<li>configure each of these</li>\n<li>find the keyboard shortcut or create a key binding for it</li>\n<li>write down the keyboard shortcut to help me learn it</li>\n</ul>\n<p>… then finally do the thing I was trying to do!</p>\n<p>When you’re used to doing things with your hands without thinking, it <em>feels arduous</em> to use a mouse to perform tasks and to search for commands, let alone work out how to do it all.</p>\n<h3 id=\"why-i-finally-switched\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#why-i-finally-switched\">Why I finally switched</a></h3>\n<p>The main prompt to finally switch was my new work place was using devcontainers in VS Code and I didn’t want to waste time figuring out how to make that work with my vim setup.</p>\n<p>Now I’ll get into how I actually made the switch!</p>\n<h2 id=\"getting-started\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#getting-started\">Getting started</a></h2>\n<h3 id=\"vscode-neovim-extension\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#vscode-neovim-extension\">VSCode Neovim extension</a></h3>\n<p>Firstly, I set up the <a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=asvetliakov.vscode-neovim\">VSCode Neovim extension</a>. It uses “a fully embedded Neovim instance”, not just emulation. It uses VSCode’s native functionality for insert mode and editor commands, “making the best use of both editors”.</p>\n<p>I created a separate Neovim init file just for VS Code called <code>vscode.vim</code> next to my actual Neovim init file for vim, which is called <code>.vimrc</code> (for historical reasons, but yours might be <code>init.vim</code>).</p>\n<p>A lot of vim behaviours work seamlessly out of the box with the VSCode Neovim extension, and it does a good job of documenting where it does not and what differences you need to consider.</p>\n<p>When I started using it, I commented out most of my new <code>vscode.vim</code> file then walked through and turned parts on as needed.</p>\n<p>I kept my leader key defined as space:</p>\n<pre><code><span>\" Leader key is space bar:</span>\n<span>let</span> mapleader = <span>\" \"</span>\n</code></pre>\n<h3 id=\"vim-plugins-in-vs-code\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#vim-plugins-in-vs-code\">Vim plugins in VS Code</a></h3>\n<p>I disabled most of my usual vim plugins in <code>vscode.vim</code> so I didn’t have to worry too much about conflicts and to push myself to work out the native VS Code alternatives.</p>\n<p>I kept my vim Plug setup, <code>tpope</code> plugins like <code>vim-surround</code> and <code>vim-subvert</code>, and <code>junegunn</code> plugins like <code>vim-easy-align</code>. Over time when I missed a vim plugin with vim specific behaviour, I would toggle them back on one at a time.</p>\n<h3 id=\"using-vs-code-behaviour-in-vim-code\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#using-vs-code-behaviour-in-vim-code\">Using VS Code behaviour in vim code</a></h3>\n<p>I have a handful of vim mappings defined in <code>vscode.vim</code> to help overcome my hard-wired muscle memory. For example:</p>\n<pre><code><span>\" If I reflexively hit space a, run ⌘⇧f instead:</span>\n<span>nmap</span> <span>&lt;leader&gt;</span><span>a</span> <span>&lt;Cmd&gt;</span><span>call</span> VSCodeCall(<span>'workbench.action.findInFiles'</span>)<span>&lt;CR&gt;</span>\n<span>\" If I reflexively hit space e, run ⌘⇧e instead:</span>\n<span>nmap</span> <span>&lt;leader&gt;</span><span>e</span> <span>&lt;Cmd&gt;</span><span>call</span> VSCodeCall(<span>'workbench.view.explorer'</span>)<span>&lt;CR&gt;</span>\n<span>\" If I reflexively hit space s, run ⌥⇧f instead:</span>\n<span>nmap</span> <span>&lt;leader&gt;</span>s <span>&lt;Cmd&gt;</span><span>call</span> VSCodeCall(<span>'editor.action.formatDocument'</span>)<span>&lt;CR&gt;</span>\n</code></pre>\n<p>This is handy for key bindings that use vim behaviour like the leader key, or for incorporating into more complex vim commands.</p>\n<p>To figure out what to put in the <code>VSCodeCall()</code>, you can search the VS Code command palette. Once you have found the name of the command, search for the command name in the VS Code Keyboard Shortcuts (⌘K ⌘S). Then right-click and pick “Copy Command ID”. Pop that text into your <code>vscode.vim</code> inside the <code>VSCodeCall()</code>.</p>\n<h3 id=\"migrating-simple-shortcuts-to-vs-code\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#migrating-simple-shortcuts-to-vs-code\">Migrating simple shortcuts to VS Code</a></h3>\n<p>For simpler behaviour, you can add VS Code key bindings using VS Code directly.</p>\n<p>For example, I used to use ⌥j in Neovim in iTerm to move a line down:</p>\n<pre><code><span>\" ⌥j on macOS types ∆. We use it here to move a line down:</span>\n<span>nnoremap</span> ∆ :<span>m</span> .+<span>1</span><span>&lt;CR&gt;</span>\n</code></pre>\n<p>To add this shortcut in VS Code:</p>\n<ul>\n<li>I can use ⌘K ⌘S to open Keyboard Shortcuts.</li>\n<li>I search for the command with “move lines down” and discover it’s called <code>moveLinesDownAction</code>.</li>\n<li>With that command highlighted, I press ⌘K ⌘A to add a new key binding.</li>\n<li>VS Code asks me to type the keyboard shortcut (⌥j) and press Enter.</li>\n<li>I can then “Change When Expression” ⌘K ⌘E if I like e.g. <code>editorTextFocus &amp;&amp; !editorReadonly</code>.</li>\n</ul>\n<p>To add multiple shortcuts to a single command, you can select a command and press ⌘K ⌘A again.</p>\n<p>If you prefer editing JSON, you can also search the command palette for <code>Preferences: Open Keyboard Shortcuts (JSON)</code> to open the <code>keybindings.json</code> file and add your key binding there:</p>\n<pre><code><span>[</span>\n <span>{</span>\n <span>\"key\"</span><span>:</span> <span>\"alt+j\"</span><span>,</span>\n <span>\"command\"</span><span>:</span> <span>\"editor.action.moveLinesDownAction\"</span><span>,</span>\n <span>\"when\"</span><span>:</span> <span>\"editorTextFocus &amp;&amp; !editorReadonly\"</span>\n <span>}</span>\n<span>]</span>\n</code></pre>\n<h3 id=\"reducing-the-ui-clutter\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#reducing-the-ui-clutter\">Reducing the UI clutter</a></h3>\n<p>If you’re used to a very minimal vim setup, you might find the VS Code User Interface (and its myriad unlabelled icons) overwhelming.</p>\n<p>I turned off the activity bar (<code>toggleActivityBarVisibility</code>) and instead learned shortcuts to each of the sections inside the side bar that I need to make them appear on demand:</p>\n<ul>\n<li><code>View: Toggle Primary Side Bar Visibility</code>: ⌘B</li>\n<li><code>View: Show Explorer</code>: ⌘⇧E</li>\n<li><code>Search: Find in Files</code>: ⌘⇧F</li>\n<li><code>View: Show Extensions</code>: ⌘⇧X</li>\n</ul>\n<p>For the rare cases that I want the activity bar back, I added ⌃\\ as a shortcut to toggle the “Activity Bar” visibility:</p>\n<pre><code><span>[</span>\n <span>{</span>\n <span>\"key\"</span><span>:</span> <span>\"ctrl+\\\\\"</span><span>,</span>\n <span>\"command\"</span><span>:</span> <span>\"workbench.action.toggleActivityBarVisibility\"</span>\n <span>}</span>\n<span>]</span>\n</code></pre>\n<p>I toggle on <code>View: Toggle Panel Visibility</code> (⌘J) whenever I need to see the terminal and then collapse it again.</p>\n<p>Because of how I toggle these panels, I tend to keep them obscenely large so that when the panels are open, I can read full file paths in the explorer and see the full terminal output.</p>\n<p>I also leave the <a target=\"_blank\" href=\"https://code.visualstudio.com/docs/getstarted/userinterface#_sticky-scroll\">sticky scroll</a> (<code>@command:editor.action.toggleStickyScroll</code>) off most of the time and toggle it on using a custom shortcut ⌘Y when I need it:</p>\n<pre><code><span>[</span>\n <span>{</span>\n <span>\"key\"</span><span>:</span> <span>\"cmd+y\"</span><span>,</span>\n <span>\"command\"</span><span>:</span> <span>\"editor.action.toggleStickyScroll\"</span>\n <span>}</span>\n<span>]</span>\n</code></pre>\n<figure><a target=\"_blank\" href=\"https://didoesdigital.com/images/2024/neovim-to-vs-code/vscode-hover.png\"><img alt=\"The dialog element is highlighted with a hover widget showing the type of the element with a link to the MDN reference. Visible details include the VS Code project title, the 3 open files, the file path breadcrumbs, relative line numbers, and the status bar.\" src=\"https://didoesdigital.com/images/2024/neovim-to-vs-code/vscode-hover.png\" /></a><figcaption><p>A zoomed in but minimal VS Code interface with a hover widget shown</p></figcaption></figure>\n<h2 id=\"enjoying-vs-code-features\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#enjoying-vs-code-features\">Enjoying VS Code features</a></h2>\n<p>I want to share some of my favourite features in VS Code so far!</p>\n<p><a target=\"_blank\" href=\"https://code.visualstudio.com/docs/editor/refactoring\">Refactoring</a> shortcuts mostly just work, including <a target=\"_blank\" href=\"https://code.visualstudio.com/docs/typescript/typescript-refactoring\">refactoring TypeScript</a>. Symbol renaming works across files. Extract function can be handy. I had previously had mixed success achieving such things in vim with Coc.</p>\n<p><a target=\"_blank\" href=\"https://code.visualstudio.com/Docs/languages/javascript#_auto-imports\">Auto Imports</a> are super convenient e.g. ⌘. to bring up the “Quick Fix” list of JS/TS refactorings:</p>\n<figure><a target=\"_blank\" href=\"https://didoesdigital.com/images/2024/neovim-to-vs-code/quick-fix-auto-import.png\"><img alt=\"The object type Metadata is highlighted with a quick fix popover offering to add import from next or add import from next/types\" src=\"https://didoesdigital.com/images/2024/neovim-to-vs-code/quick-fix-auto-import.png\" /></a><figcaption><p>Magical quick fix imports</p></figcaption></figure>\n<p><a target=\"_blank\" href=\"https://code.visualstudio.com/docs/typescript/typescript-refactoring#_organize-imports\">Organize Imports</a> (⇧⌥O) is like Prettier for imports.</p>\n<p>I also enjoy how keyboard shortcuts are listed in the command palette and popovers. The visibility is handy for learning or reminding yourself of new shortcuts.</p>\n<p>I don’t need to manually create <code>tags</code> files to navigate codebases. Shortcuts like go to definition, implementation, and references work well, and I can even use vim shortcuts like <code>gd</code> or <code>⌃]</code> to operate them.</p>\n<h3 id=\"modal-editing-and-copilot\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#modal-editing-and-copilot\">Modal editing and Copilot</a></h3>\n<p><a target=\"_blank\" href=\"https://github.com/features/copilot\">GitHub Copilot</a> came to <a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=GitHub.copilot\">VS Code as the Copilot extension</a> before <a target=\"_blank\" href=\"https://docs.github.com/en/copilot/using-github-copilot/getting-started-with-github-copilot?tool=vimneovim#about-github-copilot-and-vimneovim\">Copilot came to Neovim</a> but it did get to Neovim too, eventually.</p>\n<p>If you’ve read this far you’re probably aware that vim has modal editing meaning in “normal” mode I can navigate around using regular keys, delete or copy text, and so on, while in “insert” mode I can type new text.</p>\n<p>Using GitHub Copilot to predict text can sometimes be distracting if it’s predicting something irrelevant while I’m trying to think. So I hit escape to leave insert mode while I ponder what I need to write in peace. When I’m ready to type, I go back to insert mode. I understand that other people just ignore copilot’s suggestions. Some folk also like to pop Copilot out into a new tab with multiple additional options using Ctrl+Enter.</p>\n<h2 id=\"vs-code-reload-window\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#vs-code-reload-window\">VS Code reload window</a></h2>\n<p>Sometimes if the VSCode Neovim extension hits an error for whatever reason, it can get confused and the vim behaviour gets out of sync so you’re effectively typing all over the place. At this point, absolutely do not press the vim undo shortcut as it also has no idea what’s going on and mangles the text further. Instead, call VS Code’s reload window command. It reboots the extension without losing what you have typed, even if the file is unsaved. Or maybe try <a target=\"_blank\" href=\"https://code.visualstudio.com/updates/v1_88#_restart-extensions\">restarting the Neovim extension from the command palette</a>.</p>\n<p>The reload window command does not affect any processes you have running in any VS Code terminals. I’ve also seen VS Code users run this command at the drop of a hat any time something is vaguely misbehaving so I don’t think this is necessarily a failure of the Neovim extension.</p>\n<h2 id=\"power-users\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#power-users\">Power users</a></h2>\n<p>I’ve noticed with Neovim that the tool itself attracts more of a keyboard-oriented, power user community. The learning curve requires that you are really committed to figuring out how to work efficiently. While I know a lot of people that use VS Code, I don’t know many that have delved deeply into all of its features. So when I ask for ideas on how to do something that I’m used to doing fast in vim, they often don’t know how to do that task efficiently in VS Code. They’re content with using their mouse, searching the command palette every time, navigating manually through the file explorer, or just typing things out slowly.</p>\n<p>This contributed to the high switching cost for me that meant I didn’t make the switch years ago. I hope this post can help you make the switch faster if you want.</p>\n<h2 id=\"using-both-in-parallel\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#using-both-in-parallel\">Using both in parallel</a></h2>\n<p>Sometimes things are still just easier in Neovim. For substitutions or complex macros, I sometimes switch to Neovim.</p>\n<p>I also continue to use Neovim for editing my personal notes files. No one is stopping you from using multiple editors!</p>\n<h2 id=\"vs-code-features-i-dont-use\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#vs-code-features-i-dont-use\">VS Code features I don’t use</a></h2>\n<p>I still use <a target=\"_blank\" href=\"https://git-scm.com/\">git</a>/<a target=\"_blank\" href=\"https://jonas.github.io/tig/\">tig</a> in iTerm for the majority of <code>git</code> operations. I like the single-line staging you can do with <code>tig</code>. I’ve also yet to see any compelling reason to use the VS Code git features, and skipping them cuts down on how much VS Code I have to learn at once.</p>\n<p>By certain flukes, I haven’t needed to resolve any merge conflicts in the last few months. I expect I’d still use <code>vimdiff</code> or <code>opendiff</code> or whatever, but it might depend on how much time/capacity I have to learn and navigate a new interface next time I have to deal with large conflicts.</p>\n<h2 id=\"settings\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#settings\">Settings</a></h2>\n<p>Here are some settings I’ve changed in VS Code to get you started:</p>\n<ul>\n<li>Set <code>Editor: Line Numbers</code> (<code>@id:editor.lineNumbers</code>) to “relative” in Settings to make vim line navigation easier.</li>\n<li>Set <a target=\"_blank\" href=\"https://code.visualstudio.com/docs/terminal/advanced#_confirmation-dialogs\">Terminal › Integrated: Confirm On Exit</a> to control whether to confirm when the window closes if there are active terminal sessions.</li>\n</ul>\n<h2 id=\"extensions-1\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#extensions-1\">Extensions</a></h2>\n<p>Here are some extensions I’ve found useful:</p>\n<ul>\n<li><a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=asvetliakov.vscode-neovim\">VSCode Neovim extension</a> (obviously)</li>\n<li><a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker\">Code Spell Checker</a> to spell check code and prose</li>\n<li><a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens\">Git Lens</a> to see git blame and history</li>\n<li><a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost\">Import Cost</a> to see the size of imports</li>\n<li><a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=mechatroner.rainbow-csv\">Rainbow CSV</a> for working with CSV/TSV files</li>\n</ul>\n<h2 id=\"random-tips\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#random-tips\">Random tips</a></h2>\n<ul>\n<li>⌘K ⌘S to open Keyboard Shortcuts</li>\n<li>⌘. to bring up the quick fix suggestions</li>\n<li>K to show the hover widget, which can be handy for seeing documentation,\ntypes, function signature, or errors</li>\n<li>⌃space to “invoke Intellisense” and bring up a suggestions window</li>\n<li>You can ⌘ click all sorts of things to follow them somewhere useful.</li>\n</ul>\n<p>I also collected a bunch more shortcuts in that format (shortcut then concise, personal description) to refer back to when I forget them. I encourage you to do the same!</p>\n<p>I like using space 1, space 2, and so on to jump to specific editor tabs. You can set this up in your <code>vscode.vim</code>:</p>\n<pre><code><span>\" Leader key is space bar:</span>\n<span>let</span> mapleader = <span>\" \"</span>\n<span>nmap</span> <span>&lt;leader&gt;</span><span>1</span> <span>&lt;Cmd&gt;</span><span>call</span> VSCodeCall(<span>'workbench.action.openEditorAtIndex1'</span>)<span>&lt;CR&gt;</span>\n<span>nmap</span> <span>&lt;leader&gt;</span><span>2</span> <span>&lt;Cmd&gt;</span><span>call</span> VSCodeCall(<span>'workbench.action.openEditorAtIndex2'</span>)<span>&lt;CR&gt;</span>\n<span>nmap</span> <span>&lt;leader&gt;</span><span>3</span> <span>&lt;Cmd&gt;</span><span>call</span> VSCodeCall(<span>'workbench.action.openEditorAtIndex3'</span>)<span>&lt;CR&gt;</span>\n</code></pre>\n<p>Depending on your setup, you might consider globally ignoring <code>.vscode/settings.json</code> files in your global config <code>.gitignore</code> file so that you can customise whatever you like without affecting your team’s settings:</p>\n<pre><code># Ignore VS Code workspace settings\n.vscode/settings.json\n</code></pre>\n<h2 id=\"some-things-i-havent-worked-out-yet\"><a target=\"_blank\" href=\"https://didoesdigital.com/blog/neovim-to-vs-code/#some-things-i-havent-worked-out-yet\">Some things I haven’t worked out yet</a></h2>\n<p>If you have solutions to these problems, please let me know!</p>\n<ul>\n<li>In VS Code the Neovim extension lets you run substitution commands, but if you want to edit a previously run substitution you can’t just press the up key to see the previous substitution and tweak it. If you know how to make this work, please let me know! Update! ⌃p/⌃n work for this. Thank you, <a target=\"_blank\" href=\"https://lobste.rs/s/nlluhq/neovim_vs_code#c_ty8gjy\">weaksauce on Lobsters</a>.</li>\n<li>I used to toggle relative line numbers on and off when screen sharing with colleagues to make it easier for them to follow along. While I can adjust the setting in VS Code settings, I haven’t worked out how to toggle it quickly with a key binding yet. Update! There are some extensions to toggle or cycle settings, such as <a target=\"_blank\" href=\"https://marketplace.visualstudio.com/items?itemName=rebornix.toggle\">Toggle by Peng Lv</a> that <a target=\"_blank\" href=\"https://brianschiller.com/\">Brian Schiller</a> shared with me. Here’s how I have configured the Toggle extension to switch the line numbers setting from “relative” to “on”:\n<pre><code><span>{</span>\n <span>\"key\"</span><span>:</span> <span>\"alt-l\"</span><span>,</span>\n <span>\"command\"</span><span>:</span> <span>\"toggle\"</span><span>,</span>\n <span>\"when\"</span><span>:</span> <span>\"editorTextFocus\"</span><span>,</span>\n <span>\"args\"</span><span>:</span> <span>{</span>\n <span>\"id\"</span><span>:</span> <span>\"lineNumbers\"</span><span>,</span>\n <span>\"value\"</span><span>:</span> <span>[</span>\n <span>{</span>\n <span>\"editor.lineNumbers\"</span><span>:</span> <span>\"on\"</span>\n <span>}</span><span>,</span>\n <span>{</span>\n <span>\"editor.lineNumbers\"</span><span>:</span> <span>\"relative\"</span>\n <span>}</span>\n <span>]</span>\n <span>}</span>\n<span>}</span><span>,</span>\n</code></pre>\n</li>\n<li>On rare occasions, <code>cit</code> to change inside a HTML tag eats up more than just the current tag but a level or two higher. I haven’t worked out why yet.</li>\n<li>Can I have a cspell user dictionary that doesn’t clutter up my user <code>settings.json</code> file with words, and doesn’t live in a specific project/workspace?</li>\n<li>Only in MDX files, sometimes an unsaved duplicate of the current file will appear in a new tab. I’m not sure how and when this happens!</li>\n<li>My colleagues say “press F12” or whatever and I have no idea what that means because I use vim shortcuts. Just kidding!</li>\n</ul>\n<p>If you have feedback, <a target=\"_blank\" href=\"https://didoesdigital.com/cdn-cgi/l/email-protection#20494e464f604449444f45534449474954414c0e434f4d1f5355424a4543541d6e454f56494d051210544f0512107673051210634f4445\">send me an email</a> at <code><a target=\"_blank\" href=\"https://didoesdigital.com/cdn-cgi/l/email-protection\">[email protected]</a></code></p></div>",
"author": "Diana MacDonald",
"favicon": "https://didoesdigital.com/favicon/favicon.ico",
"source": "didoesdigital.com",
"published": "",
"ttr": 562,
"type": "website"
}