DEV Community

Cover image for 10 More VS Code Vim Tricks to Code Faster ⚡
Anson Heung
Anson Heung

Posted on • Edited on

10 More VS Code Vim Tricks to Code Faster ⚡

Around a year ago, I published the article “10 VS Code Vim Tricks to Boost Your Productivity ⚡”, which I shared various tips for the VSCodeVim extension. Turns out that the article was a hit, so allow me to share a few more VS Code Vim tips that I have up my sleeves 😆.

Prerequisites:

  • You’ve installed the VSCodeVim extension
  • Basic understanding of Vim keybindings. This guide is NOT for total beginners!

Table of Contents

(Previous 10 Tips)

(Full Settings: settings.json | keybindings.json)


Navigation

#1 - Tabs Management

I prefix all my custom keybindings for managing editor tabs with the Shift key so that it’s easy to remember. For tab navigation:

  • Shift + h - Focus the left tab
  • Shift + l - Focus the right tab
  • Shift + q - Close current tab

Notice how intuitive they are, since h and l keys in Vim means left and right, and q means “quit”.

1.1-tabs

To define the custom keybindings, open settings.json by pressing "Ctrl+Shift+P" and search for “Preferences: Open User Settings (JSON)”. Then, add the following item:

{
  "vim.normalModeKeyBindings": [
    {
      "before": ["H"], // Focus previous tab at the left
      "commands": ["workbench.action.previousEditor"]
    },
    {
      "before": ["L"], // Focus next tab at the right
      "commands": ["workbench.action.nextEditor"]
    },
    {
      "before": ["Q"], // Close current tab
      "after": ["<C-w>", "q"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

As for reorganizing the order of tabs:

  • Shift + LeftArrow - Move tab leftwards
  • Shift + RightArrow - Move tab rightwards

1.2-move-tabs

Setting up this keybinding is a bit tricky. VS Code has default keybindings where “Shift + LeftArrow” will select one character at the cursor’s left side (similar for “Shift + RightArrow”).

Actually we don’t need that since Vim’s Visual Mode can achieve the same thing via vh and vl keybindings. Thus, we need to disable VS Code’s default keybindings for Shift + Left/Right.

Open keybindings.json via “Ctrl + Shift + P” → “Preferences: Open Keyboard Shortcuts (JSON)” and append the following items:

[
  {
    "key": "shift+left",
    "command": "-cursorColumnSelectLeft",
    "when": "editorColumnSelection && textInputFocus"
  },
  {
    "key": "shift+left",
    "command": "-cursorLeftSelect",
    "when": "textInputFocus && vim.mode != 'Visual'"
  },
  {
    "key": "shift+left",
    "command": "workbench.action.moveEditorLeftInGroup",
    "when": "vim.mode == 'Normal'"
  },
  {
    "key": "shift+right",
    "command": "-cursorColumnSelectRight",
    "when": "editorColumnSelection && textInputFocus"
  },
  {
    "key": "shift+right",
    "command": "-cursorRightSelect",
    "when": "textInputFocus && vim.mode != 'Visual'"
  },
  {
    "key": "shift+right",
    "command": "workbench.action.moveEditorRightInGroup",
    "when": "vim.mode == 'Normal'"
  }
]
Enter fullscreen mode Exit fullscreen mode

(🔼 Table of Contents)

#2 - Splits Management

I prefix all splits-related keybindings with Ctrl key, similar to Tip #1 where I prefix all tabs-related keybindings with Shift key.

To create splits easily, I’ve defined 2 custom keybindings:

  • | (Shift + \) - Split tab vertically
  • _ - Split tab horizontally

I picked these two symbols because it looks like the split direction (| is a vertical stroke, _ is a horizontal stroke).

{
  "vim.normalModeKeyBindings": [
    {
      "before": ["|"], // Split tab vertically
      "after": ["<C-w>", "v"]
    },
    {
      "before": ["_"], // Split tab horizontally
      "after": ["<C-w>", "s"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

(💡 <C-w> is a Vim keybinding syntax that means Ctrl + w)

The Shift + q keybinding we saw from Tip #1 can also close a split if it only has one tab 👇

2.1-create-splits

To navigate around splits, I don’t like Vim’s <C-w> h/j/k/l keybindings because I have to press Ctrl + w every time. To reduce the number of keystrokes, I use Ctrl + h/j/k/l instead:

{
  "vim.normalModeKeyBindings": [
    {
      "before": ["<C-h>"], // Focus split window at left
      "commands": ["workbench.action.focusLeftGroup"]
    },
    {
      "before": ["<C-j>"], // Focus split window at right
      "commands": ["workbench.action.focusBelowGroup"]
    },
    {
      "before": ["<C-k>"], // Focus split window at above
      "commands": ["workbench.action.focusAboveGroup"]
    },
    {
      "before": ["<C-l>"], // Focus split window at below
      "commands": ["workbench.action.focusRightGroup"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

2.2-move-across-splits

⚠️ For Windows/Linux users, the Ctrl + k keybinding may not work properly. You may see this message at the bottom bar:

(Ctrl + K) was pressed. Waiting for second key of chord…

This is because VS Code has a lot of default keyboard shortcuts that starts with Ctrl + k (e.g. Ctrl+K Ctrl+S to open keyboard shortcuts). As a fallback, you can use Vim’s original keybinding of Ctrl+w k.

(🔼 Table of Contents)

#3 - Multi-Cursor

VS Code’s multi-cursor editing is also supported in VS Code Vim.

Below are the built-in VS Code keybindings to enter multi-cursor mode:

  • MacOS - Cmd + d
  • Windows/Linux - Ctrl + d

Unfortunately for Windows/Linux users, pressing Ctrl + d will instead scroll down the file, because it clashes with Vim’s keybinding of <C-d> for scrolling down a file. As a workaround, I remapped Ctrl + d to Alt + d to enter multi-cursor mode. To do that, append these to keybindings.json:

[
  {
    "key": "ctrl+d",
    "command": "-editor.action.addSelectionToNextFindMatch",
    "when": "editorFocus"
  },
  {
    "key": "alt+d",
    "command": "editor.action.addSelectionToNextFindMatch",
    "when": "editorFocus"
  }
]
Enter fullscreen mode Exit fullscreen mode

Alternative to Cmd/Alt + d, VS Code Vim also has a built-in keybinding of gb to enter multi-cursor mode. Here’s a demo in MacOS for both the Cmd + d and gb keybindings:

3.1-multi-cursor

VS Code Vim treats multi-cursor mode as a variant of Visual Mode, so you need to use Visual Mode keybindings to perform multi-cursor actions, such as:

  • Shift + i - Move to beginning of selection and enter Insert Mode
  • Shift + a - Move to end of selection and enter Insert Mode
  • s or c - Delete word and enter Insert Mode

For example, I need to press Shift + a in order to start typing at the end of the word (unlike in Normal Mode where I only need to type a):

3.2-multi-cursor-edit

#4 - Quick Search

To quickly search text from files, I’ve created the <leader>f keybinding:

💡 <leader> stands for Leader Key, which is commonly used as a prefix for user-defined shortcuts. By default it's the backslash key (\), but we commonly map it to Space key (see vim.leader option below)

{
  "vim.leader": "<space>",
  "vim.normalModeKeyBindings": [
    {
      "before": ["<leader>", "f"], // Search text
      "commands": ["workbench.action.findInFiles"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

4.1-find-in-files

I also created a keybinding of <leader>s to search all symbols in a workspace. This will save you a lot of time from locating the definition of a symbol.

{
  "vim.normalModeKeyBindings": [
    {
      "before": ["<leader>", "s"], // Search symbol
      "commands": ["workbench.action.showAllSymbols"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

For example, I want to directly jump to a React component called SaveButton. To do that, I launch the symbol search via <leader>s and enter the component name:

4.2-search-symbol

💡 Tip: If you place your cursor under a word (e.g. SaveButton) and press <leader>s, VS Code will automatically fill that word in the search bar 👇

4.3-search-symbol-tip

(🔼 Table of Contents)


Vim Features

#5 - Emulated Plugins

VS Code Vim emulates quite a number of Vim plugins. While I’m not going to walkthrough every plugin here (see docs for the full list), I want to highlight a plugin called vim-surround.

This plugin will save you tons of time when working with surrounding characters like paranthesis, brackets, quotes, and XML tags. Here’s a quick cheatsheet:

Surround Command Description
y s <motion> <desired> Add desired surround around text defined by 
d s <existing> Delete existing surround
c s <existing> <desired> Change existing surround to desired
S <desired> Surround when in visual modes (surrounds full selection)

For a full guide, please refer to vim-surround’s GitHub README.

Examples:

  • ysw) - Add () surround around a word
  • ds] - Delete the [] that surrounds the cursor
  • cs)] - Changes the () surrounding to []
  • S} - Surrounds the selected text with {} block

5-vim-surround

(🔼 Table of Contents)

#6 - Macros

Macro is a powerful feature in Vim that lets you record a series of commands and replay them. It’s useful to perform similar code changes for many times.

For example, let’s say I have a JavaScript object and I want to turn each value from a string to an array of strings. It’d be very tedious to manually edit line-by-line:

const data = {
  item1: "one",  // -> item1: ["one"]
  item2: "two",  // -> item2: ["two"]
  ...
}
Enter fullscreen mode Exit fullscreen mode

Here’s the anatomy of macros:

  • Record a macro: q<register><commands>q.
    • Begin a macro recording with q followed by <register> (i.e. a single letter, which is like the name of a save slot). For example, qa means I record a macro to a slot named a.
    • <commands> is the series of Vim keybindings you wish to perform.
    • End the recording by pressing q.
  • To play a macro: @<register>.
    • <register> is the single letter that you just recorded your macro to. For example, @a will play the commands that I’ve previously recorded via qa<commands>q.
    • You can play the macro multiple times. For example, 4@a plays the macro 4 times.

Back to our example. We basically want to repeat the action of adding a [] bracket around the string value. Let’s record our macro:

  1. Place our cursor at start of the object key name.
  2. qa - Begin recording the macro to slot a.
  3. f" - Move the cursor to the first double quote.
  4. yst,] - A vim-surround keybinding that we saw from Tip #5. It means adding a [] bracket for the text from the current cursor’s position (first double quote) till the first comma (i.e. t,).
  5. j0w - Places the cursor in an appropriate location before the macro ends. We move one line down with j, then move to beginning of first word with 0w.
    • 💡 The cursor’s end position matters because it affects the repeatability of the macro.
  6. q - Stops the macro recording.

6.1-macro-record

To confirm the macro works, let’s replay it once via @a. Note how the cursor perfectly lands at the start of next line (item3), so that we can replay the macro consecutively via 4@a (i.e. play 4 times):

6.2-macro-play

(🔼 Table of Contents)


Git

All my custom Git keybindings are prefixed with <leader>g, where g stands for git. I highly recommend grouping related keybindings with the same prefix so that it’s easier to remember 👍.

#7 - Peek Diff

If your cursor is located inside a Git hunk, I can press <leader>gp to peek the diff changes (gp = git peek).

{
  "vim.normalModeKeyBindings": [
    {
      "before": ["<leader>", "g", "p"], // Peek Git diff for the changed line
      "commands": ["editor.action.dirtydiff.next"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

7-peek-diff

(🔼 Table of Contents)

#8 - Revert Hunk or Selected Lines

To revert a Git hunk (i.e. discard unstaged changes) in Normal Mode:

  1. Place my cursor inside the Git hunk
  2. Press <leader>gr (gr = git revert)
{
  "vim.normalModeKeyBindings": [
    {
      "before": ["<leader>", "g", "r"], // Revert hunk
      "commands": ["git.revertSelectedRanges"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

8-revert-hunk-normal

To discard multiple hunks at the same time, I’ve also defined the same keybinding for Visual Mode:

{
  "vim.visualModeKeyBindings": [
    {
      "before": ["<leader>", "g", "r"], // Revert hunk
      "commands": ["git.revertSelectedRanges"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Then, I can first select multiple hunks in Visual Mode, then use <leader>gr to revert them:

8-revert-hunk-visual

(🔼 Table of Contents)

#9 - Jump to Previous/Next Changes

Sometimes I have many Git changes across a long file. To quickly jump between changes:

  • <leader>gj - Jump to next Git change (j = down)
  • <leader>gk - Jump to previous Git change (k = up)
{
  "vim.normalModeKeyBindings": [
    {
      "before": ["<leader>", "g", "j"], // Jump to next Git change
      "commands": ["workbench.action.editor.nextChange"]
    },
    {
      "before": ["<leader>", "g", "k"], // Jump to previous Git change
      "commands": ["workbench.action.editor.previousChange"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

9-jump-change

(🔼 Table of Contents)

#10 - Open File on Remote

If your Git repo has a remote upstream (e.g. GitHub, GitLab), you can easily get a sharable URL to the file you’re currently opening.

  1. Prerequisite: Install GitLens extension
  2. Add these keybindings to settings.json
   {
     "vim.normalModeKeyBindings": [
       {
         "before": ["<leader>", "g", "g"], // Open file in GitHub
         "commands": ["gitlens.openFileOnRemote"]
       }
     ],
     "vim.visualModeKeyBindings": [
       {
         "before": ["<leader>", "g", "g"], // Open file in GitHub
         "commands": ["gitlens.openFileOnRemote"]
       }
     ]
   }
Enter fullscreen mode Exit fullscreen mode
  1. Move your cursor on a line you wish to open on remote, or enter Visual Mode to select multiple lines
  2. Press <leader>gg (mnemonic: last g stands for GitHub)

To jump to a specific line on remote, use the keybinding in Normal Mode 👇

10.1-remote-normal

This also works for Visual Mode too. Try it yourself!

(🔼 Table of Contents)


For the full settings code, they can be found below 👇

(I used .jsonc instead of .json in the Gists. Otherwise, the syntax highlighting will add an ugly red background to all comments since theoretically comments are not allowed in JSON 😢)

Thanks for reading. Don’t forget to like and share this post if you found it useful, cheers! 🙌

Top comments (4)

Collapse
 
snowyang profile image
Snow Yáng

I can't believe there is no comment!!! You are my hero!!! Thanks a lot!

Collapse
 
anish_ali_ff2a0405ba1ebeb profile image
Anish Ali

Thank you Buddy Soo Much

Collapse
 
travisbeck profile image
Travis Beck

Amazing work. Thanks for sharing.

Collapse
 
randone profile image
Cai.Z.T

help me a lot!!