5. Commands

Commands are found throughout Sublime Text. Almost all actions are carried out by Commands. In essence, Commands are the bread-and-butter of getting things done within Sublime Text.

Commands are connected to:

You can see (and execute) most of the publicly-accessible Commands via the Command Palette: Tools ‣ Command Palette... (normally Ctrl-Shift-P).

You can also create your own commands using Plugins.

See also https://docs.sublimetext.io/reference/commands.html .

5.1. Adding Existing Commands to Command Palette

When a Package implements new Commands that should also be available in the Command Palette, they are added to it using a .sublime-commands file.

5.1.1. Variables Available in .sublime-commands Args

Some commands take paths as parameters. Among these, some commands (e.g. open_file) support variables passed in strings like ${packages}/PackageName/path/to/file.ext, and snippet-like syntax using “$0” to mark where to place the cursor, in a “contents” parameter which can be used when the file may not exist yet.

Contrary to what you might guess, these Commands are not, in fact, preprocessed by Sublime Text before passing them on to the Command, but are processed by the Command itself, in the strings exactly as you passed them. So if you want to create a Command that can take variables, you will need to code the variable substitution yourself, or pass them on to something that does.

Here is an example of how such variables are handled from within the edit_settings Command (from the EditSettingsCommand class in settings.py from the shipped Package Default.sublime-package). The comments were inserted for enhanced readability.

platform_name = {
    'osx': 'OSX',
    'windows': 'Windows',
    'linux': 'Linux',
}[sublime.platform()]

variables = {
    'packages': '${packages}',
    'platform': platform_name,
}

# -----------------------------------------------------------------
# Prep
# -----------------------------------------------------------------
base_file = sublime.expand_variables(base_file.replace('\\', '\\\\'), variables)
if user_file is not None:
    user_file = sublime.expand_variables(user_file.replace('\\', '\\\\'), variables)

# -----------------------------------------------------------------
# Handle '${packages}' variable.
# -----------------------------------------------------------------
base_path = base_file.replace('${packages}', 'res://Packages')
is_resource = base_path.startswith('res://')
file_name = os.path.basename(base_file)

# . . .

# -----------------------------------------------------------------
# Translate 'Default ($platform).sublime-xxxxx' file names
# sometimes found in *.sublime-commands files.
# -----------------------------------------------------------------
if user_file is None:
    user_package_path = os.path.join(sublime.packages_path(), 'User')
    user_file = os.path.join(user_package_path, file_name)

    # If the user path does not exist, and it is a supported
    # platform-variant file path, then try and non-platform-variant
    # file path.
    if not os.path.exists(os.path.join(user_package_path, file_name)):
        for suffix in {'.sublime-keymap', '.sublime-mousemap', '.sublime-menu'}:
            platform_suffix = ' (%s)%s' % (platform_name, suffix)
            if not file_name.endswith(platform_suffix):
                continue
            non_platform_file_name = file_name[:-len(platform_suffix)] + suffix
            non_platform_path = os.path.join(user_package_path, non_platform_file_name)
            if os.path.exists(non_platform_path):
                user_file = non_platform_path
                break

This is the API documentation for the sublime.expand_variables() function:

def expand_variables(value: Value, variables: dict[str, str]) -> Value:
    """
    Expands any variables in ``value`` using the variables defined in the
    dictionary ``variables``. value may also be a list or dict, in which case the
    structure will be recursively expanded. Strings should use snippet syntax,
    for example: ``expand_variables("Hello, ${name}", {"name": "Foo"})``.
    """
    return sublime_api.expand_variables(value, variables)

Examples:

Preferences ‣ Settings uses the edit_settings command.

{
    "caption": "Preferences: Settings",
    "command": "edit_settings",
    "args":
    {
        "base_file": "${packages}/Default/Preferences.sublime-settings",
        "default": "// Settings in here override those in \"Default/Preferences.sublime-settings\",\n// and are overridden in turn by syntax-specific settings.\n{\n\t$0\n}\n"
    }
},

When command-logging via sublime.log_commands(True) is turned on in the Console, and Preferences: Settings is executed via the Command Palette, it results in the following logged in the Console before opening the read-only version from with Sublime Text’s shipped Default Package. (This was when the new Settings window had not already been opened previously.)

command: edit_settings {
    "base_file": "${packages}/Default/Preferences.sublime-settings",
    "default": "// Settings in here override those in \"Default/Preferences.sublime-settings\",\n// and are overridden in turn by syntax-specific settings.\n{\n\t$0\n}\n"
}
command: new_window
command: set_layout {
    "cells": [[0, 0, 1, 1], [1, 0, 2, 1]], "cols": [0.0, 0.5, 1.0], "rows": [0.0, 1.0]
}
command: open_file {
    "file": "${packages}/Default/Preferences.sublime-settings"
}
command: open_file {
    "contents": "// Settings in here override those in \"Default/Preferences.sublime-settings\",\n// and are overridden in turn by syntax-specific settings.\n{\n\t$0\n}\n",
    "file": "C:\\Users\\Vic\\AppData\\Roaming\\Sublime Text\\Packages\\User\\Preferences.sublime-settings"
}
Unable to open /C/Users/Vic/AppData/Roaming/Sublime Text/Packages/Default/Preferences.sublime-settings
command: exec {"update_annotations_only": true}
command: exec {"update_annotations_only": true}

So it can be seen that the edit_settings command uses the open_file command internally.

Note also the function of the $0 variable in the “default” element. It marks where the cursor will be placed after the text is inserted, in the case where the file does not already exist. This happens when the user has not created any custom settings, there is no <data_path>/Packages/User/Preferences.sublime-settings file yet so Sublime Text creates one (without saving it yet) and inserts this as its contents:

// Settings in here override those in "Default/Preferences.sublime-settings",
// and are overridden in turn by syntax-specific settings.
{
    |  <-- cursor is placed here
}

This file does not get written to disk until the user saves it.


This is found in Context.sublime-menu (context menu within an open file), creating the “Open Containing Folder…” option:

{ "command": "open_dir", "args": {"dir": "$file_path", "file": "$file_name"}, "caption": "Open Containing Folder…" },

passing the above strings into the “dir” and “file” arguments. It causes the OS’s File Explorer to open and focus placed upon the target file. Nice touch.


The following is from Main.sublime-menu in the Preferences menu:

{
    "command": "edit_settings", "args":
    {
        "base_file": "${packages}/Default/Default ($platform).sublime-keymap",
        "default": "[\n\t$0\n]\n"
    },
    "caption": "Key Bindings"
},
{
    "command": "edit_settings", "args":
    {
        "base_file": "${packages}/Default/Default ($platform).sublime-mousemap",
        "default": "[\n\t$0\n]\n"
    },
    "caption": "Mouse Bindings"
},

It is clear that these are used in a variety of ways: ${packages} and bare $platform, both inside a string.

5.1.2. Paths

Commands expect forward-slash directory separators in paths if not otherwise noted, including on Windows (for example, /C/Program Files/Sublime Text/Lib/python314/sublime_plugin.py).

Relative paths in arguments to commands are assumed to start at the <data_path> directory.

5.2. Further Reading

A relatively-complete list of commands exists in the Unofficial Documentation Commands Page.