語意突顯指南
語意標示(Semantic highlighting)是對語法標示指南中所述語法標示的補充。Visual Studio Code 使用 TextMate 文法作為主要的 Token 化引擎。TextMate 文法以單一檔案作為輸入,並根據以正規表示式表達的詞法規則將其拆解。
語意 Token 化允許語言伺服器根據其對專案上下文中符號解析的理解,提供額外的 Token 資訊。佈景主題可以選擇使用語意 Token 來改善並精進文法所提供的語法標示。編輯器會在文法標示的基礎上,疊加應用語意 Token 的標示。
以下是語意標示能增加的功能範例
沒有語意標示

有語意標示

注意根據語言服務對符號的理解而產生的顏色差異
- 第 10 行:
languageModes被標示為參數 - 第 11 行:
Range和Position被標示為類別,而document被標示為參數。 - 第 13 行:
getFoldingRanges被標示為函式。
語意 Token 提供者
為了實作語意標示,語言擴充功能可以依照文件語言和/或檔案名稱註冊一個 semantic token provider(語意 Token 提供者)。當需要語意 Token 時,編輯器會向提供者發送請求。
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);
語意 Token 提供者 API 有兩種形式,以適應語言伺服器的功能
-
DocumentSemanticTokensProvider- 始終以完整文件作為輸入。provideDocumentSemanticTokens- 提供文件的所有 Token。provideDocumentSemanticTokensEdits- 以相對於先前回應的差異(delta)形式提供文件的所有 Token。
-
DocumentRangeSemanticTokensProvider- 僅作用於特定範圍。provideDocumentRangeSemanticTokens- 提供文件範圍內的所有 Token。
提供者返回的每個 Token 都附帶一個分類,該分類由 Token 類型、任意數量的 Token 修飾詞以及 Token 語言組成。
如上例所示,提供者會在 SemanticTokensLegend 中命名它將使用的類型和修飾詞。這允許 provide API 將 Token 類型和修飾詞以索引形式返回給圖例(Legend)。
語意 Token 分類
語意 Token 提供者的輸出由 Token 組成。每個 Token 都有一個範圍和一個 Token 分類,描述了該 Token 代表哪種語法元素。如果 Token 是嵌入式語言的一部分,分類還可以選擇性地命名語言。
為了描述語法元素的種類,會使用語意 Token 類型和修飾詞。此資訊類似於語法標示指南中描述的 TextMate 作用域,但我們希望設計一套專屬且更簡潔的分類系統。
VS Code 為所有語意 Token 提供者提供了一套標準的語意 Token 類型和修飾詞。不過,語意 Token 提供者仍可自由定義新的類型和修飾詞,並建立標準類型的子類型。
標準 Token 類型與修飾詞
標準類型和修飾詞涵蓋了許多語言通用的概念。雖然每種語言對於某些類型和修飾詞可能使用不同的術語,但透過遵守標準分類,佈景主題作者將能定義跨語言通用的佈景規則。
以下是由 VS Code 預定義的標準語意 Token 類型和語意 Token 修飾詞
標準 Token 類型
| ID | 說明 |
|---|---|
namespace |
用於宣告或引用命名空間、模組或套件的識別碼。 |
class |
用於宣告或引用類別型別的識別碼。 |
enum |
用於宣告或引用列舉型別的識別碼。 |
interface |
用於宣告或引用介面型別的識別碼。 |
struct |
用於宣告或引用結構型別的識別碼。 |
typeParameter |
用於宣告或引用型別參數的識別碼。 |
類型 |
用於宣告或引用上述未涵蓋之型別的識別碼。 |
parameter |
用於宣告或引用函式或方法參數的識別碼。 |
變數 |
用於宣告或引用區域或全域變數的識別碼。 |
property |
用於宣告或引用成員屬性、成員欄位或成員變數的識別碼。 |
enumMember |
用於宣告或引用列舉屬性、常數或成員的識別碼。 |
decorator |
用於宣告或引用裝飾器(Decorator)和註釋(Annotation)的識別碼。 |
事件 |
用於宣告事件屬性的識別碼。 |
function |
用於宣告函式的識別碼。 |
method |
用於宣告成員函式或方法的識別碼。 |
macro |
用於宣告巨集的識別碼。 |
label |
用於宣告標籤(Label)的識別碼。 |
comment |
用於代表註解的 Token。 |
string |
用於代表字串常值的 Token。 |
keyword |
用於代表語言關鍵字的 Token。 |
number |
用於代表數字常值的 Token。 |
regexp |
用於代表正規表示式常值的 Token。 |
operator |
用於代表運算子的 Token。 |
標準 Token 修飾詞
| ID | 說明 |
|---|---|
declaration |
用於符號的宣告。 |
definition |
用於符號的定義,例如在標頭檔中。 |
readonly |
用於唯讀變數和成員欄位(常數)。 |
static |
用於類別成員(靜態成員)。 |
deprecated |
用於不應再使用的符號。 |
abstract |
用於抽象的型別和成員函式。 |
async |
用於標記為 async 的函式。 |
modification |
用於變數被賦值的變數引用。 |
documentation |
用於文件中出現的符號。 |
defaultLibrary |
用於屬於標準函式庫的符號。 |
除了標準類型和修飾詞外,VS Code 還定義了一組將類型和修飾詞映射到類似 TextMate 作用域的規則。這部分內容在語意 Token 作用域映射章節中說明。
自訂 Token 類型與修飾詞
如有必要,擴充功能可以透過其 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。
除了自訂 Token 類型外,擴充功能還能定義如何將它們映射到 TextMate 作用域。這在自訂映射章節中有說明。請注意,自訂映射規則不會自動從父類型繼承。相反地,子類型需要重新定義映射,最好映射到更具體的作用域。
語意標示的啟用
是否計算並標示語意 Token 取決於設定 editor.semanticHighlighting.enabled。它可以設定為 true、false 或 configuredByTheme。
true和false會為所有佈景主題開啟或關閉語意標示。configuredByTheme是預設值,讓每個佈景主題自行控制是否啟用語意標示。所有隨 VS Code 附帶的佈景主題(例如預設的 "Dark+")預設皆已啟用語意標示。
依賴語意 Token 的語言擴充功能可以在其 package.json 中為該語言覆寫預設設定。
{
"configurationDefaults": {
"[languageId]": {
"editor.semanticHighlighting.enabled": true
}
}
}
主題設定
佈景主題的核心在於為 Token 指定顏色和樣式。佈景規則是在「色彩佈景主題」(Color Theme)檔案(JSON 格式)中指定的。使用者也可以在使用者設定中自訂這些規則。
色彩佈景主題中的語意著色
為了支援基於語意 Token 的標示,色彩佈景主題檔案格式中新增了兩個屬性。
屬性 semanticHighlighting 定義該佈景主題是否已準備好進行語意 Token 標示。預設為 false,但我們鼓勵所有佈景主題啟用它。此屬性在設定 editor.semanticHighlighting.enabled 為 configuredByTheme 時生效。
屬性 semanticTokenColors 允許佈景主題定義新的著色規則,以匹配語意 Token 提供者發出的語意 Token 類型和修飾詞。
{
"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 },或是與 tokenColors 中 TextMate 佈景規則所使用的 { foreground: string, fontStyle: string } 相同。
前景色需要遵循色彩格式中描述的格式。不支援透明度。
以下是選擇器和樣式的其他範例
"*.declaration": { "bold": true } // 所有宣告皆為粗體"class:java": { "foreground": "#0f0", "italic": true } // java 中的類別
如果沒有匹配的規則,或佈景主題沒有 semanticTokenColors 區塊(但已啟用 semanticHighlighting),VS Code 會使用語意 Token 作用域映射來評估給定語意 Token 的 TextMate 作用域。該作用域將與 tokenColors 中佈景主題的 TextMate 佈景規則進行匹配。
語意 Token 作用域映射
為了讓語意標示能在未定義任何特定語意規則的佈景主題上運作,並作為自訂 Token 類型和修飾詞的備援,VS Code 維護了一個從語意 Token 選擇器到 TextMate 作用域的映射表。
如果佈景主題啟用了語意標示,但沒有包含給定語意 Token 的規則,則會使用這些 TextMate 作用域來尋找 TextMate 佈景規則。
預定義的 TextMate 作用域映射
下表列出了目前預定義的映射。
| 語意 Token 選擇器 | 備援 TextMate 作用域 |
|---|---|
namespace |
entity.name.namespace |
類型 |
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.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 |
事件 |
variable.other.event |
自訂 TextMate 作用域映射
此映射可以由擴充功能透過其 package.json 中的 semanticTokenScopes 貢獻點進行擴充。
擴充功能有兩種使用情境:
-
定義自訂 Token 類型和 Token 修飾詞的擴充功能,在佈景主題未定義該新增語意 Token 類型或修飾詞的規則時,提供 TextMate 作用域作為備援。
{ "contributes": { "semanticTokenScopes": [ { "scopes": { "templateType": ["entity.name.type.template"] } } ] } } -
TextMate 文法的提供者可以描述語言特定的作用域。這有助於使用包含語言特定佈景規則的佈景主題。
{ "contributes": { "semanticTokenScopes": [ { "language": "typescript", "scopes": { "property.readonly": ["variable.other.constant.property.ts"] } } ] } }
試試看
我們提供了一個 Semantic Tokens 範例,展示了如何建立語意 Token 提供者。
作用域檢查器(Scope inspector)工具可讓你探索原始碼檔案中存在哪些語意 Token,以及它們匹配到哪些佈景規則。若要查看語意 Token,請在 TypeScript 檔案上使用內建佈景主題(例如 Dark+)。