语义突出显示指南

语义突出显示是对语法突出显示的补充,如语法突出显示指南中所述。Visual Studio Code 使用 TextMate 语法作为主要标记化引擎。TextMate 语法将单个文件作为输入,并根据正则表达式中表达的词汇规则对其进行分解。

语义标记化允许语言服务器根据语言服务器关于如何解析项目上下文中的符号的知识来提供附加标记信息。主题可以选择使用语义标记来改进和细化语法中的语法突出显示。编辑器将语义标记的突出显示应用到语法的突出显示之上。

以下是语义突出显示可以添加的内容的示例:

没有语义突出显示:

没有语义突出显示

通过语义突出显示:

具有语义突出显示

请注意基于语言服务符号理解的颜色差异:

  • 第 10 行:languageModes作为参数着色
  • 第 11 行:RangePosition被着色为类和document参数。
  • 第 13 行:getFoldingRanges作为函数着色。

语义标记提供者

为了实现语义突出显示,语言扩展可以按文档语言和/或文件名注册semantic token provider。当需要语义标记时,编辑器将向提供者提出请求。

const tokenTypes = ['class', 'interface', 'enum', 'function', 'variable'];
const tokenModifiers = ['declaration', 'documentation'];
const legend = new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers);

const provider: vscode.DocumentSemanticTokensProvider = {
  provideDocumentSemanticTokens(
    document: vscode.TextDocument
  ): vscode.ProviderResult<vscode.SemanticTokens> {
    // analyze the document and return semantic tokens

    const tokensBuilder = new vscode.SemanticTokensBuilder(legend);
    // on line 1, characters 1-5 are a class declaration
    tokensBuilder.push(
      new vscode.Range(new vscode.Position(1, 1), new vscode.Position(1, 5)),
      'class',
      ['declaration']
    );
    return tokensBuilder.build();
  }
};

const selector = { language: 'java', scheme: 'file' }; // register for all Java documents from the local file system

vscode.languages.registerDocumentSemanticTokensProvider(selector, provider, legend);

语义令牌提供程序 API 有两种形式来适应语言服务器的功能:

  • DocumentSemanticTokensProvider- 始终采用完整文档作为输入。

    • provideDocumentSemanticTokens- 提供文档的所有标记。
    • provideDocumentSemanticTokensEdits- 提供文档的所有标记作为先前响应的增量。
  • DocumentRangeSemanticTokensProvider- 仅适用于一定范围。

    • provideDocumentRangeSemanticTokens- 提供文档范围的所有标记。

提供者返回的每个令牌都带有一个分类,该分类由令牌类型、任意数量的令牌修饰符和令牌语言组成。

如上面的示例所示,提供程序命名它将在SemanticTokensLegend. 这允许provideAPI 返回标记类型并修改为图例的索引。

语义标记分类

语义标记提供者的输出由标记组成。每个标记都有一个范围和一个标记分类,用于描述该标记代表哪种语法元素。可选地,如果标记是嵌入语言的一部分,则分类还可以命名语言。

为了描述语法元素的种类,使用语义标记类型和修饰符。此信息类似于语法突出显示指南中描述的 TextMate 范围,但我们希望提出一个专用且更清晰的分类系统。

VS Code 附带了一组标准语义标记类型和修饰符,供所有语义标记提供程序使用。尽管如此,语义标记提供者仍然可以自由定义新类型和修饰符并创建标准类型的子类型。

标准令牌类型和修饰符

标准类型和修饰符涵盖了许多语言使用的常见概念。虽然每种语言可能对某些类型和修饰符使用不同的术语,但通过遵守标准分类,主题作者可以定义跨语言的主题规则。

这些是 VS Code 预定义的标准语义标记类型和语义标记修饰符:

标准代币类型:

ID 描述
namespace 用于声明或引用命名空间、模块或包的标识符。
class 用于声明或引用类类型的标识符。
enum 用于声明或引用枚举类型的标识符。
interface 用于声明或引用接口类型的标识符。
struct 用于声明或引用结构类型的标识符。
typeParameter 用于声明或引用类型参数的标识符。
type 对于声明或引用上面未涵盖的类型的标识符。
parameter 用于声明或引用函数或方法参数的标识符。
variable 用于声明或引用局部或全局变量的标识符。
property 用于声明或引用成员属性、成员字段或成员变量的标识符。
enumMember 用于声明或引用枚举属性、常量或成员的标识符。
decorator 用于声明或引用装饰器和注释的标识符。
event 用于声明事件属性的标识符。
function 用于声明函数的标识符。
method 用于声明成员函数或方法的标识符。
macro 对于声明宏的标识符。
label 对于声明标签的标识符。
comment 对于代表评论的标记。
string 对于表示字符串文字的标记。
keyword 用于表示语言关键字的标记。
number 对于代表数字文字的标记。
regexp 用于表示正则表达式文字的标记。
operator 对于代表运算符的标记。

标准标记修饰符:

ID 描述
declaration 用于符号声明。
definition 用于符号的定义,例如,在头文件中。
readonly 对于只读变量和成员字段(常量)。
static 对于类成员(静态成员)。
deprecated 对于不应再使用的符号。
abstract 对于抽象的类型和成员函数。
async 对于标记为异步的函数。
modification 对于变量被分配到的变量引用。
documentation 对于文档中出现的符号。
defaultLibrary 对于属于标准库一部分的符号。

除了标准类型和修饰符之外,VS Code 还定义了类型和修饰符到类似 TextMate 范围的映射。语义标记范围图部分对此进行了介绍。

自定义令牌类型和修饰符

如果需要,扩展可以声明新类型和修饰符,或者通过其扩展中的semanticTokenTypes和贡献点创建现有类型的子类型:semanticTokenModifierspackage.json

