Skip to content

Instantly share code, notes, and snippets.

@nikolovlazar
Last active February 5, 2025 19:54
Show Gist options
  • Save nikolovlazar/1174876ab2769c52ac9fc1534c557d70 to your computer and use it in GitHub Desktop.
Save nikolovlazar/1174876ab2769c52ac9fc1534c557d70 to your computer and use it in GitHub Desktop.
VSCode key bindings to navigate like Neovim
[
// Navigation
{
"key": "ctrl-h",
"command": "workbench.action.navigateLeft"
},
{
"key": "ctrl-l",
"command": "workbench.action.navigateRight"
},
{
"key": "ctrl-k",
"command": "workbench.action.navigateUp"
},
{
"key": "ctrl-j",
"command": "workbench.action.navigateDown"
},
{
"key": "space ,",
"command": "workbench.action.showAllEditors",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
},
{
"key": "space e",
"command": "runCommands",
"args": {
"commands": [
"workbench.action.toggleSidebarVisibility",
"workbench.files.action.focusFilesExplorer"
]
},
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus) && !sideBarFocus"
},
{
"key": "space e",
"command": "runCommands",
"args": {
"commands": [
"workbench.action.toggleSidebarVisibility",
"workbench.action.focusActiveEditorGroup"
]
},
"when": "sideBarFocus && !inputFocus"
},
{
"key": "space e",
"when": "vim.mode == 'Normal' && editorTextFocus && foldersViewVisible",
"command": "workbench.action.toggleSidebarVisibility"
},
{
"key": "tab",
"command": "workbench.action.nextEditorInGroup",
"when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
},
{
"key": "shift-tab",
"command": "workbench.action.previousEditorInGroup",
"when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
},
{
"key": "s h",
"command": "workbench.action.splitEditor",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
},
{
"key": "s v",
"command": "workbench.action.splitEditorDown",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
},
// Coding
{
"key": "space c a",
"command": "editor.action.codeAction",
"when": "vim.mode == 'Normal' && editorTextFocus"
},
{
"key": "shift-k",
"command": "editor.action.moveLinesUpAction",
"when": "vim.mode == 'VisualLine' && editorTextFocus"
},
{
"key": "shift-j",
"command": "editor.action.moveLinesDownAction",
"when": "vim.mode == 'VisualLine' && editorTextFocus"
},
{
"key": "shift-k",
"command": "editor.action.showHover",
"when": "vim.mode == 'Normal' && editorTextFocus"
},
{
"key": "space c r",
"command": "editor.action.rename",
"when": "vim.mode == 'Normal' && editorTextFocus"
},
{
"key": "space c s",
"command": "workbench.action.gotoSymbol",
"when": "vim.mode == 'Normal' && editorTextFocus"
},
{
"key": "space b d",
"command": "workbench.action.closeActiveEditor",
"when": "(vim.mode == 'Normal' && editorTextFocus) || !inputFocus"
},
{
"key": "space space",
"command": "workbench.action.quickOpen",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
},
{
"key": "g r",
"command": "editor.action.goToReferences",
"when": "vim.mode == 'Normal' && editorTextFocus"
},
{
"key": "g i",
"command": "editor.action.goToImplementation",
"when": "vim.mode == 'Normal' && editorTextFocus"
},
{
"key": "space g g",
"command": "runCommands",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)",
"args": {
"commands": ["workbench.view.scm", "workbench.scm.focus"]
}
},
{
"key": "space s g",
"command": "workbench.action.findInFiles",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
},
{
"key": "ctrl-n",
"command": "editor.action.addSelectionToNextFindMatch",
"when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
},
// File Explorer
{
"key": "r",
"command": "renameFile",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
{
"key": "c",
"command": "filesExplorer.copy",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
{
"key": "p",
"command": "filesExplorer.paste",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
{
"key": "x",
"command": "filesExplorer.cut",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
{
"key": "d",
"command": "deleteFile",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
{
"key": "a",
"command": "explorer.newFile",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
{
"key": "s",
"command": "explorer.openToSide",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
{
"key": "shift-s",
"command": "runCommands",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus",
"args": {
"commands": [
"workbench.action.splitEditorDown",
"explorer.openAndPassFocus",
"workbench.action.closeOtherEditors"
]
}
},
{
"key": "enter",
"command": "explorer.openAndPassFocus",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceIsFolder && !inputFocus"
},
{
"key": "enter",
"command": "list.toggleExpand",
"when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && explorerResourceIsFolder && !inputFocus"
},
// Debug
{
"key": "space d a",
"command": "workbench.action.debug.selectandstart",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus) && debuggersAvailable"
},
{
"key": "space d t",
"command": "workbench.action.debug.stop",
"when": "vim.mode == 'Normal' && editorTextFocus && inDebugMode && !focusedSessionIsAttached"
},
{
"key": "space d o",
"command": "workbench.action.debug.stepOver",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus) && inDebugMode && debugState == 'stopped'"
},
{
"key": "space d b",
"command": "editor.debug.action.toggleBreakpoint",
"when": "vim.mode == 'Normal' && editorTextFocus"
},
{
"key": "space d e",
"command": "editor.debug.action.showDebugHover",
"when": "vim.mode == 'Normal' && editorTextFocus && inDebugMode && debugState == 'stopped'"
},
{
"key": "space d c",
"command": "workbench.action.debug.continue",
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus) && inDebugMode && debugState == 'stopped'"
}
]
@nikolovlazar
Copy link
Author

nikolovlazar commented Jun 5, 2024

I've just added three space e commands that toggle the visibility of the sidebar. Previously, the sidebar toggled correctly, but the focus didn't move to it when opening it, so we had to do <C-Right> to focus into it. After talking with @zdravkoiliev4814 in the comments of the YouTube video, we came up with three commands that handle toggling the visibility and also moving the focus. Really like how the sidebar behaves now!

@KevinNitroG
Copy link

KevinNitroG commented Jun 6, 2024

@nikolovlazar Hi. I just wonder if extension.multiCommand.execute could be replaced by runCommands? Because it's builtin, no need to install any extension.

{
  "key": "space e",
-  "command": "extension.multiCommand.execute",
+  "command": "runCommands",
  "args": {
-    "sequence": [
+    "commands": [
      "workbench.action.toggleSidebarVisibility",
      "workbench.files.action.focusFilesExplorer"
    ]
  },
  "when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus) && !sideBarFocus"
}

It works fine for me.

@nikolovlazar
Copy link
Author

@KevinNitroG yes it can! I'll update the keybindings. Thanks!

@nikolovlazar
Copy link
Author

Also, I just updated Coding > Shift J and K commands to happen when the vim mode is "VisualLine" only. Previously it was when it's not "Insert" but the Shift K command clashed with the "Hover" command.

@tanishqmanuja
Copy link

The extension author recommends adding keymaps in settings.json (link), Are there any downsides using keybindings.json ?

@KorigamiK
Copy link

The extension author recommends adding keymaps in settings.json (link), Are there any downsides using keybindings.json ?

I prefer using the keybindings.json instead of the vim specific configuration

@JiangpengLI86
Copy link

Thank you so much. This key mapping is fantastic.

@kunalshukla11
Copy link

@nikolovlazar
We can add one more mapping which is reveal in explorer to go direct to file in explorer
{
"key": "space r e",
"command": "runCommands",
"args": {
"commands": [
"workbench.action.toggleSidebarVisibility",
"workbench.files.action.focusFilesExplorer"
]
},
"when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus) && !sideBarFocus"
},

@JiangpengLI86
Copy link

It appears that when you are focused on a file, like a project file displayed in the explorer, and then open the explorer using 'space + e', you are unable to use 'shift + s' to open a file in a vertical split.

I've slightly modified that entry to make it work (VSCode version: 1.93.1).

  {
    "args": {
      "commands": [
        "workbench.action.newGroupBelow",
        "explorer.openAndPassFocus",
      ]
    },
    "command": "runCommands",
    "key": "shift-s",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },

Initially, the script functioned when it focused on certain external files, like settings.json, which were not displayed in the project explorer.

@sharjeelmazhar
Copy link

how can i set space jk to exit insert mode and enter normal mode?

@nikolovlazar
Copy link
Author

nikolovlazar commented Oct 22, 2024

Update: I've added a "Go To Implementation" keybinding on space g I:

  {
    "key": "space g i",
    "command": "editor.action.goToImplementation",
    "when": "vim.mode == 'Normal' && editorTextFocus"
  },

@ivo-toby
Copy link

ivo-toby commented Oct 29, 2024

Just wanted to say BIG BIG thanks for this. I usually work in NeoVim, but lately I've been working in Python notebooks, which is cumbersome using the default Jupyter web-interface. Working with VSCode for notebooks made life a bit easier, but these mappings make a big difference.. No more painful wrists! 🙇🏼‍♂️

Somehow g g was not working from the getgo, so I added

  {
    "key": "g g",
    "command": "cursorTop",
    "when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
  },

@imsakg
Copy link

imsakg commented Nov 28, 2024

You should use "vim.normalModeKeyBindingsNonRecursive" for writing shortcuts. Check this out.

@sumanth-lingappa
Copy link

sumanth-lingappa commented Dec 23, 2024

@nikolovlazar , what's the difference between goToImplementaiton and revealDefinition?

because, in my case, the revealDefinition is showing all the implementations of the interface.

@leviFrosty
Copy link

leviFrosty commented Jan 22, 2025

You should use "vim.normalModeKeyBindingsNonRecursive" for writing shortcuts. Check this out.

Although it's true that the VIM extension has this feature, using the settings.json/vim.normalModeKeyBindingsNonRecursive setting has a few drawback. keybinds.json file is preferred for almost all situations.

  1. Keybinds within settings.json/vim.normalModeKeyBindingsNonRecursive only register when within normal mode in an editor. There are many situations where we want to use keybinds outside an editor, like space e. That simply isn't possible through the VIM extension.
  2. Using this abstraction is inherently slower than the built-in VSCode keybinds.json and relies on the VIM extension to be loaded before keybinds can be pressed.

keybinds.json is the correct choice even if it's not as concise to write your configs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment