现已发布!阅读 10 月份的新功能和修复。

语义高亮指南

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

语义标记允许语言服务器根据语言服务器对如何在项目上下文中解析符号的了解来提供额外的标记信息。主题可以选择使用语义标记来改进和优化来自语法的语法高亮。编辑器在来自语法的突出显示之上应用来自语义标记的突出显示。

以下是一个语义高亮可以添加的内容的示例

没有语义高亮

without semantic highlighting

使用语义高亮

with semantic highlighting

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

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

语义令牌提供者

为了实现语义高亮,语言扩展可以根据文档语言和/或文件名注册一个 语义令牌提供者。当需要语义令牌时,编辑器将向提供者发出请求。

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 中命名它将使用的类型和修饰符。这使得 provide API 可以以索引到图例的形式返回令牌类型和修饰符。

语义令牌分类

语义令牌提供者的输出包括令牌。每个令牌都有一个范围和一个令牌分类,该分类描述了令牌表示的语法元素类型。可选地,如果令牌是嵌入式语言的一部分,分类还可以命名语言。

为了描述语法元素的类型,使用语义令牌类型和修饰符。此信息类似于 语法高亮指南 中描述的 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 作用域的映射。这在 语义令牌作用域映射 部分中介绍。

自定义令牌类型和修饰符

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

{
  "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 } // 所有声明都是粗体
  • "class:java": { "foreground": "#0f0", "italic": true } // 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.preprocessor
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 范围映射

扩展可以通过其package.json中的semanticTokenScopes贡献点扩展此映射。

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

  • 定义自定义令牌类型和令牌修饰符的扩展提供了 TextMate 范围作为回退,当主题没有为添加的语义令牌类型或修饰符定义主题规则时

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

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

试一试

我们有一个 语义令牌示例,演示如何创建语义令牌提供者。

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