语义高亮指南
语义高亮是对语法高亮指南中描述的语法高亮的补充。Visual Studio Code 使用 TextMate 语法作为主要的标记化引擎。TextMate 语法以单个文件作为输入,并根据正则表达式表达的词汇规则将其分解。
语义标记化允许语言服务器基于其对如何在项目上下文中解析符号的知识提供额外的标记信息。主题可以选择使用语义标记来改进和优化语法高亮。编辑器在语法高亮的基础上应用语义标记的高亮。
以下是语义高亮可以添加的示例:
没有语义高亮显示:
使用语义高亮显示:
注意基于语言服务符号理解的颜色差异:
- 第10行:
languageModes
被着色为一个参数 - 第11行:
Range
和Position
被着色为类,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
中的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格式)中指定。用户还可以在用户设置中自定义主题规则。
颜色主题中的语义着色
为了支持基于语义标记的高亮显示,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+)。