Embercode
✕
VS CodeLesson 8 of 10

35 min read

Custom keyboard shortcuts

Why this matters

A keyboard shortcut is a key press that runs a command. A command is a named action inside VS Code, such as "delete this line" or "show the sidebar". VS Code has hundreds of them, and each one can have a key attached.

The previous lesson showed how to record a shortcut through the screen. That works for one key at a time. Editing the file by hand is better when you want to see every change in one place, remove a key that gets in your way, or find out why a key does nothing. This lesson covers those three jobs.

The file and its parts

Open the file from the Command Palette (Ctrl+Shift+P). Run Preferences: Open Keyboard Shortcuts (JSON). The file is called keybindings.json. It is your personal file: it lives on your computer, not in the project folder, so it applies to every project you open.

The file holds a list. The list starts with [ and ends with ]. Each item in the list is a rule, written as an object with curly braces. A rule has up to three fields:

  • key is the key or keys to press, written in lower case with plus signs between them. For example, ctrl+alt+b means Control, Alt and B pressed together.
  • command is the name of the action to run. Its formal name is the command ID. For example, editor.action.deleteLines deletes the current line.
  • when is optional. It says in which situation the rule works. Without it, the rule works everywhere.

Here is the smallest useful file. It has one rule and nothing else:

[
  {
    "key": "ctrl+alt+b",
    "command": "workbench.action.toggleSidebarVisibility"
  }
]

Two words in that example need a definition. A command ID is the text that names a command, usually with dots in it. A JSON file is a plain text file where data is written with square brackets for lists, curly braces for objects, double quotes around text, and commas between items. You do not need more than that to edit this file.

Finding the command ID

Nobody memorises command IDs. There are two ways to look them up.

The first is the file of built-in rules. Run Preferences: Open Default Keyboard Shortcuts (JSON) from the palette. It lists every rule VS Code ships with, including its key and command ID. The file is read-only, so you cannot change it there, but you can search it with Ctrl+F.

The second is the Keyboard Shortcuts page, which the previous lesson covered. In that page, right-click a row and choose Copy Command ID. Then paste it into your file.

Worked example 1: add one rule by hand

The goal: a key that hides or shows the sidebar. The built-in key for this is Ctrl+B on Windows and Linux. You will add a second key, Ctrl+Alt+B, that does the same job.

Open keybindings.json and write this list:

[
  {
    "key": "ctrl+alt+b",
    "command": "workbench.action.toggleSidebarVisibility"
  }
]

Save the file with Ctrl+S. VS Code reads the file again each time you save it, so the new rule works straight away. Press Ctrl+Alt+B. The sidebar hides or shows, the same as Ctrl+B.

If the file already has a rule, add a comma after the closing brace of the rule before yours. Here is the same file with two rules:

[
  {
    "key": "ctrl+shift+d",
    "command": "workbench.view.debug"
  },
  {
    "key": "ctrl+alt+b",
    "command": "workbench.action.toggleSidebarVisibility"
  }
]

The comma between the two closing and opening braces separates the two rules. The last rule has no comma after it.

Worked example 2: remove a default key

Sometimes you do not want a built-in key at all. For example, you use Ctrl+B for a different program, and the sidebar toggle gets in the way. To remove the built-in key, write a rule whose command starts with a minus sign:

[
  {
    "key": "ctrl+b",
    "command": "-workbench.action.toggleSidebarVisibility"
  }
]

The minus sign means "remove the default rule that has this key and this command". The key and the command must match the default exactly. If you type the key ctrl+b but the command without the minus sign, you have added a second rule, and Ctrl+B still works.

Now Ctrl+B does nothing in VS Code, unless another rule uses it. The removal rule does not delete the default rule from the built-in file. It only switches it off while your file has the rule.

To move the key instead of removing it, keep the removal rule and add the new key next to it:

[
  {
    "key": "ctrl+b",
    "command": "-workbench.action.toggleSidebarVisibility"
  },
  {
    "key": "ctrl+alt+b",
    "command": "workbench.action.toggleSidebarVisibility"
  }
]

Read this file top to bottom. The first rule turns off Ctrl+B for the sidebar. The second turns on Ctrl+Alt+B for it. The result is that the sidebar moves from one key to the other.

Deleting the removal rule later brings the default key back. Deleting your own added rule does not, because it was a separate rule.

Worked example 3: the when clause

Some keys are useful in two places with two different jobs. Take Ctrl+Alt+K. In the text editor you want it to delete the current line. In the terminal you want it to clear the screen. Two rules can share one key if each one has a when clause that says where it applies:

