7. Scope, Selectors and Context

7.1. Scope

Scope is the “characteristics of the caret’s location in a text file”.

Syntax definitions make Sublime Text aware of the Scope for any given position in a file. They do so by generating a scope name for each caret position to provide metadata about language tokens. This metadata is updated on-the-fly every time the caret moves in any kind of document, but it is especially applicable in source code or data files (e.g. HTML, XML, JSON, YAML, CSS, etc.) where the metadata reveals the nature of the caret’s current location (e.g. keyword, keyword category, string, variable, constant, number, symbol, comment, tag, etc.).

Current Scope is the basis for syntax highlighting in Sublime Text, among many other things.

7.2. Scope Name

A Scope Name is the space-separated list of elements of a Scope that indicates the nature of a specific location in the text buffer. Each language token (e.g. a function name, variable name, HTML tag name, etc.) will have a single Scope Name associated with it. Each element may be comprised of one or more dot-separated words. The order of these elements is from least specific to most specific. This ordering is defined by the Syntax and is important.

Example in a C Source Code file:

/* Caret is in function name "some_func":
 *         v       */
void some_func(int * op) {
    int a = *op;
    int b = a + 2;
}

Resulting Scope Name:

"source.c  meta.function.c  entity.name.function.c"

(An extra space is added to make it easier to read. In practice the separator is a single space.)

Example of current Scope for various caret positions of this string in a C file (you will need to scroll the below to the right to see the current Scope Name of each point):

