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-keymapfiles. 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.1. Recommended Key Binding Changes
The following are recommended key-binding changes from the default keymap installed with Sublime Text.
If you edit source code or any other kind of valuable intellectual property, be aware of the F9 key. If you want to find out why, go to any editable source-code file and hit the F9 key with no text selected. Hint: you will need the UNDO key! (If you don’t want to “experiment” like this, be advised that if no text is selected, it sorts each line of the ENTIRE FILE with no dialog box and no confirmation.) Now that you have seen this, you won’t wonder what scrambled your code if you hit F9 or Shift-F9 by accident.
When I learned that Sublime Text ships with the
sort_linescommand bound to the F9 key by default, knowing the type of files I am most often editing, I quickly disarmed it. Now and then I do need to use a sort-lines feature and when I do, I am quite happy with it being available from one of the menu options, and not from keys that might get hit accidentally.Here’s how to disable it:
,
place focus in the left side View,
find the two F9 key bindings by searching for “f9”,
copy them to the JSON list in the right side,
change the command to any command that doesn’t exist, like “noop” (comes from the common NOOP Assembly Language command in many Assembly languages which does nothing):
// -------------------------------------------------------------------- // Block F9 "sort_lines" commands: it's dangerous to have whole-file // sorting bound to an open F-key. // // It is possible this could be later mapped to a more sophisticated // Package that did sort_lines with a Dialog Box like Multi-Edit: // // - starting column \__ These COULD be defined by selected column // - number of characters / // - case sensitivity (yes/no) // - ascending/descending // // Note 1: Sorting the lines within a selected block only is implied by // having a block selected. // // Note 2: "noop" is a fictitious command: Sublime Text "dead-ends" // the key if it cannot find the command. // -------------------------------------------------------------------- { "keys": ["f9"], "command": "noop" }, { "keys": ["ctrl+f9"], "command": "noop" },
Whew! Now you’re safe!
Alternately, you can bind those to key combinations to something else.
What these details actually MEAN is explained in detail below. I just thought you’d like to see this early so you don’t damage any important intellectual property by accident.
Note
All of the below JSON keymap entries are also placed in the same keymap file on the right as you did with the above.
Also note that this JSONC format allows you to include comments, use of which is recommended so that 1-2 years from now, you will be able to remember WHY you did certain things.
For those of you that have muscle memory associated with Ctrl-Shift-S, and you want it to mean instead of :
// Move Ctrl-Shift-S from "Save As..." to "Save All" // to match many IDEs. { "keys": ["ctrl+shift+s"], "command": "save_all" },
For those of you that don’t want accidental keystrokes to change font size (i.e. you typically set the font size and then pretty much never change it), the following changes are recommended, leaving a single, intentional way to change the font size with the mouse (namely: Ctrl-Mousewheel). This also frees up these keys for other things which you may want to use.
// -------------------------------------------------------------------- // Turn off all font-size change keys except for directly editing // `Preferences > Settings` "font_size" setting, or Ctrl-MouseWheel. // -------------------------------------------------------------------- // ["ctrl++"], ["ctrl+-"] and ["ctrl+keypad_minus"] also suppress // `sublime-rst-completion` Package's PROMOTE and DEMOTE section heading // mappings except for `ctrl+keyboard_plus` and `ctrl+shift+keyboard_plus` // (involving one key: [keyboard_plus]), which is what I want. // -------------------------------------------------------------------- // Block: Increase Font Size { "keys": ["ctrl+keypad_plus"], "command": "noop" }, { "keys": ["ctrl++"], "command": "noop" }, { "keys": ["ctrl+="], "command": "noop" }, { "keys": ["ctrl+equals"], "command": "noop" }, // Block: Decrease Font Size { "keys": ["ctrl+keypad_minus"], "command": "noop" }, { "keys": ["ctrl+-"], "command": "noop" }, { "keys": ["ctrl+shift+equals"], "command": "noop" }, { "keys": ["ctrl+shift+keypad_plus"], "command": "noop" },
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:
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, andDefault ($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-keymapfiles 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.
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.Bindings from all other shipped Packages are loaded next in alphabetical order by Package name (from compressed
.sublime-packagefiles) in<install_path>/Packages/
Bindings from installed Packages are loaded in alphabetical order by Package name (from compressed
.sublime-packagefiles in<data_path>/Installed Packages/.
Bindings from loose-file (override) Packages are loaded in alphabetical order by Package name in
<data_path>/Packages/<pkg_name>.
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
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 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:truemeans all selections must meet this condition for this key binding to be applied,falsemeans 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 |
||||||
is_javadoc |
S |
bool |
equ |
x |
Is caret in a comment beginning with |
||||||
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 |
|||||||
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 includes text that is selected, if any.
Appears to only work in .java files. Other files tested that it does not
appear to work in: .c, .cpp, .js, .cs, .css and .html.
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 |
||||||||
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 |
||||||||
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 |
Valid values currently known: “goto” and “command_palette”.
With or without focus.
Possible options are: “find” (one of the Find panels), “input” and “output”.
This author’s current understanding about “input” and “output” is that it is
referring to panels created with window.create_io_panel() and
window.create_output_panel() respectively. See output of
window.panels() in Console for additional information.
xxxx can be the name of any setting as it applies to the current View, e.g. “word_wrap” (see and for lists).
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
TrueofFalse. 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:
TrueorFalseif the plugin handles this context key and it either does or doesn’t match. If the context is unknown, returnNone.
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.
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:
More than one binding exists for a keypress or keypress sequence.
Of bindings found in 1, more than one of them have “context” entry restrictions that are satisfied by a given editing context.
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"], andthere 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.