语法定义

Sublime Text 可以使用 .sublime-syntax.tmLanguage 文件来进行语法高亮。本文档介绍了 .sublime-syntax 文件。

概述🔗

Sublime Syntax 文件是 YAML 文件,包含一个小标题,后面跟着一个上下文列表。每个上下文都有一个模式列表,描述了如何在该上下文中高亮显示文本,以及如何更改当前文本。

以下是一个用于高亮显示 C 语言的语法文件的小示例。

%YAML 1.2
---
name: C
file_extensions: [c, h]
scope: source.c

contexts:
  main:
    - match: \b(if|else|for|while)\b
      scope: keyword.control.c

从本质上讲,语法定义将作用域(例如,keyword.control.c)分配给文本区域。这些作用域由配色方案用于高亮显示文本。

此语法文件包含一个上下文 main,它匹配单词 [if, else, for, while],并将它们分配给作用域 keyword.control.c。上下文名称 main 是特殊的:每个语法都必须定义一个主上下文,因为它将在文件开头使用。

match 是一个正则表达式,支持来自 Oniguruma 正则表达式引擎 的功能。在上面的示例中,\b 用于确保只匹配单词边界,以确保诸如 elsewhere 之类的单词不被视为关键字。

请注意,由于 YAML 语法,.sublime-syntax 文件中不允许使用制表符。

上下文🔗

对于大多数语言,您将需要多个上下文。例如,在 C 语言中,我们不希望字符串中间的 for 单词被高亮显示为关键字。以下是如何处理此问题的示例

%YAML 1.2
---
name: C
file_extensions: [c, h]
scope: source.c

contexts:
  main:
    - match: \b(if|else|for|while)\b
      scope: keyword.control.c
    - match: '"'
      push: string

  string:
    - meta_scope: string.quoted.double.c
    - match: \\.
      scope: constant.character.escape.c
    - match: '"'
      pop: true

在主上下文中添加了第二个模式,该模式匹配双引号字符(注意,'"' 用于此,因为独立的引号将是 YAML 语法错误),并将一个新的上下文 string 推入上下文堆栈。这意味着文件其余部分将使用字符串上下文进行处理,而不是主上下文,直到字符串上下文从堆栈中弹出。

字符串上下文引入了新的键:meta_scope。这将为所有文本分配 string.quoted.double.c 范围,只要 string 上下文在堆栈中。

在 Sublime Text 中编辑时,可以通过按下 Ctrl+Shift+P(Mac)或 Ctrl+Alt+Shift+P(Windows/Linux)来检查已应用于插入符号下文本的范围。

string 上下文有两个模式:第一个匹配反斜杠字符后跟任何其他字符,第二个匹配引号字符。请注意,最后一个模式指定了遇到未转义引号时的操作,字符串上下文将从上下文堆栈中弹出,返回到使用主上下文分配范围。

当上下文具有多个模式时,将找到最左边的模式。当多个模式在同一位置匹配时,将选择第一个定义的模式。

元模式🔗

meta_scope

这将给定范围分配给此上下文中的所有文本,包括将上下文推入堆栈和从堆栈中弹出的模式。

meta_content_scope

如上所述,但不适用于触发上下文的文本(例如,在上面的字符串示例中,内容范围不会应用于引号字符)。

meta_include_prototype

用于阻止当前上下文自动包含 prototype 上下文。

clear_scopes

此设置允许从当前堆栈中删除范围名称。它可以是整数,也可以是值 true 以删除所有范围名称。它在 meta_scopemeta_content_scope 之前应用。这通常仅在一种语法嵌入另一种语法时使用。

meta_prepend
4075

布尔值,控制 继承 期间的上下文名称冲突解决。如果指定此值,则此上下文中的规则将插入到祖先语法定义中具有相同名称的上下文的任何现有规则之前。

meta_append
4075

布尔值,控制 继承 期间的上下文名称冲突解决。如果指定此值,则此上下文中的规则将插入到祖先语法定义中具有相同名称的上下文的任何现有规则之后。

