Overview

Wrap Command

A Command Wrapper for providing an unified easy to use and powerful interface for Sublime Text 2 and Sublime Text 3 commands.

Example:

{
    "command": "wrap_command",
    "args": {
        "command": "open_file":
        "args": {"file": "${input:File to open}"}
    }
}

This will prompt user for input and then runs command open_file.

Motivation

While trying to setup build systems and configuring packages, I noticed that there are more or less powerful opportunities to specify arguments, depending on the package.

For example, I tried to setup a build system for a django app collection, where the build system runs the test:

manage.py test <APPLICATION NAME>

But this was only partly possible:

{
     "selector": "source.python",
     "cmd": ["python", "manage.py", "test", "${file_path/.*[\\/\\\\]//}" ],
     "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
     "working_dir": "${project_path:}/webapps"
}

Problem is that this works only for files at application's root folder.

Solution

I created a new package WrapCommand providing command wrap_command. Problem above would be solved like this:

{
     "selector": "source.python",

     // we wrap the default "exec" target with with "wrap_command"
     "target": "wrap_command",
     "wrap_command": "exec",

     // make sure we have wanted evaluation order
     "order": ["f", "django_site", "django_app"],

     // get normed filename
     "f": "${n:$file_path}",

     // get normed django-site (folder where manage..py is located)
     "django_site": "${n:${d:${back:$f;manage.py}}}",

     // extract django app name from filename
     "django_app": "${s:${django_site}/([^/]+).*;$1;$f}",

     "args": {
         "cmd": ["python", "manage.py", "test", "${django_app}"]
         "working_dir": "${django_site}",
     }

     "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)"
     "name": "Run"
}

I have to admit that it does not look nice, but it does the job. You will find output on console useful, while setting up such patterns.

I mostly do not use Build Systems, but rather commands in SublimeREPL, for easy debugging:

{
    "command": "wrap_command",
    "caption": ":django manage test",
    "args": {
        "wrap_command": "repl_open",

        "order": ["f", "django_site", "django_app"],

        "f": "${n:$file_path}",
        "django_site": "${n:${d:${back:$f;manage.py}}}",
        "django_app": "${s:${django_site}/([^/]+).*;$1;$f}",

        "args": {
            "type": "subprocess",
            "encoding": "utf8",
            "cmd": ["python", "-i", "-u", "manage.py", "test", "${django_app}"],
            "cwd": "${django_site}",
            "syntax": "Packages/Python/Python.tmLanguage",
            "external_id": "python",
            "extend_env": {"PYTHONIOENCODING": "utf-8"}
        }
    }
}

Settings

WrapCommand interpretes following settings:

Name Type Default Description
wrap_command_debug boolean false toggle debug logging to console helpfull to trace expansions.

Options

select

Select an argument set:

{
    "select": "${ext:$file_name}"
    "foo": { "wrap_command": "echo", ... }
    "bar": { "wrap_command": "echo", ... }
}

Please note, that first all arguments will be evaluated and only right before calling the command, the selection will be applied. You may override the wrap_command parameter in selected arguments.

Default is to select "args".

wrap_command
The command to run. This key may also be in arguments section. It will be removed from arguments passed to command to run.
order

Variables specified in order are evaluated first, in given order.

You may specify the order of argument parsing (or only a part of it). This is useful, if you use named captures, you want to reuse later, or you use other argument's values. In following examples there is a platform independent path separator:

{
    "order": ["sep"],      // make sure $sep can be used in
                           // other configs like args
    "sep": "(?:\\\\|/)",   // file separator pattern
    "wrap_command": "exec"
    "args": { ... }
}
args
The wrapped command's arguments.
*
Any other argument may be used for either name an argument set, which can be selected by select or to predefine a value. Each argument will be available as variable after processing.

Argument Expansion

Variables

First there are supported build system variables as specified in documentation for buildsystems.

$file_path The directory of the current file, e. g., C:\Files.
$file The full path to the current file, e. g., C:\Files\Chapter1.txt.
$file_name The name portion of the current file, e. g., Chapter1.txt.
$file_extension The extension portion of the current file, e. g., txt.
$file_base_name The name only portion of the current file, e. g., Document.
$packages The full path to the Packages folder.
$project The full path to the current project file.
$project_path The directory of the current project file.
$project_name The name portion of the current project file.
$project_extension The extension portion of the current project file.
$project_base_name The name only portion of the current project file.

Apart from these, there are following more variables available:

$installed_packages what sublime.installed_packages() returns
$settings_path Directory of your Settings directory, where session files reside.
$session_file Full path of session file.
$auto_session_file Full path of auto session file.
$session dictionary of data stored in session
$auto_session dictionary of data stored in auto session
$settings dictionary of settings
$project_buildsystems list of buildsystems
$project_folders list of project folders
$project_settings dictionary of project settings.
$env dictionary of environment

You probably noticed that there are also complex types available. You can access them like you usually access such data:

$session[dictionary]

will provide the path to your dictionary file, like specified in your settings.

So you can access cmd of first buildsystem of your project:

$project_buildsystems[0][cmd]

All of these values you can also access in ${} notation, which is:

${project_buildsystems[0][cmd]}

If a variable does not exist, you can provide a default:

${project_buildsystems[0][cmd]:Default}
Shortcuts

There are some shortcuts for easier Access:

$PB $project_buildsystems
$PS $project_settings
$PF $project_folders
$S $settings

Anonymous Regex Captures

All variables $0, $1, $2, ..., $9 are automatically expanded to \\1, \\2, etc., for having more convenient capture parameters. So instead:

{ "foo": "${s:f(oo);\\\\1}" }

You can write:

{ "foo": "${s:f(oo);$1}" }

List Interpolation

