Skip to content

语义 Token ​

clice 使用自有的 Token 类型体系对文档中的每个 Token 进行分类。这套体系比标准 LSP Token 类型更丰富,并在 clice 的所有响应中保持一致。偏好标准 LSP 类型的客户端可以通过配置进行映射。

词法 Token ​

直接从 Token 流本身推导出的类型,不依赖 AST。

支持

注释

行注释、块注释和文档注释均标记为注释 Token

支持

字面量

数字、字符和字符串均标记为字面量 Token

支持

关键字

运算符的替代拼写和上下文说明符仍标记为关键字 Token

支持

预处理指令

#if 指令链保留指令类型;未启用的分支保留词法类型;pragma 参数保持普通文本样式

支持

非活动区域

未选中分支中的 Token 保留其词法类型,并带有 inactive 修饰符;未分类的 Token 则归为普通的 identifier 类型以承载该修饰符,因此即使某行只有一个 },也会显示为暗淡样式

支持

头文件名

包含指令中用引号或尖括号括起的文件名均标记为字符串 Token

支持

Preamble 中的非活动区域

文件开头各指令中未选中的分支也以相同方式显示为暗淡样式

不支持

字面量前缀和后缀

字面量的前缀、后缀和分隔符尚未标记为独立的 Token

不支持

转义序列

字面量中的转义序列尚未单独高亮显示

不支持clangd#1421

声明符与运算符的区分

声明符中的运算符和表达式中的运算符尚未使用不同的 Token 类型

支持

基本类型的 Token 类型

内置类型使用独立的 Token 类型,而非普通的 keyword

不支持

括号的 Token 类型

匹配的括号尚未使用各对括号专属的 Token 类型

声明 ​

名称按其所定义或引用的声明进行分类。

支持

命名空间

命名空间的定义、引用、嵌套和别名均标记为命名空间 Token

支持

类型

类型定义和引用保留各自的类型类别

支持

函数和方法

函数声明、定义和调用均标记为函数 Token

支持

变量

变量声明和引用保留各自的变量类别

支持

模板

模板参数标记为类型或变量类别,模板名称带有 templated 修饰符

支持

Concept

Concept 的定义及其在约束中的使用均标记为 Concept Token

支持

标签

标签及其在 goto 中的引用均标记为标签 Token

支持

结构化绑定(structured bindings)

结构化绑定名称在定义和使用处均标记为变量 Token

