语义高亮指南

语义高亮是对语法高亮指南中描述的语法高亮的补充。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 - Provides all tokens of a document.
    • provideDocumentSemanticTokensEdits- Provides all tokens of a document as a delta to the previous response.
  • DocumentRangeSemanticTokensProvider - 仅适用于范围。

    • provideDocumentRangeSemanticTokens - Provides all tokens of a document range.

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

如上例所示,提供者在SemanticTokensLegend中命名了它将使用的类型和修饰符。这使得provide API能够将标记类型和修饰符作为图例的索引返回。

语义标记分类

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

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

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

标准令牌类型和修饰符

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

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

标准令牌类型:

ID Description
namespace For identifiers that declare or reference a namespace, module, or package.
class For identifiers that declare or reference a class type.
enum For identifiers that declare or reference an enumeration type.
interface For identifiers that declare or reference an interface type.
struct For identifiers that declare or reference a struct type.
typeParameter For identifiers that declare or reference a type parameter.
type For identifiers that declare or reference a type that is not covered above.
parameter For identifiers that declare or reference a function or method parameters.
variable For identifiers that declare or reference a local or global variable.
property For identifiers that declare or reference a member property, member field, or member variable.
enumMember For identifiers that declare or reference an enumeration property, constant, or member.
decorator For identifiers that declare or reference decorators and annotations.
event For identifiers that declare an event property.
function For identifiers that declare a function.
method For identifiers that declare a member function or method.
macro For identifiers that declare a macro.
label For identifiers that declare a label.
comment For tokens that represent a comment.
string For tokens that represent a string literal.
keyword For tokens that represent a language keyword.
number For tokens that represent a number literal.
regexp For tokens that represent a regular expression literal.
operator For tokens that represent an operator.

标准令牌修饰符:

ID Description
declaration For declarations of symbols.
definition For definitions of symbols, for example, in header files.
readonly For readonly variables and member fields (constants).
static For class members (static members).
deprecated For symbols that should no longer be used.
abstract For types and member functions that are abstract.
async For functions that are marked async.
modification For variable references where the variable is assigned to.
documentation For occurrences of symbols in documentation.
defaultLibrary For symbols that are part of the standard library.

除了标准类型和修饰符外,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格式)中指定。用户还可以在用户设置中自定义主题规则。

颜色主题中的语义着色

为了支持基于语义标记的高亮显示,Color Theme 文件格式中新增了两个属性。

属性 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 },用于tokenColors中的TextMate主题规则。

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

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

  • "*.declaration": { "bold": true } // 所有声明都是粗体
  • "class:java": { "foreground": "#0f0", "italic": true } // Java中的类

如果没有规则匹配或主题没有semanticTokenColors部分(但启用了semanticHighlighting),VS Code 使用语义令牌范围映射来评估给定语义令牌的TextMate范围。该范围与主题中的TextMate主题规则在tokenColors中进行匹配。

语义标记范围映射

为了使语义高亮适用于尚未定义任何特定语义规则的主题,并作为自定义令牌类型和修饰符的回退,VS Code 维护了一个从语义令牌选择器到 TextMate 作用域的映射。

如果主题启用了语义高亮显示,但不包含给定语义标记的规则,则使用这些TextMate范围来查找TextMate主题规则。

预定义的TextMate范围映射

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

Semantic Token Selector Fallback TextMate Scope
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+)。