In commands you often are more interested in a list of values than only at a single string:

{ "cmd": [ "echo", "${project_folders}" ] }

Will expand to:

{ "cmd": [ "echo", "folder1/foo;folder2/bar" ] }

This is maybe not what you want. Consider list interpolation sigil "@":

{ "cmd": [ "echo", "@{project_folders}" ] }

In context of a list, this will expand to:

{ "cmd": [ "echo", "folder1/foo", "folder2/bar" ]}

Functions

Apart from variables, you can also evaluate little functions on these Variables. There may be more useful functions, but these shall be enough for now.

basename, base, b

Get the basename of a file. Please note that here the extension of the file is included:

${b:foo/bar.txt} -> bar.txt
Arguments:
  • path of a file
count

Count variables of a list or characters of a string:

${count:a;b;c} -> 3
Argunents:
  • any
dirname, dir, d

Get directory part of a filename:

${d:foo/bar.txt} -> foo
Arguments:
  • path of a file
escape, esc, e

Escape the given value (or values) for further processing. (";" and "" are escaped, such that you can use it further as arguments). This is useful for e.g. map function.

${e:foo;bar} -> foo;bar
Arguments:
  • any
Returns:
string
exists

Test if given file exists:

${exists:$packages/WrapCommand} -> true
${exists:does/not/exist} ->

If something does not exist, an empty string is returned.

Arguments:
  • path of file
extension, ext

Return file's extension:

${ext:foo/bar.txt} -> txt
Arguments:
  • path of file
filter

Filter some elements out of a list:

${filter:oo;foo;gloo;fue;glue} -> foo;gloo
Arguments:
  • pattern
  • list to filter
find

Return a list of files whose path match a regular expression:

${find:$packages;\\.sublime-commands$} -> sublime command files

Resulting list contains file paths relative to given root.

Arguments:
  • root where to start search
  • pattern
find_back, back

Return a list of files whose path match a regular expression. Difference to find is that the search is done backwards in path. It will be returned the first non-empty set of files in a parent directory.

Resulting list contains absolute path names.

Arguments:
  • root where to start
  • pattern
has

Check if a variable has an item:

${has:var[item]}

Please note that ${has:$var[item]} is something different.

Arguments:
  • An itemgetter
if

If the first arguments evaluates to a true (non-empty) value, the second argument is returned, else third argument is returned if given or "":

${if:condition;then;else} -> then
${if:;then;else} -> else
Arguments:
  • Condition
  • result for non-empty condition
  • result for empty condition
input, in

Get some user input:

${in:User Prompt} -> Open a user input
${in:User Prompt;default} -> Open a user input
Arguments:
  • caption of input
  • default value (optional)

It is not possible to use ${in:foo} in a nested way:

${exists:${input:Filename}}  THIS DOES NOT WORK AS EXPECTED, result will be the
                             user's input
join

Join all parts of list using the first parameter as joining string:

${join: ;first;second;third} -> first second third
Arguments:
  • Joining string
  • any other args
length, len

Return length of string:

${length:foo} -> 3
Arguments:
  • a string
list

Create a list:

${list:first;second;third} -> Explicitely create a list
Arguments:
  • list items
map

Map a function to multiple values:

${map:${e:s:/$;;$_};first/;second;third/}

This example will remove trailing slashes, if present. Please note that the first parameter will be evaluated by map for each following parameter, replacing $_ with parameter's value.

The escape function must be used, to have a nice way of writing this, otherwise you had to escape all ";" and "$".

The upper command is actually the same like:

${list:${s:/$;;first/};${s:/$;;second};${s:/$;;third/}}

If your mapping function only takes one parameter, you can omit the escaping:

${map:abspath;$_;first;second;third}
Arguments:
  • map function without "${}" around
  • list to map
match, m

Match a string. If match contains named captures, variable dictionary is updated with these captures for later use:

${m:f(?P<o>oo);foobar} -> foo
${m:f(?P<o>oo);foobar}$o -> foooo

Returns the matched string.

Arguments:
  • pattern
  • string to match
normpath, norm, n

Normalize a path:

${norm:foo//bar} -> foo/bar
Arguments:
  • pattern
select, select

Open a Quick Panel for a user selection:

${sel:first;second;third} -> Open a panel and user can select between
                            first, second, third
${sel:first\nmore info;second\n;third\nalso more info}
                         -> Open a panel and user can select between
                            first, second, third
                            with displayed more info
Arguments:
  • values displayed in quick panel

It is not possible to use ${sel:foo} in a nested way:

${exists:${sel:Filename}}  THIS DOES NOT WORK AS EXPECTED, result will be the
                             user's input

Here the recommended usage for opening either "foo.txt" or "bar.txt":

{
    "command": "wrap_command",
    "args": {
        "command": "open_file",
        "args": {"file": "${sel:foo.txt;bar.txt}"}
    }
}

If you have to manipulate result you got from user:

{
    "command": "wrap_command",
    "args": {
        "command": "open_file",
        "filename": "${sel:foo.txt;bar.txt}",
        "args": {"file": "${s:\\.txt;\\.cpp;${filename}}"}
    }
}
subst, s

Substitute matched patterns with value:

${s:foo;bar;foobar}      -> barbar
${s:f(oo);bar\1;foobar}  -> baroobar
${s:f(oo);bar$1;foobar}  -> baroobar

Please note that in JSON context, you have to escape "\":

{ foo: "${s:f(oo);bar\\1;foobar}" }
Arguments:
  • pattern
  • replacement
  • string to process

Adding a custom function

In sublime text 2 context, this is pretty easy:

import expander

@expander.modifier('my', 'funcs', 'names')
def _myfunc(*args):
    return args

Changes

Added functions for collecting user input.

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.