语义高亮指南
语义高亮是对 语法高亮指南 中所述语法高亮的补充。Visual Studio Code 使用 TextMate 语法作为主要的标记化引擎。TextMate 语法以单个文件作为输入,并根据正则表达式表达的词法规则将其分解。
语义标记化允许语言服务器基于其在项目上下文中解析符号的知识,提供额外的标记信息。主题可以自行选择使用语义标记,以改进和细化基于语法的语法高亮。编辑器会将语义标记的高亮效果应用在语法高亮之上。
以下是语义高亮可以添加的内容示例
没有语义高亮

启用语义高亮

注意基于语言服务符号理解的颜色差异
- 第 10 行:
languageModes被着色为参数 - 第 11 行:
Range和Position被着色为类,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- 将文档的所有标记作为与先前响应的增量(delta)提供。
-
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 |
用于表示注释的标记。 |
字符串 |
用于表示字符串字面量的标记。 |
keyword |
用于表示语言关键字的标记。 |
number |
用于表示数字字面量的标记。 |
regexp |
用于表示正则表达式字面量的标记。 |
operator |
用于表示运算符的标记。 |
标准标记修饰符
| ID | 描述 |
|---|---|
declaration |
用于符号声明。 |
definition |
用于符号定义,例如在头文件中。 |
readonly |
用于只读变量和成员字段(常量)。 |
static |
用于类成员(静态成员)。 |
deprecated |
用于不再使用的符号。 |
abstract |
用于抽象类型和成员函数。 |
async |
用于标记为 async 的函数。 |
modification |
用于已赋值的变量引用。 |
documentation |
用于文档中出现的符号。 |
defaultLibrary |
用于标准库中的符号。 |
除了标准类型和修饰符外,VS Code 还定义了类型和修饰符到类似 TextMate 作用域的映射。这将在 语义标记作用域映射 一节中介绍。
自定义标记类型和修饰符
如有必要,扩展可以通过其 package.json 中的 semanticTokenTypes 和 semanticTokenModifiers 贡献点声明新的类型和修饰符,或创建现有类型的子类型。
{
"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 决定。它可以有 true、false 和 configuredByTheme 三个值。
true和false可开启或关闭所有主题的语义高亮。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 },正如 tokenColors 中的 TextMate 主题规则所使用的那样。
前景色需要遵循 颜色格式 中描述的颜色格式。不支持透明度。
以下是选择器和样式的其他示例
"*.declaration": { "bold": true } // 所有声明加粗"class:java": { "foreground": "#0f0", "italic": true } // java 中的类
如果没有任何规则匹配,或者主题没有 semanticTokenColors 部分(但启用了 semanticHighlighting),VS Code 将使用 语义标记作用域映射 来评估给定语义标记的 TextMate 作用域。该作用域将与主题在 tokenColors 中的 TextMate 主题规则进行匹配。
语义标记作用域映射
为了使语义高亮对未定义任何特定语义规则的主题生效,并作为自定义标记类型和修饰符的后备,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+)。