8. Key Bindings

key binding

A run-time data structure that connects a keypress or keypress sequence with the Command it executes. Key bindings in Sublime Text are defined in .sublime-keymap files. Keymap files use JSONC format, with the top-level structure being an array of key-binding definition objects.

key-binding definition

A JSONC object that defines:

  • the keypress or keypress sequence to be bound;

  • the name of the target Command;

  • any arguments to be passed to that Command; and

  • the editing circumstances under which the binding is to be applied.

The editing circumstances that can be specified can be related to:

  • how many selections there are, and whether any text is selected or not;

  • the text that is selected or nearby;

  • type of environment a selection is in (e.g. in a string or keyword in source code);

  • whether various popup lists, panels or overlays are visible or have keyboard focus;

  • whether a certain setting in that View has a certain value;

  • etc.

See Test Names for details.

“Applying” or “using” a selected key binding amounts to running the command it is bound to, passing the arguments in the binding definition (if any).

Technically speaking, key bindings are “instantiated” at run time from key-binding definitions. However, to simplify expression, the term “key binding” is used below to refer to both, and you can discern which meaning is being used based on the topic being discussed.

8.2. Keymap Files

.sublime-keymap files contain a JSONC format list (array) of Key-Binding Definitions.

Like Preferences, default key-binding definitions can be viewed by:

Preferences ‣ Key Bindings

This opens a window with a pair of .sublime-keymap files shown side by side. The left side is read-only, and editable user-overrides for those key bindings are on the right side of that window. Creating individual overrides is done by copying the default key-binding object from the left window and pasting and modifying it on the right side.

Sublime Text recognizes only 2 keymap file-naming patterns:

  • Default.sublime-keymap, and

  • Default ($platform).sublime-keymap,

where $platform is Windows, OSX, or Linux.

Example:

Default (Windows).sublime-keymap

Default.sublime-keymap files are used by most language-specific Packages, where the keypresses that apply should be identical across all platforms. Default ($platform).sublime-keymap files are only loaded on the indicated platform, and tends to be used by most other types of Packages.

8.3. How Key Bindings are Chosen

During editing, as a response to every keypress, Sublime Text uses an algorithm to select which key binding to apply. The below is a description of that algorithm.

Definition:

A key binding’s “context” entry restricts the editing circumstances in which that binding will be used.

Thus, a binding with no “context” entry is a “catch-all” binding that applies in ALL editing circumstances.

Each keypress (i.e. key combination) has its own (possibly-empty) list of bindings that are associated with it.

If that list is not empty, the list is searched backwards (from bottom up), and the first binding encountered with a “context” entry that matches the current editing circumstances (i.e. “editing context”) is selected and used.

That is the algorithm.

Thus, the ordering of those lists is vital to Sublime Text’s response to each keypress.

Here is how that ordering is controlled:

  • Each binding lists for each keypress (key combination) starts out being empty.

  • Each binding list for keypresses associated with printable characters is effectively “pre-loaded” with a “catch-all” binding that, when no other bindings apply, causing each such character to be inserted into the View that currently has keyboard focus (assuming it is not in “read-only” mode). (It is not known by this author whether actual “bindings” are pre-loaded or whether the default behavior is merely built into Sublime Text’s internal code [more likely], but what is know is that this is its behavior—so you can think with how it is going to behave.)

  • The list of .sublime-keymap files is loaded in a controlled order. Each binding definition encountered (passing through the file from top to bottom) is appended to the list for its respective keypress.

Thus, the order those files are loaded in is vital, and the ordering of each key-binding definition within each file is vital.

When .sublime-keymap files are edited, to stay current, this sequence is repeated each time a keymap file is saved.

To be most useful, each list should be comprised of bindings with the most “general” bindings at the top (with the least-restrictive “context” entries), followed by bindings with progressively more restrictive “context” entries.

To make .sublime-keymap files more maintainable, the typical arrangement is that all the bindings for one keypress (one key combination) are listed together, with the most general first, followed by binding definitions with more restrictive “context” entries (i.e. more specific). The binding definitions for the " and ' keypresses (with no Ctrl, Alt or Shift modification keys) in the Default/Default ($platform).sublime-keyamp files are good examples of this.

8.3.1. Keymap File Loading Order

