语义高亮指南
语义高亮是语法高亮指南中描述的语法高亮的补充。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
- 提供文档的所有标记作为对先前响应的增量。
-
DocumentRangeSemanticTokensProvider
- 仅对范围起作用。provideDocumentRangeSemanticTokens
- 提供文档范围的所有标记。
提供程序返回的每个标记都带有一个分类,该分类由标记类型、任意数量的标记修饰符和标记语言组成。
如上例所示,提供程序在SemanticTokensLegend
中命名它将使用的类型和修饰符。这允许provide
API 返回标记类型并将它们修改为图例的索引。
语义标记分类
语义标记提供程序的输出由标记组成。每个标记都有一个范围和一个标记分类,描述了该标记代表哪种语法元素。如果标记是嵌入语言的一部分,则分类还可以可选地命名一种语言。
为了描述语法元素的类型,使用了语义标记类型和修饰符。此信息类似于语法高亮指南中描述的 TextMate 作用域,但我们希望提出一个专用且更清晰的分类系统。
VS Code 带有一组标准语义标记类型和修饰符供所有语义标记提供程序使用。尽管如此,语义标记提供程序可以自由定义新的类型和修饰符,并创建标准类型的子类型。
标准标记类型和修饰符
标准类型和修饰符涵盖了许多语言使用的常见概念。虽然每种语言可能对某些类型和修饰符使用不同的术语,但通过遵守标准分类,主题作者将能够定义跨语言工作的主题规则。
这些是 VS Code 预定义的标准语义标记类型和语义标记修饰符
标准标记类型
ID | 描述 |
---|---|
命名空间 |
用于声明或引用命名空间、模块或包的标识符。 |
类 |
用于声明或引用类类型的标识符。 |
enum |
用于声明或引用枚举类型的标识符。 |
接口 |
用于声明或引用接口类型的标识符。 |
结构体 |
用于声明或引用结构体类型的标识符。 |
类型参数 |
用于声明或引用类型参数的标识符。 |
type |
用于声明或引用上述未涵盖的类型的标识符。 |
参数 |
用于声明或引用函数或方法参数的标识符。 |
variable |
用于声明或引用局部或全局变量的标识符。 |
property |
用于声明或引用成员属性、成员字段或成员变量的标识符。 |
enumMember |
用于声明或引用枚举属性、常量或成员的标识符。 |
装饰器 |
用于声明或引用装饰器和注解的标识符。 |
event |
用于声明事件属性的标识符。 |
函数 |
用于声明函数的标识符。 |
方法 |
用于声明成员函数或方法的标识符。 |
宏 |
用于声明宏的标识符。 |
标签 |
用于声明标签的标识符。 |
注释 |
表示注释的标记。 |
字符串 |
表示字符串字面量的标记。 |
关键字 |
表示语言关键字的标记。 |
number |
表示数字字面量的标记。 |
正则表达式 |
表示正则表达式字面量的标记。 |
运算符 |
表示运算符的标记。 |
标准标记修饰符
ID | 描述 |
---|---|
声明 |
用于符号的声明。 |
定义 |
用于符号的定义,例如在头文件中。 |
只读 |
用于只读变量和成员字段(常量)。 |
静态 |
用于类成员(静态成员)。 |
已弃用 |
用于不应再使用的符号。 |
抽象 |
用于抽象类型和成员函数。 |
异步 |
用于标记为异步的函数。 |
修改 |
用于变量被赋值的变量引用。 |
文档 |
用于文档中符号的出现。 |
默认库 |
用于属于标准库的符号。 |
除了标准类型和修饰符,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 附带的主题(例如,“深色+”默认主题)都默认启用了语义高亮。
依赖于语义标记的语言扩展可以在其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 作用域 |
---|---|
命名空间 |
entity.name.namespace |
type |
entity.name.type |
type.defaultLibrary |
support.type |
结构体 |
storage.type.struct |
类 |
entity.name.type.class |
class.defaultLibrary |
support.class |
接口 |
entity.name.type.interface |
enum |
entity.name.type.enum |
函数 |
entity.name.function |
function.defaultLibrary |
support.function |
方法 |
entity.name.function.member |
宏 |
entity.name.function.preprocessor |
variable |
variable.other.readwrite , entity.name.variable |
variable.readonly |
variable.other.constant |
variable.readonly.defaultLibrary |
support.constant |
参数 |
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 文件上使用内置主题(例如,深色+)。