元模式必须在上下文中列出,在任何匹配或包含模式之前。

匹配模式🔗

匹配模式可以包含以下键

match

用于匹配文本的 正则表达式。YAML 允许许多字符串在不加引号的情况下编写,这有助于使正则表达式更清晰,但重要的是要了解何时需要引用正则表达式。如果正则表达式包含字符 #:-{[>,那么您可能需要引用它。正则表达式仅在一次运行时针对单行文本运行。

scope

分配给匹配文本的范围。

captures

数字到范围的映射,将范围分配给匹配正则表达式的捕获部分。请参阅下面的示例。

push

要推入堆栈的上下文。这可以是单个上下文名称、上下文名称列表或内联匿名上下文。

pop

从堆栈中弹出上下文。值 true 将弹出单个上下文。

大于零的整数将弹出相应数量的上下文。

4050

pop 键可以与 pushsetembedbranch 结合使用。组合使用时,在执行其他操作之前,将从堆栈中弹出指定数量的上下文。对于 pushembedbranch 操作,pop 将匹配视为前瞻,这意味着匹配将不会接收被弹出的上下文的 meta_scope

4075
set

接受与 push 相同的参数,但会先弹出当前上下文,然后将给定的上下文推入堆栈。

任何匹配都将接收被弹出的上下文的 meta_scope 以及被推入的上下文的 meta_scope

embed
3153

接受要推入的单个上下文的名称。虽然类似于 push,但它会在找到 escape 模式后立即从任何数量的嵌套上下文中弹出。这使其成为将一种语法嵌入另一种语法的理想工具。

escape

如果使用 embed,则需要此键,它是一个用于退出嵌入上下文的正则表达式。此模式中的任何反向引用都将引用 match 正则表达式中的捕获组。

embed_scope

分配给 match 之后和 escape 之前匹配的所有文本的范围。概念上类似于 meta_content_scope

escape_captures

捕获组到范围名称的映射,用于 escape 模式。使用捕获组 0 将范围应用于整个转义匹配。

branch
4050

接受两个或多个上下文的名称,这些上下文按顺序尝试。如果遇到 fail 操作,文件的突出显示将从 branch 发生的位置的字符重新开始,并将尝试下一个上下文。

branch_point

这是 branch 的唯一标识符,当匹配使用 fail 操作时指定。

branch 操作允许处理语法结构的歧义,并允许处理跨越多行的结构。

为了获得理想的性能,上下文应按其被接受的可能性顺序排列。注意:由于使用分支进行突出显示需要在每次更改文档时重新处理整个分支,因此突出显示引擎在发生 fail 时不会回溯超过 128 行。

fail
4050

接受要回溯到的 branch_point 的名称,并重试下一个上下文。如果 fail 操作指定了一个从未推入堆栈的 branch_point,或者已经从堆栈中弹出,它将不会有任何效果。

以下键控制排他性行为,每个匹配模式只能指定一个

  • push

  • pop

    <4075
  • set

  • embed

    3153
  • branch

    4050
  • fail

    4050

匹配示例🔗

将单个范围分配给整个匹配的基本匹配

- match: \w+
  scope: variable.parameter.c++

将不同的范围分配给正则表达式捕获组

- match: ^\\s*(#)\\s*\\b(include)\\b
  captures:
    1: meta.preprocessor.c++
    2: keyword.control.include.c++

推入名为 function-parameters 的另一个上下文

- match: \b\w+(?=\()
  scope: entity.name.function.c++
  push: function-parameters

从上下文弹出

- match: \)
  scope: punctuation.section.parens.end.c++
  pop: true

从当前上下文弹出并推入另一个上下文

- match: \}
  scope: punctuation.section.block.end.c++
  set: file-global

嵌入另一种语法

