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 (6)

Collapse
 
snowyang profile image
Snow Yáng

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

Collapse
 
travisbeck profile image
Travis Beck

Amazing work. Thanks for sharing.

Collapse
 
anish_ali_ff2a0405ba1ebeb profile image
Anish Ali

Thank you Buddy Soo Much

Collapse
 
sfritsch09 profile image
Sebastian Fritsch • Edited

You also can jump to next highlighted word with * and previous with #
or
You can jump to last highlight in Visual Mode with gv or last insert mode with go

Collapse
 
himanshubanerji profile image
Himanshu Banerji

This is actually pretty useful

Collapse
 
randone profile image
Cai.Z.T

help me a lot!!