[
  {
    "key": "ctrl+alt+k",
    "command": "editor.action.deleteLines",
    "when": "editorTextFocus"
  },
  {
    "key": "ctrl+alt+k",
    "command": "workbench.action.terminal.clear",
    "when": "terminalFocus"
  }
]

The when value is a condition built from context keys. A context key is a named fact about the screen that VS Code keeps up to date. Two examples used here:

  • editorTextFocus is true when the cursor is inside a text editor.
  • terminalFocus is true when the cursor is inside the terminal.

If the cursor is in an editor, the first rule matches and the second does not. If the cursor is in the terminal, it is the other way round.

A condition can combine facts with three operators:

  • && means both facts must be true, for example editorTextFocus && !terminalFocus.
  • || means at least one fact must be true, for example editorTextFocus || terminalFocus.
  • ! before a name means the fact must be false. !terminalFocus means "the cursor is not in the terminal".

A when clause only checks the name you write. If you type editorTextFocos with a typo, no error appears, and the rule never applies. This is a common reason a rule with a when clause does nothing.

Worked example 4: a rule for one language

The goal: a key that comments out the current line, but only in Python files. The command ID for toggling a line comment is editor.action.commentLine. The example uses Ctrl+Alt+3, a key that is unlikely to be taken already. Check it on the Keyboard Shortcuts page before you rely on it:

[
  {
    "key": "ctrl+alt+3",
    "command": "editor.action.commentLine",
    "when": "editorLangId == python && editorTextFocus"
  }
]

editorLangId is a context key that holds the language of the open file. Its value is a short name in lower case, such as python, markdown or javascript. The == operator asks whether the value is equal to the word after it. The && joins two conditions, and both must be true.

Here is what happens in three places:

  • In a Python file with the cursor in the editor, both conditions are true, and Ctrl+Alt+3 comments the line.
  • In a JavaScript file with the cursor in the editor, the first condition is false, so this rule does not run.
  • In a Python file with the cursor in the terminal, the second condition is false, so this rule does not run either.

Combining the two checks like this keeps a key from doing something surprising in a place where you did not expect it. The rule is still active in every file, but it answers only in the files that match.

Undoing a change

Your rules are the only part of the list you can change, so undoing a change means editing your own list. To undo a rule you added, delete its object from the list, including its braces, and save. To undo a removal rule, delete the line with the minus sign, and the default key comes back.

Before a bigger edit, select everything in the file and paste it into a scratch file. If an edit breaks the list, you can put the old text back.

Finding a conflict

A conflict happens when two rules want the same key and both could run. Sometimes you see it at once, because the wrong command runs. Other times, a key simply does nothing, because another rule is taking its place. Here is a method that works in both cases.

Step 1. Write down the key that misbehaves and what you expected to happen.

Step 2. Open the Keyboard Shortcuts page with Ctrl+K Ctrl+S. In the search box, type @key: followed by the key, for example @key:ctrl+alt+b. The list now shows only the rules that use that key. Each row has a Source column (User means your file, Default means built in) and a When column with the condition.

Step 3. Look at the rows. There are three cases:

  • Only one row, source User, and the command is the one you expected. The key is not the problem. Look at the when clause or the focus, as the next step explains.
  • Two rows, both with an empty When column. They always compete. Remove one, or give one a when clause that separates them.
  • Two rows with different when clauses that can both be true at once. For example, editorTextFocus and editorHasSelection, which are both true when text is selected in an editor. Narrow one of them.

Step 4. Check the focus. A when clause such as terminalFocus is false while the cursor is in the editor. If your rule works in one place and not the other, the rule is fine and the cursor is in the wrong place.

Step 5. If you still cannot tell which rule runs, run Developer: Toggle Keyboard Shortcuts Troubleshooting from the palette. VS Code then writes a message about each key press into the Developer Tools console, which you open from the Help menu with Toggle Developer Tools. The messages show which key was pressed and which rule matched it. Turn the troubleshooting off again when you are done, because it writes a lot of text.

When two of your own rules have the same key and both can match, the rule that comes later in the file is the one that runs. Your rules also win over the built-in rule for the same key. These two facts are enough to predict most results. You can check them yourself with two rules on one key.

Common mistakes

A missing comma between rules

The file is a list, so rules need a comma between them. Here is a file with one comma missing:

[
  {
    "key": "ctrl+shift+d",
    "command": "workbench.view.debug"
  }
  {
    "key": "ctrl+alt+b",
    "command": "workbench.action.toggleSidebarVisibility"
  }
]

If you check this file with Node's JSON parser, it reports:

