Keybinding forms

You can define the bindings in four different ways. It is also possible to combine them freely.

Single Command

The simplest way is to map a key to a single command. This has the format:

"<binding>": "<command>"

The <binding> specifies the sequence of keys to press (and possibly the mode), and <command> is any valid VS Code command. You can see the list of all available commands by opening global settings with command Preferences: Open Default Keyboard Shortcuts (JSON).

The example in the previous section maps the i key to the modalkeys.enterInsert command.

Commands with Arguments

Some commands take arguments. For example cursorMove which allows you to specify which direction and how much cursor moves. These commands can be executed by defining an object with predefined properties:

"<binding>":  {
    "<command>": { ... }
    "repeat": number | "<expression>"
}

The <command> is again a valid VS Code command. Any arguments you wish to pass to this command should be placed inside the curly brackets. ModalKeys evaluates any argument strings that contain __ as JavaScript expressions, and replaces the argument value with the result of this evaluation. The following variables are recognized during this evaluation.

VariableTypeDescription
__filestringThe name of the current file
__linenumberThe line number of cursor location
__colnumberThe column number of cursor location
__charstringThe character under the cursor
__languagestringThe languageId of the current file
__selectionsSelection[]The selection objects of the current editor
__selectionSelectionThe primary selection object of the current editor
__selectionstrstringThe text of the primary selection
__wordstr .stringSame as __selectionstr unless there is no selection, and then this is equal to the current word under the cursor.
__selectingboolTrue if text should be selected (e.g. we're in visual mode).
__modestringA string specifying the current mode
__countnumberA number indicating the prefixed numerical values in front of a command: see below.
__capturedstringThe list of captured keys following captureChar

Below is an example that leverages string evaluation. It maps the key o to a command that moves the cursor to the end of the line. It also selects the jumped range, if we have a selection already active.

"o": { "cursorMove": { to: 'wrappedLineEnd', select: '__selecting' } },

Numeric arguments

When you type a modal command you can prefix it with numbers: these are passed using the __count variable to your command.

For example, the following would bind h to move left (like vim) in all modes.

    "h": { "cursorMove": { to: 'left', value: '__count' } },

Because value is specified as __count, if you typed 12h, the cursor would move 12 characters to the left.

Repeating commands

The repeat property of a command allows you to run the command multiple times. If it's a number, it should indicate the number of times to repeat the command. If it's a string it is evaluated as a JavaScript expression, and should evaluate to a numeric or boolean value. If numeric, the command repeats the given number of times. If boolean, it yields while-loop behavior: the command will continue to be repeated until it evaluates to false.

Shared command name

You can specify a series of bindings that all use the same command, each with different arguments to that same command. Use the following format.

"::using::<command>": {
    "<binding1>": { ... },
    "<binding2>": { ... },
    ...
}

Each binding is bound to the command <command>, passing the arguments specified for that binding ({...}).

Sequence of Commands

To construct more complex operations consisting of multiple steps, you can define command sequences. Commands in a sequence will be run one after another. A sequence is defined as an array.

"<binding>": [ <command1>, <command2>, ... ]

In above, <command> can assume any of the four command types.

The next example maps the f key to a command sequence that first deletes the selected text and then switch to insert mode. It corresponds to the c command in Vim.

"f": [
    "deleteRight",
    "modaledit.enterInsert"
],

Conditional Commands

For even more complex scenarios, you can define commands that run different commands depending on a specified condition. The most common use case for this is to run a different command when selection is active. The format of a conditional command is:

"<binding>":  {
    "if": "<condition>",
    "then": <command1>,
    "else": <command2>,
}

Here <condition> can be any valid JavaScript expression. You can use variables listed in the "Commands with Arguments" section in the expression. If the expression evaluates to true, <command1> will be executed, if false, <command2> will be run. Commands can be of any kind: a single command, sequence, or command with arguments.

Below is an example that moves cursor one word forward with w key. We use the __mode variable to determine if we're in visual mode. If so, we extend the selection using cursorWordStartRightSelect command, otherwise we just jump to next word with cursorWordStartRight.

"w": {
    "if": "__mode == 'visual'",
    "then": "cursorWordStartRightSelect",
    "else": "cursorWordStartRight"
},

Debugging Keybindings

If you use a javascript file to define your bindings you can place console.log statements in the file and they will show up under the ModalKeys output log. Any errors encountered when parsing or evaluating your bindings will also show up here.

You can find this output by opening View - Output and then choosing the ModalKeys from the drop-down menu.

Documenting a Keybinding

If you wish your own keybindings to leverage the visual documentation feature of ModalKeys you will also need to add a ::doc:: entry for each command. These take the following form:

"::doc::<binding>": { kind: <kind>, label: <label>, detail: <detail> }

The kind is a string defining a broad category of keys and determines the color of the key. The label is a string that will show up on the key: it should be very short: a good guideline is ≤2 words, ≤6 chars per word. Unicode can be useful in shortening the key lable (e.g. → and ←). The detail entry is a string description that will show up when you hover the mouse over the keybinding. It should explain in much more detail what the key does. The description for the kind will also be included, so the detail string should not be redundant with this more general information.

You also need to document the different command kinds used in your keymap. These are specified as an additional field of the main object of your keybindings file, called docKinds and takes the following format.

module.exports = {
docKinds: [
    { name: <kind1>, description: <str> },
    // etc...
],

keybindings: {
    // all keybindings...
}
}

The colors for doc kinds are determined by their order in docKinds, and are selected from a carefully defined Batlow color scheme. There are 5 such colors, and they are chosen to be friendly to color-blind viewers by consistently varying the luminance. You can also switch to a more vibrant color brewer set, which has 8 distinct colors. This set is not as accessable to color-blind viewers.

If you have more kinds than avaialble colors in your documentation, the colors will be cycled (i.e. the 6th kind will be colored the same as the 1st if you use the color-blind friendly option).

Leaders

If your keybindings include a sequence of multiple keys (e.g. gj), you should also document the general role of a given sequence prefix. For example, the vim bindings include the following ::doc:: entry:

"::doc::g": { kind: "leader", label: "more actions", detail: "additional commands (mostly actions)" },
View on GitHub