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
Falseresult 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:
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;
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
Truein 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.
"""