SyntaxError: Expected ',' or ']' after array element in JSON at position 77 (line 6 column 3)

The message is from Node's JSON parser, run on this exact file. VS Code marks the problem with a red underline and its own text. Both point at line 6, the opening brace after the missing comma on line 5.

A key written with a hyphen

VS Code writes keys with a plus sign between the parts. ctrl-alt-b is not written that way, so the rule does not match the key you press. Compare the key with the one in the Keyboard Shortcuts page.

A typo in the command ID

workbench.action.toggleSidebarVisiblity (with the letters iblity instead of ibility) is not a command. The rule does nothing, and no warning appears in the editor. Copy the command ID from the default file or from the Keyboard Shortcuts page instead of typing it from memory.

A removal rule without the minus sign

If you wrote the command as workbench.action.toggleSidebarVisibility for a default key, you did not remove anything. You added a second rule that runs the same command on the same key. Put the minus sign in front of the command ID to remove the default one.

Forgetting to save

The rule only works after the file is saved. The title of the editor tab shows a dot when the file has unsaved changes. Press Ctrl+S and check the dot is gone.

Editing the default file

The file from Preferences: Open Default Keyboard Shortcuts (JSON) lists the built-in rules as a reference. Changes saved there do not change how keys work. Write your rules in the file from Preferences: Open Keyboard Shortcuts (JSON), which is the one VS Code reads for your changes.

A key that works in one place but not another

A rule with a when clause can be correct and still look broken. If Ctrl+Alt+3 comments a line in the editor but not in the terminal, the rule is working as written. Check the focus first, then the when clause, as the conflict steps above describe.

Practice

Exercise 1. Write a rule that deletes the current line with Ctrl+Alt+D, but only when the cursor is in a text editor. The command ID is editor.action.deleteLines.

Show the answer

[
  {
    "key": "ctrl+alt+d",
    "command": "editor.action.deleteLines",
    "when": "editorTextFocus"
  }
]

Exercise 2. Turn off the default Ctrl+B key for the sidebar, and add nothing else.

Show the answer

[
  {
    "key": "ctrl+b",
    "command": "-workbench.action.toggleSidebarVisibility"
  }
]

Exercise 3. Your file has these two rules. The cursor is in the terminal and you press Ctrl+Alt+K. Which command runs?

[
  {
    "key": "ctrl+alt+k",
    "command": "editor.action.deleteLines",
    "when": "editorTextFocus"
  },
  {
    "key": "ctrl+alt+k",
    "command": "workbench.action.terminal.clear",
    "when": "terminalFocus"
  }
]

Show the answer

workbench.action.terminal.clear. The first rule needs editorTextFocus to be true, and the cursor is in the terminal, so that fact is false. The second rule needs terminalFocus, which is true.

Exercise 4. Find the two mistakes in this file.

[
  {
    "key": "ctrl-shift-d",
    "command": "workbench.view.debug"
  },
  {
    "key": "ctrl+alt+b",
    "command": "workbench.action.toggleSidebarVisiblity"
  }
]

Show the answer

The first key uses hyphens. It must use plus signs: ctrl+shift+d. The second command ID is misspelled. The correct ID is workbench.action.toggleSidebarVisibility, with ibility at the end.

Exercise 5. You want the built-in key for toggling the terminal, Ctrl+backtick, to stop working. You want Ctrl+Alt+T to do the same job instead. The command ID for the terminal toggle is workbench.action.terminal.toggleTerminal. Write the two rules.

Show the answer

[
  {
    "key": "ctrl+`",
    "command": "-workbench.action.terminal.toggleTerminal"
  },
  {
    "key": "ctrl+alt+t",
    "command": "workbench.action.terminal.toggleTerminal"
  }
]

Exercise 6. Add a rule that deletes the current line with Ctrl+Alt+M, but only in Markdown files and only when the cursor is in the editor.

Show the answer

[
  {
    "key": "ctrl+alt+m",
    "command": "editor.action.deleteLines",
    "when": "editorLangId == markdown && editorTextFocus"
  }
]

Summary

  • keybindings.json is a list of rules. Each rule has a key, a command, and an optional when.
  • Find command IDs in the default keybindings file, or copy them from the Keyboard Shortcuts page.
  • A command ID that starts with a minus sign removes the default rule with the same key and command.
  • A when clause checks context keys such as editorTextFocus and terminalFocus. A typo in its name makes the rule silently never apply.
  • To debug a clash, search for the key with @key: on the Keyboard Shortcuts page and read the Source and When columns.
  • A rule works only after the file is saved.

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Dev containers and WSL