Skip to content

Key bindings

Every action in Tyrn can be bound to a key. You can start from a familiar editor’s keymap, change bindings in a visual editor, or write them by hand in keymap.json.

Keys in these docs use the cmd-shift-p style: modifiers joined with -, keys in a sequence separated by a space.

Base keymaps

If you come from another editor, set base_keymap in settings.json:

settings.json
{ "base_keymap": "VSCode" }

Supported values are "VSCode", "Atom", "JetBrains", "SublimeText", "TextMate", "Emacs", "Cursor" and "None". "None" turns off every default binding. Remove the setting to go back to Tyrn’s own keymap.

Turn on Vim or Helix bindings with vim_mode or helix_mode.

Keymap editor

Open it with cmd-k cmd-s, or run tyrn: open keymap. It lists every action with its current binding. To change a binding, double-click a row or select it and press enter, press the new keys, then confirm with enter. Your changes are written to keymap.json.

Task Keybinding
Search actions cmd-f
Search by keystroke cmd-alt-f
Show only conflicts cmd-alt-c
Edit the selected binding enter
Add another binding for the action alt-enter
Copy the action name cmd-c
Copy the context cmd-shift-c
Open keymap.json cmd-e

To see the default bindings, run tyrn: open default keymap.

keymap.json

Your keymap file is ~/.config/tyrn/keymap.json. Open it with tyrn: open keymap file. It is a list of binding groups. A group without a context is always active.

keymap.json
[
{
"bindings": {
"ctrl-right": "editor::SelectLargerSyntaxNode",
"ctrl-left": "editor::SelectSmallerSyntaxNode"
}
},
{
"context": "ProjectPanel && not_editing",
"bindings": {
"o": "project_panel::Open"
}
}
]

Find action names in the keymap editor, or by searching the command palette. An action name has the form namespace::ActionName.

Most actions are bound as a string. Some take arguments and are bound as an array: "cmd-1": ["workspace::ActivatePane", 0].

Key syntax

A binding can be one keypress or a sequence typed in order. Separate keypresses with a space.

  • Modifiers are ctrl-, cmd-, alt- (the option key on macOS), shift- and fn-.
  • A key is a single character, or a name such as tab, enter, escape, f1 or space.
  • shift- works with letters, as in shift-g for capital G. For punctuation, write the character itself.
keymap.json
{
"bindings": {
"cmd-k cmd-p": "workspace::ReopenLastPicker",
"space e": "editor::ShowCompletions",
"shift shift": "file_finder::Toggle"
}
}

shift shift matches pressing and releasing shift twice. Bindings on a bare modifier fire on release.

Contexts

A binding group can have a context that limits where it applies. Tyrn’s contexts form a tree. The root is Workspace, which holds panes and docks, which hold editors, panels and so on. A binding is active only when its context matches the focused part of the tree.

To see what is active right now, run dev: open key context view. It prints something like:

Workspace os=macos
Pane
Editor mode=full extension=md vim_mode=insert

Context expressions can use:

  • X && Y and X || Y
  • !X
  • (X) to group
  • X > Y, true when an ancestor matches X and this node matches Y
  • key == value and key != value for attributes such as mode, extension, os and vim_mode

Examples:

Context Matches
Editor Any editor, including small inputs
Editor && mode == full The main code editors
Editor && extension == md Editors showing Markdown files
!Editor && !Terminal Anywhere except an editor or a terminal
os == macos > Editor Editors on macOS

Attributes belong only to the node that sets them. To match an attribute that a parent sets, use >, for example debugger_stopped > Editor.

Precedence

When several bindings match the same keys:

  1. A binding on a deeper node wins. An Editor binding beats a Workspace binding. A group with no context matches at the lowest level.
  2. At the same depth, the binding defined later wins. Your keymap.json loads after the defaults, so yours win.

If one binding is a prefix of another, such as ctrl-w and ctrl-w left, Tyrn waits a moment after ctrl-w to see whether you type more. Pending keys show in the status bar. Hover the indicator to list what can still match.

To list the options without leaving a half-typed sequence, press alt-/. To show a bigger pop-up automatically, set:

settings.json
{ "which_key": { "enabled": true, "delay_ms": 1000 } }

Disable a binding

Bind a key to null to make it do nothing in a context.

keymap.json
[
{
"context": "Workspace",
"bindings": { "cmd-r": null }
}
]

Send keystrokes

workspace::SendKeystrokes replays a list of keystrokes. Use it to map one key to several.

keymap.json
[
{
"bindings": {
"alt-down": ["workspace::SendKeystrokes", "down down down down"]
}
},
{
"context": "Editor && vim_mode == insert",
"bindings": {
"j k": ["workspace::SendKeystrokes", "escape"]
}
}
]

Asynchronous steps such as opening the command palette, talking to a language server or the network happen only after all the keystrokes are sent, so you can’t chain keystrokes after them. At most 100 keystrokes are sent at once.

Other keyboard layouts

On layouts that can’t type all ASCII characters without alt, set "use_key_equivalents": true on a binding group so the group matches the character on the key rather than the one produced by alt.

keymap.json
[
{
"use_key_equivalents": true,
"bindings": { "ctrl->": "editor::Indent" }
}
]

Task keys

Bind a key to run a task from tasks.json. See Tasks.