起始的 [ 特意不标记为任何 Token;仅高亮显示绑定名称本身。

Lambda 初始化捕获

Lambda 初始化捕获标记为变量 Token

支持

推导指引(deduction guides)

推导指引及其所引导的模板均标记为类型 Token

显式实例化

类的显式实例化会高亮显示模板名称和显式写出的实参

支持

变量模板

变量模板的声明、定义和特化均标记为变量 Token

支持

类外成员定义

限定名称保留方法类别和修饰符

支持

别名模板

别名名称带有类型类别和 templated 修饰符

支持

模板模板参数

模板模板参数(template-template parameters)在声明和使用处均获得类型 Token

支持

友元声明

友元声明中的名称解析到其目标;内联友元标记为定义

支持

函数显式实例化指令

函数显式实例化指令中的标识符会被高亮

支持

变量显式实例化指令

变量显式实例化指令中的标识符会被高亮

支持

显式实例化的成员体

待决名(dependent names)按实际解析结果高亮:各实例化中的种类一致时,保留所有实例化共有的修饰符;种类不一致时,标记为冲突

引用 ​

引用处保留其所解析到的声明的语义种类,通过语言特有的查找规则找到的名称也不例外。

成员初始化列表

成员初始化列表中被初始化的名称按字段高亮

using 声明

引入的名称保留其目标的种类

sizeof...

参数包保留其类型参数 Token

using enum

using 声明在 using 处高亮枚举名称

待决名

待决名通过已知的主模板解析

已知模板(Box<T>)的待决成员解析到主模板中的声明,并保留这些声明的种类。直接以模板参数为所属类型的成员没有候选声明,目前不获得 Token;对此类名称的启发式高亮仍有待解决。

支持

Lambda 捕获

按复制捕获和按引用捕获都引用被捕获的变量;this 仍作为关键字

支持

范围 for 循环

范围 for 循环变量在定义和使用处均保留变量 Token

支持

枚举底层类型

枚举底层类型的引用保留其类型种类

部分支持

待决 using 声明

待决 using 声明仍不高亮

引入的名称及其使用处目前均不获得 Token;预留的待决名修饰符尚未输出。

模块 ​

支持

模块声明

模块声明为上下文关键字、以点分隔的名称和私有模块片段生成 Token

支持

模块分区

模块声明为分区名称生成 Token

支持

作为标识符的 module 和 import

上下文关键字在模块声明之外保留其语义种类

Token 修饰符 ​

支持

声明与定义

声明修饰符和定义修饰符用于区分这两种位置

支持

静态

静态成员和静态局部变量带有静态修饰符

支持

只读

const 值、const 成员函数以及枚举成员带有只读修饰符

目前只读属性根据值判断:指向 const 对象的指针也视为只读,即使指针本身可以改变。

支持

虚与抽象

虚成员函数和抽象类分别带有虚修饰符和抽象修饰符

支持

已弃用

已弃用的声明及其使用处带有已弃用修饰符

支持

默认库

来自系统头文件的符号带有默认库修饰符

不支持clangd#352

作用域修饰符

符号尚不带有函数、类、文件或全局作用域修饰符

不支持clangd#839

可变引用和指针

可变引用和指针实参尚不带有修饰符

不支持

推导

推导出的类型尚不带有专用修饰符

不支持clangd#1521

用户定义的运算符

重载运算符目前尚未与内置运算符区分

冲突与歧义 ​

C++ 允许结构不同的实体共用一个名称。当代码中的同一个名称同时指向不同种类的实体时,没有任何单一的 Token 类型能准确表示它;这类名称会获得专用的 conflict(冲突)Token 类型,客户端通常以中性色显示。

支持

类型与函数

同时指代类型和函数的名称标注为 conflict

支持

类型与变量

同时指代类型和变量的名称标注为 conflict

支持

同类重载集

仅指代函数的名称不会产生冲突

支持

注入类名(injected class name)

注入类名用作构造函数名称时,仍保留类 Token

代码中的名称按类标注,其隐含的构造函数引用不产生额外高亮,( 仍不带 Token。

Token 的正确性 ​

clice 明确固定的 Token 标注行为,包括 clangd 曾处理错误的情况。

构造函数与析构函数

构造函数和析构函数使用带有专用修饰符的方法 Token

析构函数名称标注为两个 Token:~ 带有方法类型以及声明/定义修饰符,其后的类名仍是对类的引用。

支持

匿名参数

未命名参数不产生 Token

未命名参数类型后的标点仍不带 Token。

支持

运算符名称

operator 关键字和调用处的标点保持原样

代码中的运算符名称由关键字和标点组成,因此不添加名称 Token:operator 保留其关键字分类,调用处的运算符符号不产生 Token。

支持

类模板的析构函数

~ 的标注方式同样适用于模板

支持

转换运算符

名称以关键字形式书写,转换调用不产生额外高亮

支持

模板参数上的伪析构函数

~ 不产生高亮,类型名称保留其 Token 类型

支持

显式默认与删除的成员

特殊成员名称保留其定义 Token

属性 ​

不支持clangd#2209

属性名称

属性名称及其表达式目前尚不获得语义 Token

宏 ​

宏定义体内的 Token 保留其词法类型;根据宏展开结果为它们添加高亮,将由未来的展开预览功能实现。

支持

宏定义与展开

宏定义和展开会获得语义 Token

支持

展开位置与实参

展开处的名称标注为宏,代码中的实参保留其语义,定义体仍按词法标注

不支持clangd#2649

对象式宏与函数式宏

对象式宏和函数式宏目前尚未使用不同的 Token 类型

其他已知不足 ​

已整理但尚无测试用例的问题:

  • auto 参数不得高亮为模板类型参数 (clangd#1390)
  • 成员指针中的嵌套名说明符(nested name specifier)应获得 Token (clangd#2235)
  • ::new 中的 new 关键字应保持高亮 (clangd#1627)
  • 当协程返回类型为模板时,co_yield / co_await 会丢失高亮(clangd#2437)
  • Token 修饰符应应用于重载运算符的操作数(clangd#2547)
  • 依赖模板名(obj.template get<int>())、通过 using 从依赖基类引入的成员, 以及重载集合种类混杂的依赖名(clangd#484、 clangd#686、 clangd#1057)

非活动代码区域 ​

未选中的预处理器分支内,每个 Token 都带有 inactive 修饰符,同时保留其词法类别,因此编辑器可以通过设置该修饰符的样式来淡化显示整个区域,同时保留原有的语法着色。非活动代码中没有分类的 Token(普通标识符和标点符号)以不带样式的 identifier 类型输出,使 Token 覆盖整个区域。clice 的 VS Code 扩展默认会淡化显示这些区域;其他编辑器则直接为该修饰符设置样式(例如 Neovim 中的 @lsp.mod.inactive)。

  • 淡化显示非活动预处理器分支(clangd#132)
  • 正确处理 #elif 链中的非活动区域边界(clangd#602)
  • 保留非活动区域内的语法高亮(clangd#1664)
  • 保持非活动区域与注释的区别(clangd#1545)
  • 淡化显示不可达代码(clangd#1828)

格式字符串高亮 ​

  • std::format / std::print 占位符高亮(clangd#1709)
  • 将无效的格式说明符高亮显示为错误

协议支持 ​

  • 全文档语义 Token(textDocument/semanticTokens/full)
  • 以 UTF-16 码元为单位、采用差分编码的 Token 位置
  • 指定范围的语义 Token(textDocument/semanticTokens/range)——仅计算可见视口内的 Token,这对大文件至关重要
  • 增量更新(textDocument/semanticTokens/full/delta)——仅发送自上次响应以来的变化