void some_func(int * op, char * name, bool aboolIsValue) {
// | ^^^^^^^^^| | || | |   |  |  |  |  |       |       | |
// |    |     | | || | |   |  |  |  |  |       |       | +-- source.c  meta.function.c             meta.block.c  punctuation.section.block.begin.c
// |    |     | | || | |   |  |  |  |  |       |       +---- source.c  meta.function.parameters.c  meta.group.c  punctuation.section.group.end.c
// |    |     | | || | |   |  |  |  |  |       +------------ source.c  meta.function.parameters.c  meta.group.c  variable.parameter.c
// |    |     | | || | |   |  |  |  |  +-------------------- source.c  meta.function.parameters.c  meta.group.c  storage.type.c
// |    |     | | || | |   |  |  |  +----------------------- source.c  meta.function.parameters.c  meta.group.c  punctuation.separator.c
// |    |     | | || | |   |  |  +-------------------------- source.c  meta.function.parameters.c  meta.group.c  variable.parameter.c
// |    |     | | || | |   |  +----------------------------- source.c  meta.function.parameters.c  meta.group.c  keyword.operator.c
// |    |     | | || | |   +-------------------------------- source.c  meta.function.parameters.c  meta.group.c  storage.type.c
// |    |     | | || | +------------------------------------ source.c  meta.function.parameters.c  meta.group.c  punctuation.separator.c
// |    |     | | || +-------------------------------------- source.c  meta.function.parameters.c  meta.group.c  variable.parameter.c
// |    |     | | |+---------------------------------------- source.c  meta.function.parameters.c  meta.group.c  keyword.operator.c
// |    |     | | +----------------------------------------- source.c  meta.function.parameters.c  meta.group.c
// |    |     | +------------------------------------------- source.c  meta.function.parameters.c  meta.group.c  storage.type.c
// |    |     +--------------------------------------------- source.c  meta.function.parameters.c  meta.group.c  punctuation.section.group.begin.c
// |    +--------------------------------------------------- source.c  meta.function.c                           entity.name.function.c
// +-------------------------------------------------------- source.c                                            storage.type.c
//                                                           \____________________________________________________________________________________/
//                                                                                                   ^
//                                                                                                   |
//                                                                                                   +-- Scope Name for each location

An astute observer will note that the space between the int data type and the * in the formal parameter declaration still registers as source.c  meta.function.parameters.c  meta.group.c—the current Scope still knows it’s inside the parentheses among a set of function parameters.

7.3. Selector

A Selector is a string that is evaluated against the a Scope Name, and triggers a True or False result based on that evaluation. Its use is to determine whether a particular ‘thing’ is applicable based on the nature of a specific location within the text buffer, typically the current caret location. If you’re familiar with CSS, Sublime Text Selectors behave like selectors in a CSS file: they are an efficient method of determining whether something “applies” or not.

Selectors are used in a variety of subsystems in Sublime Text:

  • Key Bindings

  • Menu Items (when associated with Key Bindings, the Key Binding’s selector is used)

  • Build System

  • Completions

  • Snippets

See Selector Match for examples of Selectors.

7.4. Selector Match

To determine if a Selector matches a Scope Name, a special type of comparison is done:

Test1:

Does the first word in the selector match the beginning of any of the space-separated elements in the Scope Name, and match on a whole-word basis?

Test2:

If so, does the next space-separated element in the selector match the beginning of one of the subsequent elements in the Scope Name, and match on a whole-word basis?

Test3:

Repeat Test2 until all elements of the Selector have been exhausted, or until a False result occurs, whichever comes first.

If all tests had a True result, then the Selector is considered to match the Scope Name.

Example Selectors that would match the above C++ example (“source.c meta.function.c entity.name.function.c”):

  • “source”

  • “entity.name”

  • “source entity”

  • “source entity.name”

  • “source entity.name.function”

  • “source entity.name.function.c”

  • “source.c entity.name.function”

Note that “entity source” would not match because it is out of sequence.

This Python code mimics the behavior of this test except it does not enforce whole-word matches, whereas Sublime Text does:

def _match_on_remaining_names(sel_names, scope_names):
    """
    Does `sel_names` match `scope_names`?

    This means:
    - for each Selector string in `sel_names`:
      - is it found at the beginning of one of the remaining
        strings in `scope_names`?

    :param sel_names:    List of remaining Selector elements
    :param scope_names:  List of remaining Scope-Name elements
    :return:             Whether `sel_names` matches `scope_names`
    """
    is_match = False
    first_sel_name = sel_names[0]

    for i, scope_name in enumerate(scope_names):
        if scope_name.startswith(first_sel_name):
            is_match = True
            break

    if is_match:
        # Are there any remaining Selector Names?
        if len(sel_names) > 1:
            # 'i' is index of the Scope-Name element that matched.
            # Are there any elements after 'i'?
            if len(scope_names) > i + 1:
                # Attempt match with remaining Scope Names.
                remaining_sel_names = sel_names[1:]
                remaining_scope_names = scope_names[i + 1:]
                is_match = _match_on_remaining_names(remaining_sel_names, remaining_scope_names)
            else:
                # There are more Selector elements but no more Scope-Name elements.
                # Not a match.
                is_match = False

    return is_match


def _evaluate_selector_against_scope(selector, scope):
    """
    Does `selector` match `scope`?

    This means:
    - for each space-separated Selector element in `selector`:
      - is it found at the beginning of one of the Scope-Name elements in
        `scope` in the same sequence?

    Return the result.

    :param selector:  Selector string, e.g. "source entity.name:
    :param scope:     Scope, e.g. "source.c++ meta.function.c++ meta.toc-list.full-identifier.c++ entity.name.function.c++"
    :return:          Whether `selector` matches `scope`
    """
    is_match = False
    sel_names = selector.split(' ')
    scope_names = scope.split(' ')

    if sel_names and scope_names:
        is_match = _match_on_remaining_names(sel_names, scope_names)

    return is_match


def match_test(selector, scope, should_match: bool):
    is_match = _evaluate_selector_against_scope(_selector, _scope)
    print(f'is_match = [{is_match}]', end='')
    if is_match == should_match:
        print('  OK')
    else:
        print('  Failed')
        print(f'  Selector: [{selector}]')
        print(f'  Scope   : [{scope}]')


# Test match.
_selector = "source entity.name"
_scope = "source.c++ meta.function.c++ meta.toc-list.full-identifier.c++ entity.name.function.c++"
match_test(_selector, _scope, True)

# Test non-match (out of sequence).
_selector = "source entity.name meta"
match_test(_selector, _scope, False)

# Test match (in sequence).
_selector = "source meta entity.name"
match_test(_selector, _scope, True)

# Output:
# is_match = [True]  OK
# is_match = [False]  OK
# is_match = [True]  OK

7.5. Logical Operators in Selectors

Precedence (Highest to Lowest):

Symbol

Meaning

(...)

Grouping

-

NOT

&

AND

|

OR

,

OR

Operators with equivalent precedence are evaluated left to right. So the following two expressions are equivalent:

a , b & -c | d , e

(a , ((b & (- c)) | d)) , e

Example:

Scope Name

Selector

Matches

source.php meta.block.php

source - (keyword | storage)

Yes

source.php meta.block.php

(source - source.php) | text

No

7.6. Context

Context has 2 meanings in Sublime Text:

  1. the circumstances surrounding the current editing going on, such as the position and nature of the location(s) of the current selection(s) in the current View;

  2. The optional “context” entry of a Key Binding that contains 1 or more conditions that determine if a Key Binding is appropriate to be used, when compared to the context as defined in definition (1) above. When there is more than one such condition, ALL of them must evaluate to True in order for the Key Binding to be used. Selectors can be used to provide one or more conditional expression for this purpose, but selectors are only about 1/20th of the type of tests that can be defined inside this type of “context”.

    See more about this type of context under Key Bindings.

7.7. API

The following are Sublime Text API functions related to Scope, Selectors and Context. These are all methods of the View class. These can be used anywhere a reference to a View is available.

def scope_name(self, pt: Point) -> str:
    """
    :returns: The syntax scope name assigned to the character at the given point.
    """

def match_selector(self, pt: Point, selector: str) -> bool:
    """
    :returns: Whether the provided scope selector matches the `Point`.
    """

def score_selector(self, pt: Point, selector: str) -> int:
    """
    Equivalent to::

        sublime.score_selector(view.scope_name(pt), selector)

    See `sublime.score_selector`.
    """

def find_by_selector(self, selector: str) -> list[Region]:
    """
    Find all regions in the file matching the given selector.

    :returns: The list of matched regions.
    """

def extract_tokens_with_scopes(self, region: Region) -> list[tuple[Region, str]]:
    """
    :param region: The region from which to extract tokens and scopes.
    :returns: A list of pairs containing the `Region` and the scope of each token.

    .. since: 3172
    """

def extract_scope(self, pt: Point) -> Region:
    """
    :returns: The extent of the syntax scope name assigned to the character
              at the given `Point`, narrower syntax scope names included.
    """

def expand_to_scope(self, pt: Point, selector: str) -> Optional[Region]:
    """
    Expand by the provided scope selector from the `Point`.

    :param pt: The point from which to expand.
    :param selector: The scope selector to match.
    :returns: The matched `Region`, if any.

    .. since: 4130
    """



def meta_info(self, key: str, pt: Point) -> Value:
    """
    Look up the preference ``key`` for the scope at the provided `Point`
    from all matching ``.tmPreferences`` files.

    Examples of keys are ``TM_COMMENT_START`` and ``showInSymbolList``.
    """

def context_backtrace(self, pt: Point) -> list[ContextStackFrame]:
    """ Get a backtrace of `ContextStackFrame`\\ s at the provided `Point`.

    Note this function is particularly slow.

    .. since:: 4127
    """

def style(self) -> dict[str, str]:
    """
    See `style_for_scope`.

    :returns:
        The global style settings for the view. All colors are normalized
        to the six character hex form with a leading hash, e.g.
        ``#ff0000``.

    .. since:: 3150
    """

def style_for_scope(self, scope: str) -> dict[str, Value]:
    """
    Accepts a string scope name and returns a ``dict`` of style information
    including the keys:

    * ``"foreground": str``
    * ``"selection_foreground": str`` (only if set)
    * ``"background": str`` (only if set)
    * ``"bold": bool``
    * ``"italic": bool``
    * .. since:: 4063
        ``"glow": bool`` (only if set)
    * .. since:: 4075
        ``"underline": bool`` (only if set)
    * .. since:: 4075
        ``"stippled_underline": bool`` (only if set)
    * .. since:: 4075
        ``"squiggly_underline": bool`` (only if set)
    * ``"source_line": str``
    * ``"source_column": int``
    * ``"source_file": int``

    The foreground and background colors are normalized to the six character
    hex form with a leading hash, e.g. ``#ff0000``.
    """

The following are functions of the sublime module. (sublime.func_name(...))

def score_selector(scope_name: str, selector: str) -> int:
    """
    Match the ``selector`` against the given ``scope_name``, returning a score for how well they match.

    A score of ``0`` means no match, above ``0`` means a match. Different
    selectors may be compared against the same scope: a higher score means the
    selector is a better match for the scope.
    """