{
  "contributes": {
    "semanticTokenTypes": [
      {
        "id": "templateType",
        "superType": "type",
        "description": "A template type."
      }
    ],
    "semanticTokenModifiers": [
      {
        "id": "native",
        "description": "Annotates a symbol that is implemented natively"
      }
    ]
  }
}

在上面的示例中,扩展声明了一个新类型templateType和一个新修饰符native。通过命名type为超类型,主题样式规则type也将适用于templateType

{
  "name": "Red Theme",
  "semanticTokenColors": {
    "type": "#ff0011"
  }
}

semanticTokenColors上面显示的值适用"#ff0011"于其type所有子类型,包括templateType.

除了自定义令牌类型之外,扩展还可以定义如何将它们映射到 TextMate 范围。自定义映射部分对此进行了描述。请注意,自定义映射规则不会自动从超类型继承。相反,子类型需要重新定义映射,最好是到更具体的范围。

启用语义突出显示

是否计算语义标记并突出显示由设置决定editor.semanticHighlighting.enabled。它可以有值truefalseconfiguredByTheme

  • truefalse为所有主题打开或关闭语义突出显示。
  • configuredByTheme是默认值,让每个主题控制是否启用语义突出显示。VS Code 附带的所有主题(例如“Dark+”默认值)默认启用语义突出显示。

依赖于语义标记的语言扩展可以覆盖其语言的默认值package.json

{
  "configurationDefaults": {
    "[languageId]": {
      "editor.semanticHighlighting.enabled": true
    }
  }
}

主题化

主题化是指为标记分配颜色和样式。主题规则在颜色主题文件(JSON 格式)中指定。用户还可以在用户设置中自定义主题规则。

颜色主题中的语义着色

颜色主题文件格式中添加了两个新属性,以支持基于语义标记的突出显示。

该属性semanticHighlighting定义主题是否准备好使用语义标记突出显示。默认情况下它是 false,但我们鼓励所有主题启用它。editor.semanticHighlighting.enabled当设置设为时,使用该属性configuredByTheme

该属性semanticTokenColors允许主题定义与语义标记提供程序发出的语义标记类型和修饰符相匹配的新着色规则。

{
  "name": "Red Theme",
  "tokenColors": [
    {
      "scope": "comment",
      "settings": {
        "foreground": "#dd0000",
        "fontStyle": "italic"
      }
    }
  ],
  "semanticHighlighting": true,
  "semanticTokenColors": {
    "variable.readonly:java": "#ff0011"
  }
}

variable.readonly:java称为选择器并具有形式(*|tokenType)(.tokenModifier)*(:tokenLanguage)?.

如果规则匹配,该值描述样式。它可以是一个字符串,表示前景色,也可以是一个对象,采用形式{ foreground: string, bold: boolean, italic: boolean, underline: boolean }{ foreground: string, fontStyle: string }用于 中的 TextMate 主题规则tokenColors

前景需要遵循颜色格式中描述的颜色格式。不支持透明度。

以下是选择器和样式的其他示例:

  • "*.declaration": { "bold": true } // all declarations are bold
  • "class:java": { "foreground": "#0f0", "italic": true } // classes in java

如果没有规则匹配或主题没有semanticTokenColors部分(但semanticHighlighting已启用),VS Code 将使用语义标记范围映射来评估给定语义标记的 TextMate 范围。该范围与主题 TextMate 主题规则相匹配tokenColors

语义标记范围图

为了使语义突出显示适用于未定义任何特定语义规则的主题,并作为自定义标记类型和修饰符的后备,VS Code 维护了从语义标记选择器到 TextMate 范围的映射。

如果主题启用了语义突出显示,但不包含给定语义标记的规则,则这些 TextMate 范围将用于查找 TextMate 主题规则。

预定义的 TextMate 范围映射

下表列出了当前预定义的映射。

语义标记选择器 后备 TextMate 范围
namespace entity.name.namespace
type entity.name.type
type.defaultLibrary support.type
struct storage.type.struct
class entity.name.type.class
class.defaultLibrary support.class
interface entity.name.type.interface
enum entity.name.type.enum
function entity.name.function
function.defaultLibrary support.function
method entity.name.function.member
macro entity.name.function.macro
variable variable.other.readwrite,entity.name.variable
variable.readonly variable.other.constant
variable.readonly.defaultLibrary support.constant
parameter variable.parameter
property variable.other.property
property.readonly variable.other.constant.property
enumMember variable.other.enummember
event variable.other.event

自定义 TextMate 范围映射

semanticTokenScopes这张地图可以通过他们的贡献点进行扩展package.json

扩展有两个用例可以做到这一点:

  • 当主题没有为添加的语义标记类型或修饰符定义主题规则时,定义自定义标记类型和标记修饰符的扩展会提供 TextMate 范围作为后备:

    {
      "contributes": {
        "semanticTokenScopes": [
          {
            "scopes": {
              "templateType": ["entity.name.type.template"]
            }
          }
        ]
      }
    }
    
  • TextMate 语法的提供者可以描述特定于语言的范围。这有助于包含特定于语言的主题规则的主题。

    {
      "contributes": {
        "semanticTokenScopes": [
          {
            "language": "typescript",
            "scopes": {
              "property.readonly": ["variable.other.constant.property.ts"]
            }
          }
        ]
      }
    }
    

试试看

我们有一个语义令牌示例,说明如何创建语义令牌提供程序。

范围检查器工具允许您探索源文件中存在哪些语义标记以及它们与哪些主题规则匹配。要查看语义标记,请在 TypeScript 文件上使用内置主题(例如 Dark+)。