In the below list, the terms <install_path> and <data_path> are used to indicate specific directories on your system. Follow these links to see their definitions.

  1. Key bindings from the Default package are always loaded first (from compressed file <install_path>/Packages/Default.sublime-package) in

    • <install_path>/Packages/Default ($platform).sublime-keymap

    where {$platform} is resolved to the name of the current platform.

  2. Bindings from all other shipped Packages are loaded next in alphabetical order by Package name (from compressed .sublime-package files) in

    • <install_path>/Packages/

  3. Bindings from installed Packages are loaded in alphabetical order by Package name (from compressed .sublime-package files in

    • <data_path>/Installed Packages/.

  4. Bindings from loose-file (override) Packages are loaded in alphabetical order by Package name in

    • <data_path>/Packages/<pkg_name>.

  5. Bindings from the User Package are always loaded last:

    • <data_path>/Packages/User/Default.sublime-keymap

    • <data_path>/Packages/User/Default ($platform).sublime-keymap

    where {$platform} is resolved to the name of the current platform.

Note

Wherever both of the above 2 files (Default.... and Default ($platform)....) exist in a Package, the platform-specific one wins if they contain 2 identical key bindings that do different things. Shipped and installed packages only ever have one of these files, but Sublime Text handles having both in the User Package gracefully with the above caveat.

8.4. Multi-Keypress Bindings

Multi-Keypress bindings are defined in .sublime-keymap files by having the “keys” entry contain more than one keypress in the list.

Examples:

{ "keys": ["ctrl+k", "ctrl+u"], ... }
{ "keys": ["ctrl+k", "ctrl+l"], ... }
{ "keys": ["ctrl+k", "ctrl+up"], ... }

Such bindings are implemented in order to expand the number of Commands that can be mapped to the keyboard. A common multi-keypress binding (through the history of text editors and word processors) begins with Ctrl-K. When Sublime Text receives this keypress the first time, it sees the fact that it is the leading key in at least one multi-keypress binding and does the following:

  • It arms the key-sequence state machine to watch subsequent keypresses to see if the sequence matches a binding.

  • If subsequent keypresses match one of the multi-keypress bindings, the binding list for that keypress sequence is searched backwards and if a context is found that matches the current editing context, that binding is used.

  • If subsequent keypresses do not match a multi-keypress binding:

    • The multi-keypress sequence is discarded and the multi-keypress state machine is reset.

    • It checks to see if there is a single-keypress binding for just Ctrl-K.

      • If so, the binding list for that keypress is searched backwards and if a key context is found that matches the current editing context, that binding is used.

      • Otherwise, nothing happens.

For an improved user experience, it is best not to mix single-keypress and multi-keypress bindings that use the same leading keypress. But if it happens by accident, Sublime Text handles it gracefully as outlined above.

8.5. Key-Binding Definitions

Keymap files contain an array of key-bindings definitions objects, each of which is a JSON mapping object (that translates to a Python dictionary) with a specific format that tells Sublime Text what keypress to bind to what Command, with what arguments, and under what editing circumstances they are to be applied.

Key-bindings definitions have this structure:

{
    "keys": "[<keypress_list>]",
    "command": "<command_name>",
    "args": {...}        // Optional:  required only if command requires it;
                         //            dictionary key names must match command arg names.
    "context": [         // Optional:  limits contexts in which binding will be applied.
        {<condition>},
        ...
    ]
}

Examples:

{ "keys": ["ctrl+z"], "command": "undo" },
{ "keys": ["ctrl+shift+z"], "command": "redo" },
{ "keys": ["ctrl+y"], "command": "redo_or_repeat" },
{ "keys": ["ctrl+u"], "command": "soft_undo" },
{ "keys": ["ctrl+shift+u"], "command": "soft_redo" },

// Auto-pair brackets
{ "keys": ["("], "command": "insert_snippet", "args": {"contents": "($0)"}, "context":
    [
        { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
        { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
        { "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\)|]|;|\\}|$)", "match_all": true }
    ]
},

The “args” key, if specified, depends upon the needs of the command. If you can find the Python code that defines the command (or find its documentation), you can see the arguments it expects.

If there is more than one context <condition>, they are joined by the AND operator.

If an OR operator needs to be applied, add additional key bindings that each have their own set of context conditions. Remember to order the different key binding definitions in order from most general to most specific, because this order is what determines the order of that keypress’ binding search list as discussed in the How Key Bindings are Chosen section above.

8.5.1. Keypress Lists

A <keypress_list> is a list of keypress strings. Keypress strings are comprised of:

[<modifier_key>+[<modifier_key>+]...]<main_key_name>

(Square brackets indicate optional parts.)

The order of the modifier keys does not matter to Sublime Text, although the default Windows .sublime-keymap file does use a particular order strictly. See below for details.

Examples:

["ctrl+shift+p"]
["ctrl+k", "ctrl+u"]

The “main key” must always appear last in each keypress.

8.5.1.1. Modifier Keys

A modifier key can be any of the following:

Modifier Key Name

Where It Is Used

ctrl or control

All platforms

alt

All platforms (⌥ Option (Alt) key on OSX)

shift

All platforms

super

All platforms ( key on Windows/Linux, ⌘ Command key on OSX)

primary

All platforms (Ctrl key on Windows/Linux, ⌘ Command key on OSX)

command

⌘ Command key on OSX

option

⌥ Option (Alt) key on OSX

While the order of the modifier keys is not constrained, it is customary to order them (when present): ctrl, alt then shift. In the OSX default keymap the modifier keys are found in a somewhat more varied order, although the super key tends to be first in the sequence when used.

8.5.1.1.1. Windows Modifier Keys

For Windows, by default, the OS hooks a large number of keypresses that involve the Windows (super) key. Because of this, the default keymap only uses the first three modifier keys (ctrl, alt, shift). This is reinforced by the Windows keyboard layout: these keys are close together, and going counter-clockwise, the sequence is: shift, ctrl, alt. If you want to free up your Windows key to give you more keyboard “real estate” (i.e. more available keypresses) to work with, it’s not hard: launch Group Policy Editor, then

User Config ‣ Admin ‣ Windows Components ‣ File Explorer

and directly under the File Explorer “folder”, open Turn off Windows Key hotkeys and set it to Enabled.

(Even with the Windows key turned off, the unmodified Windows key alone remains hooked by the OS.)

8.5.1.1.2. Linux Modifier Keys

The default Linux keymap is very similar to Windows: the modifier keys mapped in the default keymap are still restricted to ctrl, alt, and shift.

Per Wikipedia, “Many Linux distributions, including popular ones like Ubuntu, use the Ctrl-Alt key combination for various keyboard shortcuts, including task switching. However, the specific key combination for the task switcher can vary by desktop environment and user configuration.”

So in those cases, it appears to be impossible to successfully bind any Commands to key combinations that use Ctrl-Alt modifiers since the OS is hooking those key combinations.

Further, Linux Mint documentation shows an example of using Ctrl-Alt in its recommended choice of key combinations for system-level custom hotkeys. So, while it is possible to customize the hotkeys that Linux uses, if you are developing a Package and want to share it with Linux users, its probably best to stay completely away from using Ctrl-Alt modifier combination on Linux.

8.5.1.1.3. OSX Modifier Keys

The OSX default keymap freely uses all of the first 4 modifiers, and this keymap is very different from the Windows default keymap. Interestingly the keypresses used NEVER include super-ctrl-alt together, but all the other permutations are used. OSX users tend to prefer super, alt, ctrl, and finally shift as modifier keys, in that order. Like Windows, this is probably enforced by the keyboards themselves because that is the order of key layout starting closest to the space bar: super (command), alt (option), ctrl and then shift, in that order.

8.5.1.2. Main Key Names

When modifier keys are used in keypresses, these (unshifted) main key names must be used with them:

                                                    Alternate      Specialty
                Regular Key Names                   Symbol Names   Keyboards
--------------------------------------------------  -------------  -----------------
0   a   n   f1   ,   keypad0          up            backquote      close
1   b   o   f2   .   keypad1          down          equals         copy
2   c   p   f3   \   keypad2          left          forward_slash  cut
3   d   q   f4   /   keypad3          right         minus          find
4   e   r   f5   ;   keypad4          insert        plus           open
5   f   s   f6   '   keypad5          delete                       paste
6   g   t   f7   `   keypad6          home                         redo
7   h   u   f8   -   keypad7          end                          save
8   i   v   f9   =   keypad8          pageup                       sysreq
9   j   w   f10  [   keypad9          pagedown                     undo
    k   x   f11  ]   keypad_period    backspace
    l   y   f12      keypad_divide    tab                          browser_back
    m   z   f13      keypad_multiply  enter                        browser_favorites
            f14      keypad_minus     pause                        browser_forward
            f15      keypad_plus      break                        browser_home
            f16      keypad_enter     space                        browser_refresh
            f17      clear            escape                       browser_search
            f18                       context_menu                 browser_stop
            f19
            f20                                                    +  (See note.)

Spanish Keyboard Note

The Spanish keyboard has a separate unshifted key + which works with Ctrl-+ (unlike most other keyboards from around the world). It’s Shift-= produces ¡ (inverted exclamation point) instead of the + character. Thus, the need for both of these entries in the Default Linux and Windows keymap files:

{ "keys": ["ctrl++"], "command": "increase_font_size" }, // For Spanish keyboard
{ "keys": ["ctrl+="], "command": "increase_font_size" }, // For other keyboards

Shifted key names can also be used in key-binding definitions, but may not be used with modifier keys. Attempting to do so generates no errors, but does not work.

"  (  )  {  }                  # <-- These can be found in Default keymap
                               #     with contexts.

`  ~  !  @  #  $  %  ^  &      # <-- These are also bind-able (and should
*  _  +  |  :  "  <  >  ?      #     have contexts if used).

" "                            # <-- Works as an unmodified key,
                               #     but is redundant with `space`.
8.5.1.2.1. <character>

The [“<character>”] keypress is a specialty application, such as if your Plugin needs to temporarily either suppress or capture all keystrokes with a custom key context. This key name (with angle brackets) captures all printable keypresses. Example:

{
    "keys": ["<character>"],
    "command": "noop",
    "context": [{"key": "setting.lsp_suppress_input"}]
},

Any command bound to <character> key also receives an additional character argument containing the character that was pressed. This Command:

class CharacterTestCommand(sublime_plugin.TextCommand):
    def run(self, edit, character):
        print(f"In CharacterTestCommand({character})....")

Produced this output on hitting all the keys in this sequence: “abc Hello World! []{}-_=+;:’”,<.>/?|1234”

In CharacterTestCommand(a)....
In CharacterTestCommand(b)....
In CharacterTestCommand(c)....
In CharacterTestCommand( )....
In CharacterTestCommand(H)....
In CharacterTestCommand(e)....
In CharacterTestCommand(l)....
In CharacterTestCommand(l)....
In CharacterTestCommand(o)....
In CharacterTestCommand( )....
In CharacterTestCommand(W)....
In CharacterTestCommand(o)....
In CharacterTestCommand(r)....
In CharacterTestCommand(l)....
In CharacterTestCommand(d)....
In CharacterTestCommand(!)....
In CharacterTestCommand( )....
In CharacterTestCommand([)....
In CharacterTestCommand(])....
In CharacterTestCommand({)....
In CharacterTestCommand(})....
In CharacterTestCommand(-)....
In CharacterTestCommand(_)....
In CharacterTestCommand(=)....
In CharacterTestCommand(+)....
In CharacterTestCommand(;)....
In CharacterTestCommand(:)....
In CharacterTestCommand(')....
In CharacterTestCommand(")....
In CharacterTestCommand(,)....
In CharacterTestCommand(<)....
In CharacterTestCommand(.)....
In CharacterTestCommand(>)....
In CharacterTestCommand(/)....
In CharacterTestCommand(?)....
In CharacterTestCommand(\)....
In CharacterTestCommand(|)....
In CharacterTestCommand(1)....
In CharacterTestCommand(2)....
In CharacterTestCommand(3)....
In CharacterTestCommand(4)....

Function keys, named keys, and keypad keys were also tried, but did not cause the above Command to be run.

The LSP Package makes use of the <character> binding to momentarily suppress all keystrokes in a View while its lsp_suppress_input setting is true. It does this through the context:

"context": [
  { "key": "setting.lsp_suppress_input" }
]

Thus, to suppress input, it merely needs to set the View’s “lsp_suppress_input” setting to True momentarily during the period that keystrokes need to be suppressed.

Additionally, the Vintage Package makes extensive use of it during certain phases of its mode-based editing. (Vintage makes Sublime Text mimic the vi and vim mode-based editors. For readers not familiar, vi is a text editor that originated in the early days of computing on Unix systems. vim is its modern replacement on Linux. “Mode-based” means they have a “command mode”, “insert mode”, and several other states of operation. vim and neovim are quite popular with Linux users.)

8.5.2. Command Names

See Commands for details about how Command names are derived. Examples: new_window, new_file, close_file, insert. See Preferences ‣ Key Bindings for more examples.

8.5.3. Args

“args” entries define the arguments that will be passed to the Command the keypress is bound to.

Commands are run internally by locating the application Command object (more on this in under Plugins), and calling its run() method. Such methods normally have only self and edit parameters. However, it is possible that a command can require additional information to run, and it is in this case that “args” entries in key-binding definitions provide the additional information. The value of the “args” entry is a JSON mapping object (Python dictionary). Each key in that dictionary must be the name of one of the formal parameters of the run() method.

Example:

class MyInsertTextCommand(sublime_plugin.TextCommand):
    def run(self, edit, text, pos, event=None):
        """
        Arguments are passed as keyword arguments.

        :param text:   string to insert
        :param pos:    index into editing buffer
        :param event:  an object that comes from, for example, a mouse click,
                         that carries 'x' and 'y' window coordinates.
        """
        if event:
            pos = self.view.window_to_text((event['x'], event['y']))

        self.view.insert(edit, pos, text)

This Text Command will need an additional 2 or 3 arguments. If you were going to use this key binding definition to call it:

{
    "keys": "alt+f5",
    "command": "example_four",
    "args": {
        "text": "Hello, World!",
        "pos": 10
    }
}

then, as you can see, the “args” element there would pass the text and pos arguments to the command.

Alternately, although it is not traditional, your command can define a **kwargs parameter after edit and receive any number of arguments with any names you choose in kwargs as a dictionary. Normally, the Command itself knows what arguments to expect, but it is also possible to accept the kwargs dictionary and use the keys in that dictionary as part of the data the Command works with.

8.5.3.1. “Variables” in Args

Here and there you may run into things like this in a keymap:

{
    "keys": "alt+`",
    "command": "toggle_terminus_panel",
    "args": {
        "cwd": "${file_path:${folder}}"
    }
}

The above key-binding definition is related to the Terminus Package, and appears at 14:59 in this YouTube video.

To the astute reader, it might appear that something in Sublime Text might be converting those variables in the “args” “cwd” entry before passing it to the Command. However, this is not the case. In the above, what gets passed to the command is exactly this Python string to the Command’s cwd argument:

'${file_path:${folder}}'

The command itself has to parse that string and do something with those “variables”. See Commands for an example of how you can do this in your own Commands.

8.5.4. Contexts

The “context” entry of a key-binding definition restricts the editing circumstances (a.k.a. “editing context”) under which that key binding will be applied.

Note that this means that key bindings with NO “context” entries match ALL editing contexts. For this reason, as mentioned above, they should normally be earliest in your keymap file EXCEPT when they are intended to “catch all” or block other key bindings from being used.

The “context” entries in key-binding definitions enable the same keypress to do different things depending on the editing circumstances. This is how Sublime Text users and Package developers make one key combination apply any number of dynamically-determined tasks.

The syntax of a “context” entry in a key-binding definition is:

"context": [         // Optional:  limits contexts in which binding will be applied.
    {<condition>},
    ...
]

As shown in this syntax diagram, the value of a “context” entry is a JSON array of <condition> objects. All contained conditions must be true for the key binding to be applied.

8.5.4.1. Context Condition Syntax

<condition> elements from the above syntax diagram are JSONC objects having this format:

{
    // Condition to check
    "key": "<test_name>",       // Required:  see <test_name> below

    // Type of comparison
    "operator": "<operator>",   // Optional:  Default:  "equal"

    // Value to test against
    "operand": "<operand>",     // Optional:  Default:  true

    // When there are multiple selections (carets), this indicates whether the
    // test needs to be satisfied for all of them (true), or just one (false).
    "match_all": false,         // Optional:  Default:  false
}
8.5.4.1.1. Test Names

Available test names are shown in the tables below. The whole list is grouped by related testing logic. Items are sorted alphabetically within each group of tests.

Key to Tables:

Codes Used Within Table:

BOL:

Beginning of line.

EOL:

End of line.

SELOP:

A Selector Match operation is performed with the scope indicated by the “key” entry:

  • “selector” key gets scope from current caret position,

  • “eol_selector” gets scope from EOL.

Then the selector test string is provided by the “operand” value. Operator “equal” causes the condition to evaluate to TRUE for a selector MATCH, and FALSE for a MISMATCH, whereas operator “not_equal” reverses that sense.

Note: the “is_javadoc” test does not use the “operand” value because it has an implied selector “source comment.block.documentation”. At this writing (build 4200), only Java and JavaScript Syntaxes provide “comment.block.documentation” in their scopes. Hopefully the C language family and others will soon follow.

Example:

{ "key": "eol_selector", "operator": "equal",
  "operand": "text.html meta.tag.structure", "match_all": true }

Performs a selector-match operation with the scope at the end of each selection’s line, against the selector "text.html meta.tag.structure" and provides a selector MATCH only when the selection is:

  • in an HTML file, and

  • there is an open HTML tag at the end of the line that wraps onto the following line.

The operator “equal” or “not_equal” tests for a MATCH or MISMATCH respectively.

See Scope, Selectors and Context for more details about how selectors work.

LHS:

Left-hand side (of Boolean test), where typically the left-hand side is provided by the value of the context “key” entry, where “operator” provides the Boolean operator, and the value of the “operand” entry is the right-hand side. (See Operators below.)

Column Headings:

C:

commonly used

V:

related to selections in current View, or to just the View itself

S:

tests scope with selector strings

T:

related to text in or near selection(s)

N:

related to Snippets and the Snippet State Machine

W:

related to window state

A:

related to Sublime Text application

Type:

required “operand” data type

Op:

operator group (equ = equality; re = regex match, meaning one of the Equality Operator Names, or one of the Regex Match Operator Names respectively. See Operators below.)

MA:

whether “match_all” key is relevant; applies when num_selections > 1:

  • true means all selections must meet this condition for this key binding to be applied,

  • false means any selection can meet this condition for the key binding to be applied)

Selections Tests

Key (test name)

C

V

S

T

N

W

A

Type

Op

MA

Description

num_selections

C

V

int

equ

Number of selections in current View

selection_empty

C

V

bool

equ

x

Is current selection empty?

Scope Tests

Key (test name)

C

V

S

T

N

W

A

Type

Op

MA

Description

eol_selector

C

S

str

equ

x

SELOP with scope at EOL after each cursor

is_javadoc

S

bool

equ

x

Is caret in a comment beginning with /** [1]

selector

C

S

str

equ

x

SELOP with scope at each selection

Text Tests

Key (test name)

C

V

S

T

N

W

A

Type

Op

MA

Description

indented_block

T

bool

equ

x

Is current block indented? [3]

following_text

T

str

re

x

Text between left edge of selection and EOL [2]

preceding_text

C

T

str

re

x

Text between BOL and the left edge of selection

text

T

str

re

x

LHS = currently-selected text

This table of tests exclusively involves logic connected with the current View. Even the overlay_... and panel_... conditions only involve the View and not the Window, even though there are 2 panel_... conditions that DO involve the Window and they are listed in the “Window and Application” table below. This is because detection of Overlay and the particular Panel conditions below use the Views that are inputs (text boxes) in those Overlays and Panels, and the tests can be executed exclusively using information available from those Views.

Key (test name)

C

V

S

T

N

W

A

Type

Op

MA

Description

auto_complete_primed

V

bool

equ

Is actual auto-complete popup visible?

auto_complete_visible

V

bool

equ

Is auto-complete, mini-auto-complete or async-complete visible?

last_command

V

str

equ

LHS = name of last command run in View.

last_modifying_command

V

str

equ

LHS = name of last command that modified View’s buffer.

overlay_has_focus

V

bool

equ

Does an Overlay or Quick Panel have focus?

overlay_name

V

str

equ

LHS = name of current overlay, if any, [4]

overlay_visible

V

bool

equ

Is any Overlay or Quick Panel visible? [5]

panel_has_focus

V

bool

equ

Is any Panel visible with focus?

panel_type

V

str

equ

Is Panel with focus of type xxxx? [6]

popup_visible

V

bool

equ

Is any popup currently being displayed?

read_only

V

bool

equ

Is buffer attached to current View in read-only mode?

setting.xxxx

V

str

equ

LHS = value of setting applying to current View [7]

Snippet Tests

Key (test name)

C

V

S

T

N

W

A

Type

Op

MA

Description

has_next_field

N

bool

equ

In Snippet field list with subsequent fields?

has_prev_field

N

bool

equ

In Snippet field list with previous fields?

has_snippet

T

N

bool

equ

Can preceding word trigger a Snippet?

Window and Application Tests

Key (test name)

C

V

S

T

N

W

A

Type

Op

MA

Description

group_has_multiselect

W

bool

equ

Does View group have multi-select?

group_has_transient_sheet

W

bool

equ

Does View group have a transient sheet?

is_recording_macro

A

bool

equ

Is user currently recording a macro?

panel

W

str

equ

LHS name of current visible panel, if any

panel_visible

W

bool

equ

Is any Panel visible?

8.5.4.1.1.1. Custom Test Names

You can create your own key-binding test names in your own Packages through the use of the on_query_context() event callback. See EventListener.on_query_context(), ViewEventListener.on_query_context(), and OdatNurd’s [P101-13] Defining Custom Contexts... video for more details.

8.5.4.1.2. Operators

Note carefully that each of the “key” (test-name) values can be used with ONLY “equal” and “not_equal” OR one of the regex tests, but not both. For example, if you wanted to enable a key binding only when certain text (e.g. the string ‘hello’) was selected, if you did it like this:

{
    "keys": ["~"],
    "command": "insert",
    "args": {"characters": "test text"},
    "context": [
        { "key": "text", "operator": "equal", "operand": "hello" },
        //                            ^^^^^
        //                              +-- Doesn't work!
    ]
},

it simply won’t work. Reason: “text” requires one of the regex operators.

This one DOES work:

{
    "keys": ["~"],
    "command": "insert",
    "args": {"characters": "test text"},
    "context": [
      { "key": "text", "operator": "regex_match", "operand": "hello" },
        //                          ^^^^^^^^^^^
        //                               +-- Does work!
    ]
},

These restrictions are shown in the “Op” column in the tables above as either “equ” or “re”, which mean one of the Equality Operator Names, or one of the Regex Match Operator Names respectively.

8.5.4.1.2.1. Equality Operator Names
equal                (default) a fixed value (str, int or Boolean)
not_equal            a fixed value (str, int or Boolean)
8.5.4.1.2.2. Regex Match Operator Names
regex_match          a string (regex)
not_regex_match      a string (regex)
regex_contains       a string (regex)
not_regex_contains   a string (regex)
8.5.4.1.3. Operands (Values to Compare Against)

Operand values can be any of the below types. The required type is shown in the “Type” column in the tables above.

  • Boolean true (default)/false (in JSON these are lower case bare true and false)

  • string (includes Regular Expressions)

  • number

8.5.4.2. Creating Custom Conditions

You can create your own Context conditions by implementing a ViewEventListener class with the on_query_context() method implemented.

Note: the is_applicable() method must return True for it to be applicable in a particular View. See Event Listeners for more details about the is_applicable() method.

Here is a copy of the portion of the sublime_plugin.ViewEventListener class’ doc-string about the on_query_context() method:

on_query_context(key: str, operator: QueryOperator, operand: bool | str | int, match_all: bool) bool | None

Called when determining whether to trigger a key binding with the given context key. If the plugin knows how to respond to the context, it should return either True of False. If the context is unknown, it should return None.

Parameters:
  • key – The context key to query. This generally refers to specific state held by a plugin.

  • operator – The operator to check against the operand; whether to check equality, inequality, etc.

  • operand – The value against which to check using the operator.

  • match_all – This should be used if the context relates to the selections: does every selection have to match (match_all == True), or is at least one matching enough (match_all == False)?

Returns:

True or False if the plugin handles this context key and it either does or doesn’t match. If the context is unknown, return None.

Important!

If a Command disables itself (e.g. from use in a key binding) by returning False from its is_enabled() method, Sublime Text WILL NOT continue looking for other possible key bindings that might apply. To make that happen, the is_enabled() method needs to return True and the on_query_context() method needs to be implemented to decipher if the particular context applies to this particular key binding.

8.6. Blocking a Key Binding

A “Command” that can be used to neuter a default key bindings is “noop” or any other command that does not exist. No errors are generated from this. Binding a keypress to such a command with no “context” entry in your User Package effectively blocks that keypress from doing anything.

8.7. Diagnosing Binding Problems

When a key binding is not doing what you think it should be, you can turn on command logging.

View ‣ Show Console

sublime.log_commands(True)

Now command-logging is enabled and will remain so until we re-start Sublime or call

sublime.log_commands(False)

It will not log items in the command palette.

Mouse operations will cause the “drag_select” command to be logged. You may need to turn off logging temporarily if you want to copy and paste the logged output.

sublime.log_input(True)

works similarly to command logging. This logs in the console the result of keys that you are pressing. Example:

key evt: shift+control+p

If command logging is also turned on, you will also see something like:

command: show_overlay {“overlay”: “command_palette”}

If you use the Windows (super) key, it can happen that another application or even the OS is already using that key sequence and reporting that it was handled, in which case, the key message never makes it to Sublime Text as a message from the OS.

Another thing that can happen is that the context where the key binding is supposed to be applied is preventing it because the context is not what we think it is (e.g. wrong file extension).

Note

The PackageDev package is very helpful when setting up key bindings because it has an intuitive command-completion feature in many parts of the .sublime-keymap files. However, at this writing it is generating exceptions and Sublime Text appears to be disabling it as a result. See Console for details when it is installed and enabled.

8.8. Key Binding Conflicts

8.8.1. Terminology

grok

To fully and completely understand something in all of its details and intricacies.

—Wiktionary

8.8.2. Overview

Conflicts among key bindings are not only important, but are difficult to detect by humans. As an example of how tricky this area can be, on 17-Mar-2022 an enhancement for the " and ' key bindings in the shipped Python Package introduced a conflict with the bindings for those keys from the Default Package. The enhancement for " was to NOT introduce a paired double-quote when the selection was inside a Python double-quoted string. The enhancement for ' was to do the same for single-quoted strings. But a subtle oversight caused the sum of these key bindings (from the Default Package with parts overridden by the Python Package) to malfunction inside Python comments.

That conflict was discovered (by @vwheeler63 [this author, while the KeyBindingsReport Package was being developed]) and fixed (by @deathaxe) on 3-May-2026. To the team’s credit, once it was reported in the Python Package Issue 4535, this conflict was fully handled within about 2 hours and merged (after several approvals from other team members) a couple of days later.

To make the point: how tricky is the area of key-binding conflicts? Answer:

These conflicts remained undetected in a mainstream Sublime Text Package for 4 years.

Towards answering the need for tools in this area, Package author Scott Kuroda wrote and released the FindKeyConflicts Package on 8-Nov-2012. Version 1, 2 and 3 were released in short succession over the next 3 days.

The name itself tends to fill one with hope that it will find problems in your key binding arrangement. However, what it actually does is report where there is more than one binding that involves a single keypress or keypress sequence, which is quite common and normal. Even an unmodified new Sublime Text installation has well over 70 of these, and none of them are actual binding conflicts.

Example, these are reported as Key Conflicts for the key bindings to the double-quote keypress. The complex contexts are not only nearly 700 characters wide, but are also presented in a form that is completely un-grok-able by a human.

Scroll to the right to examine the context definitions and ask yourself this question: “How long would it take me to determine if any of these had overlapping contexts?” The real answer is:

It’s not doable with any level of reliability without a drastic change in format that would enable the reader to either grok each context easily, or do a text ‘diff’ that would show the differences between them.

["]
  insert_snippet                           Default               [{"key": "setting.auto_match_enabled", "operand": true, "operator": "equal"}, {"key": "selection_empty", "operand": true, "operator": "equal", "match_all": true}, {"key": "following_text", "operand": "^(?:\t| |\\)|]|\\}|>|$)", "operator": "regex_contains", "match_all": true}, {"key": "preceding_text", "operand": "[\"a-zA-Z0-9_]$", "operator": "not_regex_contains", "match_all": true}, {"key": "eol_selector", "operand": "string.quoted.double - punctuation.definition.string.end", "operator": "not_equal", "match_all": true}]
  insert_snippet                           Default               [{"key": "setting.auto_match_enabled", "operand": true, "operator": "equal"}, {"key": "selection_empty", "operand": false, "operator": "equal", "match_all": true}]
  move                                     Default               [{"key": "setting.auto_match_enabled", "operand": true, "operator": "equal"}, {"key": "selection_empty", "operand": true, "operator": "equal", "match_all": true}, {"key": "following_text", "operand": "^\"", "operator": "regex_contains", "match_all": true}, {"key": "selector", "operand": "punctuation.definition.string.begin", "operator": "not_equal", "match_all": true}, {"key": "eol_selector", "operand": "string.quoted.double - punctuation.definition.string.end", "operator": "not_equal", "match_all": true}]
  insert_snippet                           CSS                   [{"key": "setting.auto_match_enabled", "operand": true, "operator": "equal"}, {"key": "selection_empty", "operand": true, "operator": "equal", "match_all": true}, {"key": "selector", "operand": "source.css - string", "operator": "equal", "match_all": true}, {"key": "following_text", "operand": "^(?:\t| |\\)|]|\\}|>|,|;|$)", "operator": "regex_contains", "match_all": true}, {"key": "preceding_text", "operand": "[\"a-zA-Z0-9_]$", "operator": "not_regex_contains", "match_all": true}, {"key": "eol_selector", "operand": "string.quoted.double - punctuation.definition.string.end", "operator": "not_equal", "match_all": true}]
  insert_snippet                           JSON                  [{"key": "setting.auto_match_enabled"}, {"key": "selector", "operand": "source.json"}, {"key": "selection_empty", "match_all": true}, {"key": "preceding_text", "operand": "[\"\\w]$", "operator": "not_regex_contains", "match_all": true}, {"key": "following_text", "operand": "^(?:\t| |]|,|:|\\}|$)", "operator": "regex_contains", "match_all": true}]
  insert_snippet                           Java                  [{"key": "setting.auto_match_enabled", "operand": true, "operator": "equal"}, {"key": "selection_empty", "operand": true, "operator": "equal", "match_all": true}, {"key": "selector", "operand": "source.java - string", "operator": "equal", "match_all": true}, {"key": "following_text", "operand": "^(?:\t| |\\)|]|\\}|>|,|:|;|\\+|$)", "operator": "regex_contains", "match_all": true}, {"key": "preceding_text", "operand": "[\"a-zA-Z0-9_]$", "operator": "not_regex_contains", "match_all": true}, {"key": "eol_selector", "operand": "string.quoted.double - punctuation.definition.string.end", "operator": "not_equal", "match_all": true}]
  lsp_json_auto_complete                   LSP-json              [{"key": "setting.auto_match_enabled", "operand": true, "operator": "equal"}, {"key": "selection_empty", "operand": true, "operator": "equal", "match_all": true}, {"key": "following_text", "operand": "^(?:\t| |\\)|]|\\}|>|$)", "operator": "regex_contains", "match_all": true}, {"key": "preceding_text", "operand": "[\"a-zA-Z0-9_]$", "operator": "not_regex_contains", "match_all": true}, {"key": "eol_selector", "operand": "string.quoted.double - punctuation.definition.string.end", "operator": "not_equal", "match_all": true}, {"key": "selector", "operand": "source.json", "operator": "equal", "match_all": true}]
  insert                                   Python                [{"key": "setting.auto_match_enabled"}, {"key": "selector", "operand": "source.python - string.quoted.double"}, {"key": "selection_empty", "match_all": true}]
  insert_snippet                           Python                [{"key": "setting.auto_match_enabled"}, {"key": "selector", "operand": "source.python - string.quoted.double"}, {"key": "selection_empty", "match_all": true}, {"key": "preceding_text", "operand": "(?i:^|[^\"\\w\\\\]|\\b[bfru]+)$", "operator": "regex_contains", "match_all": true}, {"key": "following_text", "operand": "^(?:\t| |\\)|]|\\}|\\.|,|:|;|$)", "operator": "regex_contains", "match_all": true}]
  insert_snippet                           Python                [{"key": "setting.auto_match_enabled"}, {"key": "selector", "operand": "source.python"}, {"key": "selection_empty", "operand": false, "match_all": true}]
  move                                     Python                [{"key": "setting.auto_match_enabled"}, {"key": "selector", "operand": "source.python"}, {"key": "selection_empty", "match_all": true}, {"key": "following_text", "operand": "^\"", "operator": "regex_contains", "match_all": true}, {"key": "selector", "operand": "punctuation.definition.string.begin", "operator": "not_equal", "match_all": true}, {"key": "eol_selector", "operand": "string.quoted.double - punctuation.definition.string.end", "operator": "not_equal", "match_all": true}]

Note that these are straight out of the Packages shipped with Sublime Text.

These bindings are not, in fact, conflicted because they operate in distinctly separate environments such that none of these bindings actually interfere with any of the other bindings. To state it another way: there is no overlap in their contexts.

What the FindKeyConflicts Package actually does then is report where there are POTENTIAL conflicts (more than one binding for a keypress), and prints the key bindings involved, leaving it to the reader to decipher if there are really any problems.

Conclusion: while interesting, the reports generated by FindKeyConflicts (based on a mere use of the same keypresses) are not very likely to help users detect actual problems with key bindings.

Note

It also goes to show how far a good Package name goes towards getting people to install a Package because, despite its lack of usefulness, 1-3 users per week are installing it and keeping it on their systems, perhaps in hope some day it might be useful.

8.8.3. What Is a Key-Binding Conflict?

A key-binding conflict is when one key binding unintentionally overrides another key binding that should be able to function, but cannot in some (or all) contexts. The trouble with this definition is that there is no way, in software, to determine what was intentional and what was not. If the target of a report was just based on this, you would also get every place where you had intentionally overridden key bindings in your local environment.

This might not be a bad thing, but perhaps the more useful target to report on is when a key binding OVERRIDES another in the same context. Let us consider some programmatically-detectable situations:

  1. More than one binding exists for a keypress or keypress sequence.

  2. Of bindings found in 1, more than one of them have “context” entry restrictions that are satisfied by a given editing context.

  3. Of bindings found in 1, two or more have functionally-equivalent context definitions.

Note that to test 2, there are only 2 practical ways to specify what context to test against (that this author can think of):

  • use the context of an existing key binding, or

  • use the editing context in the current View.

In both cases, two bindings for the same keypress that would both be selected in the specified context could be quite interesting, and potentially be unintended.

Also note that 1 and 2 by themselves do not necessarily indicate a “problem”, since partially-overlapping contexts does not mean the key bindings earlier in the list (the one(s) that did not get selected) would not function correctly in other contexts. This could be caused by an intentional override for one or more contexts that are more specific than the binding earlier in the list. In that same light, 1 and 3 could also be caused by an intentional override.

So the more useful Key-Binding Conflict Reports involve:

  • conditions 1 and 2 for the current context,

  • conditions 1 and 2 for the context of a specified key binding, or

  • 1 and 3 (which, as a side effect would mean 2 is also true and would produce a report with a slightly narrower list than the combination of 1 and 2).

8.8.3.1. Details

Note that “functionally equivalent” also includes when 2 bindings for the same keypress have NO “context” entries in their key-binding definition (i.e. no restrictions on where they will be applied).

Detecting 3 is not trivial given that all of the following context conditions function identically since they specify default values, which is quite common in key-binding definitions:

{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }
{ "key": "setting.auto_match_enabled", "operator": "equal" }
{ "key": "setting.auto_match_enabled", "operand": true }
{ "key": "setting.auto_match_enabled", "match_all": false }
{ "key": "setting.auto_match_enabled" }

Thus the default values for “operator”, “operand” and “match_all” would have to be taken into account.

Adding to the complexity is that the conditions in a complex context definition can be in a different order and still be functionally equivalent.

8.8.4. Single- vs Multi-Keypress Bindings

This is one area where FindKeyConflicts does report something useful: when there are both single- and multi-keypress key bindings with the same leading keypress. Fortunately, this is easy to test for, and would, in many cases, be unintentional.

However, Sublime Text mitigates this by doing something constructive when this situation occurs. In fact, there is such an example in the sublime-rst-completion Package: “ctrl+t” is used as a leading keypress in 7 multi-keypress bindings, whereas Sublime Text Default Package binds “ctrl+t” alone to the “transpose” command (swaps 2 characters on either side of the selection).

Interestingly, this actually does not create a problem. Here is why. In the environment where the multi-keypress bindings apply that use “ctrl+t” as the leading keypress, simply repeating the keypress again causes Sublime Text to discover:

  • that there is not binding for ["ctrl+t", "ctrl+t"], and

  • there is a single-keypress binding for ["ctrl+t"],

in which case it applies the latter. So the user is only burdened with one more keypress to make use of the single-keypress binding while editing .rst files. Since the Package provided no binding for ["ctrl+t", "ctrl+t"], it did not in fact “mask” the binding for ["ctrl+t"]!

So the best that can be done in this case is report it as a POTENTIAL conflict, to make the user aware of it, and show the bindings in human-readable form and let the user decide if it is a problem or not.

8.8.5. Detecting Potentially-Conflicting Key Bindings

Considering all of the above, now we can define a more useful way to detect POTENTIAL key-binding conflicts: to be potentially conflicting, 2 key bindings must:

  • involve the same keypress or keypress sequence, i.e. self.keypress_tuple() == other.keypress_tuple()

    and

  • have equivalent context conditions (which includes both having no context conditions).

8.8.6. User Experience Considerations

Upon invoking the report, the user gets a choice between:

  • show key bindings where there are multiple bindings that use the same keypresses AND also have functionally-equivalent contexts.

  • use current context,

  • choose a specific existing key binding (more complex to implement, probably not part of v1.0),

Also, to be useful at all (since the reader must still grok the contexts), the reported key bindings involved would need to have their contexts printed in a form that would be easy for a human to quickly digest and thus tell if there was an UNINTENDED override or not.

8.8.7. Detecting the Problem Presented in the Overview

What would it take to detect the key-binding conflict discovered in the Python Package? Getting the KeyBindingReport Package is a good start. (As of 13-May-2026, this package is under development and not yet released.)

Its report called:

KeyBindingReport: All Keypress Combinations in All Installed Packages

shows all keypresses (including those not bound), context details, and the source Package each binding came from, and the bindings for " and ' (double- and single-quote keys) would show the “back-to-back” series of key-binding overrides showing that while the Default Package has 3 bindings for each of them, the Python Package had only one binding for each, which is enough to raise suspicions and attract attention.

Here are the results for the unmodified double-quote keypress at the time the above conflict in the Python key bindings was detected:

Key      Footnote  Command                Args                                    Source
 "         (81)  insert_snippet           {"contents": "\"$0\""}                  Default/Default (Windows).sublime-keymap
 "         (82)  insert_snippet           {"contents": "\"${0:$SELECTION}\""}     Default/Default (Windows).sublime-keymap
 "         (83)  move                     {"by": "characters", "forward": true}   Default/Default (Windows).sublime-keymap
 "         (84)  insert_snippet           {"contents": "\"$0\""}                  CSS/Default.sublime-keymap
 "         (85)  insert_snippet           {"contents": "\"$0\""}                  Java/Default.sublime-keymap
 "         (86)  insert_snippet           {"contents": "\"$0\""}                  JSON/Default.sublime-keymap
 "         (87)  insert                   {"characters": "\""}                    Python/Default.sublime-keymap
 "         (90)  move                     {"by": "characters", "forward": true}   Python/Default.sublime-keymap
 "         (91)  lsp_json_auto_complete                                           LSP-json/Default.sublime-keymap

Footnotes (81-91) show the context conditions formatted in a nicely-readable format.

Note how the lone binding from the Python/Default.sublime-keymap keymap file stands out as overriding ALL of the the double-quote keypresses before it. Its context activated this key binding when Python files were being edited. The same was true for the unmodified single-quote keypress.

Further, the Command that runs this report can be run for specific keypresses, and the " and ' bindings (with no modifier keys) would also show the same detail, but it would be isolated to just those 2 keypresses.