- match: (```)(js|javascript)
  captures:
    1: punctuation.section.code.begin.markdown
    2: constant.other.markdown
  embed: scope:source.js
  embed_scope: meta.embedded.js.markdown
  escape: ^```
  escape_captures:
    0: punctuation.section.code.end.markdown
3153

使用 branch 尝试一种突出显示,并能够回退到另一种

expression:
  - match: (?=\()
    branch_point: open_parens
    branch:
      - paren_group
      - arrow_function

paren_group:
  - match: \(
    scope: punctuation.section.parens.begin.js
    push:
      - include: expressions
      - match: \)
        scope: punctuation.section.parens.begin.js
        set:
          - match: =>
            fail: open_parens
          - match: (?=\S)
            pop: 2

arrow_function:
  - match: \(
    scope: punctuation.section.parens.begin.js
    push:
      - match: \w+
        scope: variable.parameter.js
      - match: ','
        scope: punctuation.separator.comma.js
      - match: \)
        scope: punctuation.section.parens.begin.js
        set:
          - match: =>
            scope: storage.type.function.arrow.js
            push: arrow_function_body
4050

使用 pop 与其他操作

paragraph:
  - match: '(```)(py|python)'
    captures:
      1: punctuation.definition.code.begin.md
      2: constant.other.language-name.md
    pop: 1
    embed: scope:source.python
    embed_scope: source.python.embedded
    escape: ^```
    escape_captures:
      0: punctuation.definition.code.end.md
4075

包含模式🔗

通常,将一个上下文的内容包含在另一个上下文内非常方便。例如,您可以定义几个不同的上下文来解析 C 语言,并且几乎所有上下文都可以包含注释。与其将相关的匹配模式复制到每个上下文,不如将它们包含进来

expr:
  - include: comments
  - match: \b[0-9]+\b
    scope: constant.numeric.c
  ...

在这里,注释上下文定义的所有匹配模式和包含模式都将被拉入。它们将被插入到包含模式的位置,因此您仍然可以控制模式顺序。注释上下文定义的任何元模式都将被忽略。

包含外部原型🔗

当从另一个语法中包含一个上下文时,可能希望也包含该语法中任何适用的原型。默认情况下,包含模式包含此类原型。如果在包含模式中添加了键值对 apply_prototype: true,上下文没有指定 meta_include_prototype: false 并且另一个语法具有 prototype 上下文,那么这些模式也将被包含。

tags:
  - include: scope:source.html.basic
    apply_prototype: true

原型上下文🔗

对于诸如注释之类的元素,包含它们非常普遍,因此更简单的方法是在每个上下文中自动包含它们,而只列出例外情况。您可以通过创建一个名为 prototype 的上下文来实现这一点,它将自动包含在每个其他上下文的顶部,除非上下文包含 meta_include_prototype 键。例如

prototype:
  - include: comments

string:
  - meta_include_prototype: false
  ...

在 C 中,字符串内的 /* 不会开始注释,因此字符串上下文表示不应包含原型。

包含其他文件🔗

Sublime Syntax 文件支持一个语法定义包含另一个语法定义的概念。例如,HTML 可以包含嵌入式 JavaScript。以下是一个基本的 HTML 语法定义示例,它就是这样做的

scope: text.html

contexts:
  main:
    - match: <script>
      push: Packages/JavaScript/JavaScript.sublime-syntax
      with_prototype:
        - match: (?=</script>)
          pop: true
    - match: '<'
      scope: punctuation.definition.tag.begin
    - match: '>'
      scope: punctuation.definition.tag.end

请注意上面的第一个规则。它表明,当我们遇到一个 <script> 标签时,JavaScript.sublime-syntax 中的主要上下文应该被推送到上下文堆栈中。它还定义了另一个键,with_prototype。它包含一个将被插入到 JavaScript.sublime-syntax 中定义的每个上下文中的模式列表。请注意,with_prototype 在概念上类似于 prototype 上下文,但是它将始终被插入到每个引用的上下文,而不管它们的 meta_include_prototype 键。

在这种情况下,插入的模式将在下一个文本是 </script> 标签时弹出当前上下文。请注意,它实际上并没有匹配 </script> 标签,它只是使用了一个前瞻断言,它在这里扮演着两个关键角色:它既允许 HTML 规则匹配结束标签,并按照正常方式突出显示它,又可以确保所有 JavaScript 上下文都被弹出。例如,上下文堆栈可能处于 JavaScript 字符串的中间,但是当遇到 </script> 时,JavaScript 字符串和主上下文都将被弹出。

请注意,虽然 Sublime Text 支持 .sublime-syntax.tmLanguage 文件,但无法在 .sublime-syntax 文件中包含 .tmLanguage 文件。

另一个常见的情况是模板语言包含 HTML。以下是一个示例,这次使用 Jinja 的子集

scope: text.jinja
contexts:
  main:
    - match: ''
      push: Packages/HTML/HTML.sublime-syntax
      with_prototype:
        - match: '{{'
          push: expr

  expr:
    - match: '}}'
      pop: true
    - match: \b(if|else)\b
      scope: keyword.control

这与 HTML 嵌入 JavaScript 的示例有很大不同,因为模板语言往往是从内到外操作:默认情况下,它需要充当 HTML,只在某些表达式上转义到底层的模板语言。

在上面的示例中,我们可以看到它默认情况下以 HTML 模式运行:主上下文包含一个始终匹配的模式,不消耗任何文本,只是包含 HTML 语法。

在包含 HTML 语法的地方,Jinja 语法指令 ({ { ... }}) 通过 with_prototype 键包含进来,因此被注入到 HTML 语法(以及 JavaScript,通过传递性)中的每个上下文。

变量🔗

多个正则表达式经常会有一些共同的部分。为了避免重复输入,可以使用变量。

variables:
  ident: '[A-Za-z_][A-Za-z_0-9]*'
contexts:
  main:
    - match: '\b{{ident}}\b'
      scope: keyword.control

变量必须在 .sublime-syntax 文件的顶层定义,并在正则表达式中通过 {{varname}} 引用。变量本身可以包含其他变量。请注意,任何不匹配 {{[A-Za-z0-9_]+}} 的文本都不会被视为变量,因此正则表达式仍然可以包含字面量 {{ 字符,例如。

继承🔗

在语法是另一种语法的轻微变体,并进行了一些添加或更改的情况下,继承是一个有用的工具。

继承语法时,使用 extends 键,其值为父语法的包路径包路径将以 Packages/ 开头,并将包含包名称和语法文件名。例如

%YAML 1.2
---
name: C++
file_extensions: [cc, cpp]
scope: source.c++
extends: Packages/C++/C.sublime-syntax

使用继承的语法将继承其父语法的 variablescontexts 值。所有其他顶层键,例如 file_extensionsscope 不会被继承。

变量继承🔗

扩展语法时,variables 键将与父语法合并。具有相同名称的变量将覆盖以前的值。

变量替换是在所有变量值都实现之后执行的。因此,扩展语法可以更改父语法中的变量,并且父上下文中的所有变量使用都将使用覆盖的值。

上下文继承🔗

扩展语法中的上下文将是父语法中的上下文以及在 contexts 键下定义的所有上下文的组合。

具有相同名称的上下文将覆盖父语法中的上下文。要更改上下文名称重复时的行为,可以使用两种选项。这些元键必须在扩展语法中指定

  • - meta_prepend: true — 扩展语法中的所有模式都将在父语法中的模式之前插入。

  • - meta_append: true — 扩展语法中的所有模式都将在父语法中的模式之后插入。

多重继承🔗

当语法源自两个其他语法的组合时,可以使用多重继承。这允许 extends 键成为指向两个或多个父语法的包路径列表。父语法将按从上到下的顺序处理,并且必须源自相同的基类。

默认语法中多重继承的两个示例是

  • Objective-C++: 扩展C++Objective-C,两者都扩展C

  • TSX: 扩展JSXTypeScript,两者都扩展JavaScript

4086

限制🔗

语法可以扩展本身扩展另一个语法的语法。除了所有语法必须共享相同的 版本 之外,没有对扩展施加任何强制限制。

4075

选定示例🔗

括号平衡🔗

此示例突出显示了没有对应开括号的闭括号

name: C
scope: source.c

contexts:
  main:
    - match: \(
      push: brackets
    - match: \)
      scope: invalid.illegal.stray-bracket-end

  brackets:
    - match: \)
      pop: true
    - include: main

顺序上下文🔗

此示例将重点介绍包含过多分号的 C 样式 for 语句

for_stmt:
  - match: \(
    set: for_stmt_expr1
for_stmt_expr1:
  - match: ';'
    set: for_stmt_expr2
  - match: \)
    pop: true
  - include: expr
for_stmt_expr2:
  - match: ';'
    set: for_stmt_expr3
  - match: \)
    pop: true
  - include: expr
for_stmt_expr3:
  - match: \)
    pop: true
  - match: ';'
    scope: invalid.illegal.stray-semi-colon
  - include: expr

高级堆栈使用🔗

在 C 中,符号通常使用 typedef 关键字定义。为了使“转到定义”能够识别这些符号,这些符号应该具有 entity.name.type 范围。

这样做可能有点棘手,因为虽然 typedef 有时很简单,但它们也会变得相当复杂。

typedef int coordinate_t;

typedef struct
{
    int x;
    int y;
} point_t;

为了识别这些符号,在匹配 typedef 关键字后,将有两个上下文被推入堆栈:第一个将识别一个类型名称,然后弹出,而第二个将识别为该类型引入的名称。

main:
  - match: \btypedef\b
    scope: keyword.control.c
    set: [typedef_after_typename, typename]

typename:
  - match: \bstruct\b
    set:
      - match: '{'
        set:
          - match: '}'
            pop: true
  - match: \b[A-Za-z_][A-Za-z_0-9]*\b
    pop: true

typedef_after_typename:
  - match: \b[A-Za-z_][A-Za-z_0-9]*\b
    scope: entity.name.type
    pop: true

在上面的示例中,typename 是一个可重用的上下文,它将读取一个类型名称并在完成时从堆栈中弹出自身。它可以在任何需要使用类型的上下文中使用,例如在 typedef 中或作为函数参数。

The main 上下文使用一个匹配模式,该模式将两个上下文推入堆栈,其中列表中最右边的上下文成为堆栈中最顶层的上下文。一旦 typename 上下文弹出自身,typedef_after_typename 上下文将位于堆栈的顶部。

还要注意上面在 typename 上下文中为了简洁而使用匿名上下文。

PHP Heredocs🔗

此示例展示了如何在 PHP 中匹配 Heredocs。主上下文中的匹配模式捕获 heredoc 标识符,而 heredoc 上下文中的相应弹出模式使用 \1 符号引用此捕获的文本。

name: PHP
scope: source.php

contexts:
  main:
    - match: <<<([A-Za-z][A-Za-z0-9_]*)
      push: heredoc

  heredoc:
    - meta_scope: string.unquoted.heredoc
    - match: ^\1;
        pop: true

测试🔗

在构建语法定义时,您可以定义一个语法测试文件来为您执行检查,而不是使用 show_scope_name 命令手动检查范围。

// SYNTAX TEST "Packages/C/C.sublime-syntax"
#pragma once
// <- source.c meta.preprocessor.c++
 // <- keyword.control.import

// foo
// ^ source.c comment.line
// <- punctuation.definition.comment

/* foo */
// ^ source.c comment.block
// <- punctuation.definition.comment.begin
//     ^ punctuation.definition.comment.end

#include "stdio.h"
// <- meta.preprocessor.include.c++
//       ^ meta string punctuation.definition.string.begin
//               ^ meta string punctuation.definition.string.end
int square(int x)
// <- storage.type
//  ^ meta.function entity.name.function
//         ^ storage.type
//  @@@@@@ definition
{
    printf("check %d\n", x);
//  @@@@@@ reference

    return x * x;
//  ^^^^^^ keyword.control
}

"Hello, World! // not a comment";
// ^ string.quoted.double
//                  ^ string.quoted.double - comment

要创建一个,请遵循以下规则:

  • 确保文件名以 syntax_test_ 开头。

  • 确保该文件保存在 Packages 目录中的某个位置:与相应的 .sublime-syntax 文件并排是一个不错的选择。

  • 确保文件的第一行以以下内容开头:<comment_token> SYNTAX TEST "<syntax_file>"。请注意,语法文件可以是 .sublime-syntax.tmLanguage 文件。

满足上述条件后,运行 build 命令并选择语法测试或语法定义文件将运行所有语法测试,并在输出面板中显示结果。下一个结果 (F4) 可用于导航到第一个失败的测试。

语法测试文件中的每个测试必须首先启动注释标记(在第一行建立,它实际上不必根据语法是注释),然后是 ^<-@ 标记。

三种类型的测试是:

  • 插入符:^ 这将测试以下选择器与最近的非测试行上的范围。它将在与 ^ 相同的列中进行测试。连续的 ^ 将测试每一列与选择器。

  • 箭头:<- 这将测试以下选择器与最近的非测试行上的范围。它将在与注释字符相同的列中进行测试。

  • At:@ 这将测试以下符号类型与最近的非测试行上的文本。符号类型必须是以下之一:

    • none:文本不是符号

    • local-definition: 文本是符号的定义,但未被索引

    • global-definition: 文本是符号的定义,并且被索引

    • definition: 文本是符号的定义,无论是否被索引

    • reference: 文本是对符号的引用

    当存在这些测试之一时,文件中的所有符号都会被彻底检查。可以使用 partial-symbols 选项禁用此功能。

  • 4087

测试选项可以在 SYNTAX TEST 后面直接指定,用空格隔开,可以是以下任何一项

  • partial-symbols: 通常,当存在符号测试(@)时,文件中的所有符号都必须被检查。此选项禁用此行为。

  • 4087
  • reindent-unchanged: 将整个文件作为缩进规则的测试。检查在整个文件上运行 reindent 命令时,是否没有更改任何行。

  • 4069
  • reindent-unindented: 将整个文件作为缩进规则的测试。检查如果所有行都被取消缩进,reindent 命令是否可以重现文件。

  • 4069
  • reindent: 同时包含 reindent-unchangedreindent-unintented

  • 4069

兼容性🔗

当 Sublime Text 的语法高亮引擎需要进行会破坏现有语法的更改时,这些修改或错误修复将被放在 version 键后面。

目前存在两个版本:12。没有 version 键表示版本 1。

以下是版本 1 中保留的错误和行为列表,这些错误和行为在版本 2 中已修复或更改。此列表主要用于了解在更新语法版本时需要查找的内容。

  • embed_scope 与其他语法的 scope 堆叠

    描述:

    当嵌入另一个语法的 main 上下文时,embed_scope 将与另一个语法的 scope 相结合。在版本 2 语法中,只有在未指定 embed_scope 时,才会包含另一个语法的 scope

    语法 1:

    scope: source.lang
    contexts:
      paragraph:
        - match: \(
          scope: punctuation.section.group.begin
          embed: scope:source.other
          embed_scope: source.other.embedded
          escape: \)
          escape_captures:
            0: punctuation.section.group.end
    

    语法 2:

    scope: source.other
    contexts:
      main:
        - match: '[a-zA-Z0-9_]+'
          scope: identifier
    

    文本:

    'abc'
    

    结果:

    文本 abc 在版本 1 语法中将获得范围 source.other.embedded source.other identifier。在版本 2 语法中,它将获得 source.other.embedded identifier

  • 使用 setmeta_content_scope 的匹配模式

    描述:

    在对匹配项执行 set 操作时,匹配的文本将获得被弹出上下文的 meta_content_scope,即使 pop 操作不会,而 set 等效于 pop 然后 push

    语法:

    scope: source.lang
    contexts:
      function:
        - meta_content_scope: meta.function
        - match: '[a-zA-Z0-9_]+'
          scope: variable.function
        - match: \(
          scope: punctuation.section.group.begin
          set: function-params
    
      function-params:
        - meta_scope: meta.function.params
        - match: \)
          scope: punctuation.section.group.end
          pop: true
    

    文本:

    abc()
    

    结果:

    文本 ( 应该获得范围 meta.function.params punctuation.section.group.begin。但它却错误地获得了范围 meta.function meta.function.params punctuation.section.group.begin

  • 使用 set 匹配模式,使用 clear_scopes 匹配目标

    描述:

    如果一个 set 操作的目标具有 clear_scopes 值,则范围将无法正确清除。

    语法:

    scope: source.lang
    contexts:
      main:
        - match: \bdef\b
          scope: keyword
          push:
            - function
            - function-name
    
      function:
        - meta_scope: meta.function
    
      function-name:
        - match: '[a-zA-Z0-9_]+'
          scope: variable.function
        - match: \(
          scope: punctuation.section.group.begin
          set: function-params
    
      function-params:
        - meta_scope: meta.function.params
        - clear_scopes: 1
        - match: \)
          scope: punctuation.section.group.end
          pop: 2
    

    文本:

    def abc()
    

    结果:

    文本 ( 应该获得范围 meta.function.params punctuation.section.group.begin。但它却错误地获得了范围 meta.function meta.function.params punctuation.section.group.begin

  • 嵌入转义匹配和元范围

    描述:

    embed 操作的 escape 模式匹配的文本将不会获得包含它的上下文的 meta_scopemeta_content_scope

    语法:

    scope: source.lang
    contexts:
      context1:
        - meta_scope: meta.group
        - meta_content_scope: meta.content
        - match: \'
          scope: punctuation.begin
          embed: embed
          escape: \'
          escape_captures:
            0: punctuation.end
    
      embed:
        - match: '[a-z]+'
          scope: word
    

    文本:

    'abc'
    

    结果:

    第二个 ' 应该获得范围 meta.group meta.content punctuation.end。但它却错误地获得了范围 punctuation.end

  • 具有 clear_scopes 的多个目标推送操作

    描述

    如果一次推送多个上下文,并且多个上下文指定了 clear_scopes,其值大于 1,则结果范围将不正确。

    语法:

    scope: source.lang
    contexts:
      main:
        - meta_content_scope: meta.main
        - match: '[a-zA-Z0-9]+\b'
          scope: identifier
          push:
            - context2
            - context3
    
      context2:
        - meta_scope: meta.ctx2
        - clear_scopes: 1
    
      context3:
        - meta_scope: meta.ctx3
        - clear_scopes: 1
        - match: \n
          pop: true
    

    文本:

    abc 1
    

    结果:

    所有目标上下文的 clear_scopes 值将被加起来并应用,然后再应用任何目标的 meta_scopemeta_content_scope。因此,文本 abc 将被赋予范围 meta.ctx2 meta.ctx3 identifier,而不是正确的范围 source.lang meta.ctx3 identifier

  • 正则表达式捕获组顺序

    描述:

    如果编号较低的捕获组匹配的文本出现在编号较高的捕获组匹配的文本之后,则编号较低的捕获组将不会应用其捕获范围。

    语法:

    scope: source.lang
    contexts:
      main:
        - match: '(?:(x)|(y))+'
          captures:
            1: identifier.x
            2: identifier.y
    

    文本:

    yx
    

    结果

    文本 y 由捕获组 2 匹配,文本 x 由捕获组 1 匹配。 x 将不会被赋予范围 indentifier.x,因为它出现在捕获组 2 的匹配之后。