Skip to content

代码补全 ​

包含路径 ​

由 <、"、/ 字符触发。在构建 AST 之前处理(在 Preamble 层面处理,无需编译)。引号内的补全先搜索包含方文件自身所在的目录,再搜索已配置的包含目录。只有看起来像头文件的文件才会成为候选:任何目录下带头文件扩展名的文件,以及系统目录等存放此类头文件的位置中不带扩展名的文件。

支持

引号包含路径

补全会列出已配置搜索路径中的头文件和目录,并在目录末尾添加斜杠作为标记

支持

尖括号包含路径

尖括号包含路径的补全会提供相同的搜索路径候选项

支持

闭合定界符

接受头文件候选时会补上闭合定界符;光标后已有的定界符会被替换,不会重复

触发上下文

  • #include_next — 必须识别出指令是 #include_next 而非 #include,并调整搜索起点,从提供当前文件的目录之后的目录开始搜索

    cpp
    // in <bits/stl_vector.h>, provided by /usr/include/c++/14/
    #include_next <^>  // search starts AFTER /usr/include/c++/14/, skipping it
  • __has_include() / __has_embed() — 在这些结构内部触发包含路径补全

    cpp
    #if __has_include(<^>)  // suggest headers, same as #include <
  • #embed 指令补全

    cpp
    #embed <^>  // suggest embeddable resource files

候选项与排序

  • 遍历编译数据库中的编译器搜索路径

  • 文件和目录均可作为候选项;目录通过标签末尾的 / 区分

  • 过滤掉已包含的头文件

    cpp
    #include <vector>
    #include <^>  // should not suggest "vector" again
  • 降低私有或内部头文件的优先级,即普通用户不应直接包含的路径:

    • 单个 _ 前缀:较低优先级(例如 _ctype.h)
    • 双 __ 前缀:更低优先级(编译器内置的内部头文件,如 __config、__bit_reference)
    • 路径中包含 detail、internal、impl、bits 等关键词(第三方库的私有头文件,如 boost/detail/、bits/stdc++.h)
    cpp
    #include <^>        // __config, _ctype.h, bits/stdc++.h rank near bottom
    #include <boost/^>  // boost/detail/ ranks lower than boost/asio/
  • 按路径距离排序:在项目目录树中,离当前文件越近的头文件排名越靠前

插入行为

  • 目录补全不应插入末尾的 /,应由用户输入,以再次触发下一级目录的补全(目前 / 已包含在插入文本中,导致编辑器无法自动触发下一轮补全)(clangd#395)
    cpp
    #include <sys^>  // accept "sys" → inserts "sys", user types "/" → next completion fires

模块导入 ​

通过分析文本上下文检测。在构建 AST 之前处理(在 Preamble 层面处理,无需编译)。

当光标位于 import 或 export import 之后时触发。

支持

导入语句

在 import 之后补全已知模块名,并插入结尾的分号

已包含结尾分号的语句视为完整语句,不再提供模块名候选项。

  • 以空格字符触发(#460)

    两层门控避免每敲一个空格都触发:服务器把 (空格)注册为触发字符, 而由空格触发的请求只在导入上下文(import 、export import )中继续处理; 其余空格立即返回空结果。这与 TypeScript/Haxe 语言扩展采用的模式相同 (vscode#67714)。

  • 从结果中排除当前模块(模块导入自身是无效的)— FIXME

  • 在同一模块内导入分区

    cpp
    // inside module foo
    import :^  // suggest :core, :io (only foo's own partitions)

    注意:import M:part; 不是合法的 C++ 语法——分区只能在同一模块内通过简写形式 import :part; 导入。

  • 按点分层补全

    cpp
    import std.^  // suggest io, compat, etc.

    注意:模块名称中的点是一种命名约定,并不表示语言层面的层级关系,但由点触发的补全仍能改善用户体验。

  • 过滤掉其他模块中未导出的(内部)分区

  • 导入头文件单元

    cpp
    import <^>  // suggest importable headers (same candidates as #include)
    import "^"  // same, quoted form
  • 补全符号时自动插入 import 语句(类似于自动包含头文件)

    cpp
    std::vector^  // on accept, also insert "import std;" at the top

模块声明 ​

在模块声明上下文(module / export module)中进行补全。

  • import / module 关键字补全

    cpp
    imp^  // suggest "import" keyword
    mod^  // suggest "module" keyword
  • 在 module / export module 后补全模块名称

    cpp
    module my^  // suggest existing module names (useful when writing implementation units)
  • 在 : 后补全分区名称

    cpp
    export module mylib:^  // suggest existing partition names of mylib
    module mylib:^  // same, for partition implementation unit
  • module :private; 补全(私有模块片段)

    cpp
    module :^  // suggest "private"
  • 在主接口单元中补全用于重新导出的 export import :partition

    cpp
    // in primary interface unit of mylib
    export import :^  // suggest mylib's interface partitions that need re-exporting

成员访问 ​

由 .、->、:: 或 quickSuggestions 触发。通过无状态工作进程转发给 Clang 的 CodeCompleteConsumer。

支持

类的成员

字段、方法、析构函数和运算符均以普通名称补全

析构函数补全为 ~Account(绝不会是 ~struct Account),operator= 中的 = 前不加空格,转换运算符则写出其目标类型。

支持

实例化后的类模板成员

析构函数标签保留代码中写出的模板实参

支持

指针成员访问

对指针使用 -> 时,补全其所指对象的成员

支持

作用域限定的成员

在 :: 后列出静态数据成员、嵌套类型、方法和注入类名(injected class name)

限定名补全不会只保留无需实例即可访问的成员:实例字段和析构函数也会与静态成员及嵌套类型一起显示。

支持

继承的成员

对派生类对象进行补全时,列出其自身及基类的成员

支持

依赖成员类型

若变量的类型是某个依赖模板特化的成员别名,补全该别名所代表的类的成员

解析别名时会代入代码中写出的模板实参,因此 Vec<Vec<T>>::value_type 会列出 Vec<T> 的成员,而不是什么都列不出来。别名代表引用类型时(Vec<Vec<T>>::reference),列出被引用的类的成员。

支持

偏特化的成员

依赖模板特化匹配到某个偏特化时,补全的成员来自该偏特化,而不是主模板

支持

依赖别名链

别名自身指向的又是依赖成员类型时,解析会走完链条上的每一环

支持

所指依赖类型的成员

对指向依赖成员类型的指针使用 -> 时,补全其所指对象的成员

支持

指向具体特化的别名

依赖别名指向某个具体特化时,补全该特化实例化后的成员

若文件本身从未实例化该特化,补全时会将其实例化,因此成员带有具体的实参类型;若有与这些实参匹配的偏特化,成员则来自该偏特化。

支持

指定初始化器的字段

{ . 列出正在初始化的聚合体的字段,若聚合体是依赖类型,则列出与之匹配的偏特化的字段

支持

不可访问的成员

私有和受保护的成员不会出现在无法使用它们的位置

支持

析构函数标签

无论类位于哪个命名空间,析构函数都补全为 ~ 加上不带限定的类名

依赖表达式的结果

在模板中对下标或成员调用的返回值进行成员访问时,补全该返回值所属类的成员

rows[0]. 用在 Vec<Vec<T>> 上时,列出 Vec<T> 的成员:解析沿着容器的 reference 别名进行,与标准容器声明该别名的方式一致。若成员有 const 重载,由对象是否为 const 决定调用哪一个,通过 const 对象访问到的数据成员也算作 const;对返回的迭代器使用 -> 可以访问到元素。

支持

返回类型相同的重载

依赖调用的候选重载都返回同一类型时,补全该类型的成员

table[key]. 用在 operator[] 接受 const K& 或 K&& 的映射上时,列出映射值类型的成员。这些重载可以定义在类外,类模板也可以在定义之后再次声明,标准库中的映射正是如此。

支持

推导变量的成员

用依赖初始化器声明为 auto 的变量,补全其推导出的类的成员

auto& row = rows[0]; row. 列出 Vec<T> 的成员。声明符的作用与真实推导中一致:const auto& 使对象成为 const,按值的 auto 去掉初始化器的 const,auto&& 和 decltype(auto) 保留这一 const,auto* 则推导出指针所指的类型。初始化器为数据成员时,decltype(auto) 取该成员声明时的类型;加了括号时,则取该表达式的 const 引用类型。

  • ->——指针成员访问(带有 Clang 修正)

  • ::——命名空间或类作用域中的成员

  • 点转箭头:在指针后输入 . 时,触发 -> 成员补全并自动替换(clangd#1349)

    cpp
    std::unique_ptr<Foo> ptr;
    ptr.^  // suggest Foo's members, insert as ptr->bar()
  • 在成员补全结果中同时显示首个参数与对象类型匹配的自由函数(free functions)

    cpp
    std::vector<int> v;
    v.^  // also suggest std::sort(v, ...), std::find(v, ...) etc.
  • 在成员建议中包含 operator[]、operator->、operator()

  • 优先显示通过所输入运算符直接访问的成员(输入 . 时优先显示 . 可访问的成员,输入 -> 时优先显示 -> 可访问的成员)

指定初始化器(designated initializers) ​

  • 按声明顺序排列补全项(C++20 指定初始化器的要求)(clangd#965)

    cpp
    struct Cfg { int width; int height; bool fullscreen; };
    Cfg c = { .^  // suggest: .width, .height, .fullscreen (in this order)
  • 过滤掉已使用的指示符

    cpp
    Cfg c = { .width = 800, .^  // only suggest .height, .fullscreen
  • 复合字面量中的指定初始化器((struct T){ .field = })

  • 匿名结构体或联合体的成员指示符

    cpp
    struct S { union { int i; float f; }; };
    S s = { .^  // suggest .i, .f
  • “填充所有成员”代码片段

    cpp
    Cfg c = { ^  // first item: .width = ${1}, .height = ${2}, .fullscreen = ${3}

重写与类外定义 ​

支持

重写声明

在派生类中,基类的虚函数补全为完整的重写声明,包括返回类型和 override;在重写函数内部,该名称按其自身补全,而不是补全为对基类版本的调用

  • 遍历完整的继承层次结构以查找重写候选项(clangd#226、clangd#2374)

    cpp
    struct A { virtual void f(); };
    struct B : A { };
    struct C : B {
        ^  // suggest: void f() override (from A, through B)
    };
  • 类外定义补全

    cpp
    // in .cpp file
    void MyClass::^  // suggest all member functions with full signature + body snippet
  • 在定义上下文中显示所有成员(包括私有成员和受保护成员)

    cpp
    class Foo { private: void secret(); };
    void Foo::^  // must include "secret" — this is a definition, not a call
  • 在定义上下文中的 :: 后补全构造函数

  • 不显示类模板构造函数和析构函数的冗余模板参数

    cpp
    template<typename T>
    struct Vec { Vec(); ~Vec(); };
    
    template<typename T>
    Vec<T>::^  // suggest "Vec()" and "~Vec()", not "Vec<T>()" or "~Vec<T>()"

符号 ​

支持

模糊非限定查找

保留匹配度高的前缀匹配项,排除匹配度低的子序列匹配项和未限定的命名空间成员

支持

类模板去重

同一名称即使也对应构造函数和推导指引,仍只显示为一个类条目

支持

构造函数标签仅显示类名

类模板的构造函数和推导指引均补全为不带模板实参的类名,绝不使用带模板实参的形式

支持

关键字模式

关键字与其他候选项一样参与补全,插入内容为纯文本

支持

宏

对象式宏按常量补全,函数式宏按带参数签名的函数补全;实参代码片段遵循函数补全设置

支持

宏遮蔽声明

名称被重新定义为宏后,补全结果为该宏,而非被遮蔽的声明

支持

宏实参内的补全

宏实参中的成员访问与宏外的成员访问具有相同的补全行为

支持

命名空间限定查找

ns:: 列出该命名空间自身的成员

支持

枚举成员

有作用域枚举通过 Type:: 列出枚举项,无作用域枚举的枚举项则直接按名称补全

支持

局部名称遮蔽全局名称

被遮蔽的全局名称不会作为重复条目出现

支持

using 声明

通过 using 引入的名称无需限定即可补全

支持

依赖作用域限定符

:: 位于依赖成员类型之后时列出该类型的成员,位于依赖模板特化之后时列出与之匹配的偏特化的成员,位于依赖的成员枚举之后时则列出其枚举项

支持

必需的限定符

仅凭名称无法指到的枚举项会带上所需的限定符补全,并按不带限定的名称匹配

支持

局部变量隐藏函数

局部变量隐藏同名函数时,提供的候选项是该局部变量,而不是被隐藏的函数

支持

构造函数模板

与其他构造函数一样,构造函数模板补全为不带限定的类名

支持

跨命名空间的重载

作用域内来自不同命名空间的同名函数合并为一项,并计入全部重载

  • 限定名称查找(std::)

  • 实参依赖查找(ADL)候选项

  • 宏补全——候选集包含对象式宏和函数式宏

  • C++ 属性补全

    cpp
    [[^]]  // suggest: nodiscard, deprecated, maybe_unused, likely, ...
  • 跨作用域补全,包含类和结构体作用域内的符号(内部类型、静态方法)

    cpp
    struct Outer { struct Inner {}; static int count; };
    Inn^  // suggest Outer::Inner from a different scope
  • 插入限定符时考虑命名空间别名(优先使用最短的有效限定符)

    cpp
    namespace fs = std::filesystem;
    fs::ex^  // insert "fs::exists", not "std::filesystem::exists"
  • 根据语言过滤候选项(混合语言项目中的 C 文件不显示 C++ 符号)

  • 函数实参注释补全(/*param=*/ 风格的参数提示)

  • 语义分析不可用时,回退到基于标识符的补全

函数与代码片段 ​

以下所有选项均位于 [code_completion] 配置节中。

支持

签名与返回类型详情

参数列表和返回类型作为标签详情一同显示

支持

重载合并

一组重载合并为一个条目,并显示重载数量

支持

重载单独显示

关闭重载合并后,每个重载各占一个条目,并显示各自的签名

支持

参数占位符代码片段

补全调用时,为每个实参插入可用 Tab 键跳转的占位符;无参函数仍插入为纯文本

支持

代码片段遵循重载合并设置

合并重载时,即使启用了实参代码片段,也不会使用

支持

带默认实参的形参

带默认值的参数不显示在签名详情中

签名详情仅保留必需参数;末尾的 int retries = 3 会被省略。

支持

可变参数签名

末尾的 ... 显示在参数详情中

支持

调用括号

补全函数调用时会插入圆括号并把光标放在括号之间,除非名称后面已经写了实参,或者此处并不是调用

支持

模板实参占位符

类模板为每个没有默认值的模板形参插入一个占位符;所有形参都有默认值时插入一对空的尖括号

支持

语句关键字

语句关键字作为关键字补全;开启该选项后会插入整条语句,并为每个组成部分提供占位符

  • 根据上下文调整代码片段:在函数指针上下文中仅插入名称,不插入调用语法

    cpp
    void (*fp)(int) = my_fun^;  // insert "my_func", not "my_func(${1:int x})"
  • 从签名和代码片段中移除 C++23 显式对象参数

    cpp
    struct S { void f(this S& self, int x); };
    S s;
    s.f(^  // show signature "(int x)", not "(this S& self, int x)"
  • 在签名中显示参数默认值(clangd#100)

    cpp
    void open(std::string path, int mode = 0644);
    open(^  // detail shows "(string path, int mode = 0644)"
  • 将 Lambda 类型解析为实际签名

    cpp
    auto cmp = [](int a, int b) -> bool { return a < b; };
    cmp^  // show "(int a, int b) -> bool", not "<lambda>"
  • 解析转发函数的参数(clangd#447)

    cpp
    struct Widget { Widget(int w, int h); };
    auto p = std::make_unique<Widget>(^  // show "(int w, int h)"
  • 没有占位符时设置 InsertTextFormat::PlainText

模板与概念 ​

  • 感知概念约束的代码补全:根据模板参数的概念约束推断可用成员(clangd#1103)

    cpp
    template<typename T>
    concept Drawable = requires(T t) { t.draw(); t.resize(int{}, int{}); };
    
    template<Drawable T>
    void render(T& widget) {
        widget.^  // suggest draw(), resize() from Drawable concept
    }
  • 利用单次实例化信息为泛型 Lambda 提供代码补全:当泛型 Lambda 仅在一处被调用时,使用该调用位置的实参类型,为 Lambda 函数体内部提供代码补全

    cpp
    std::vector<std::string> names;
    std::ranges::sort(names, [](const auto& a, const auto& b) {
        return a.^  // a is deducible as std::string from the single call site
    });
    cpp
    auto results = names | std::views::transform([](const auto& s) {
        return s.^  // s is deducible as std::string
    });
  • 在类模板体内,不为注入类名(injected class name)生成模板参数代码片段

    cpp
    template<typename T>
    struct Vec {
        Vec^  // suggest "Vec", not "Vec<${1:T}>" — injected class name
    };

过滤与排序 ​

支持

下划线过滤

隐藏以下划线开头的内部符号,除非输入的前缀也以下划线开头

支持

弃用标记

带有 [[deprecated]] 属性的候选项会标记为弃用,普通的同类候选项则不会

支持

单词边界模糊匹配

前缀 fb 匹配 foo_bar_baz 中各单词的开头

frobnicate 与 fb 仅形成较弱的分散子序列匹配,因此被过滤掉;foo_bar_baz 则在 foo/bar 的单词边界处匹配,因此被保留。

支持

不区分大小写的前缀匹配

小写前缀可以匹配大小写混合的标识符

支持

前缀匹配优先于子序列匹配

精确匹配前缀的候选项排在分散子序列匹配的候选项之前

对于前缀 fo,format_output 属于真正的前缀匹配,得分高于仅形成子序列匹配的 fast_math_operation。

支持

单词中间的补全

在单词中间补全时同时提供两种范围:编辑器可以在单词剩余部分之前插入,也可以替换整个单词

支持

非 ASCII 前缀

由非 ASCII 标识符字符组成的前缀会被替换,而不是在其前面插入

  • 模糊匹配评分考虑单词边界(camelCase、snake_case)

  • 过滤掉错误恢复上下文的结果(CCC_Recovery)

  • 结果数量上限(CodeCompletionOptions.limit)

  • 综合使用频率和最近使用时间加权/提高最近使用项的排名

  • 将数字与字母之间的边界视为单词分界(clangd#1236)

    cpp
    i32^  // should match int32_t (digit-letter boundary: "32" → "t")
  • 按作用域划分相关性层级:局部符号 > 成员 > 命名空间作用域符号 > 跨作用域符号

  • 根据上下文中的类型提升排序优先级(预期类型为枚举时,建议匹配的枚举成员)(clangd#462)

    cpp
    enum Color { Red, Green, Blue };
    void paint(Color c);
    paint(^  // boost Red, Green, Blue to top
  • 过滤 switch 语句中已使用的枚举值

    cpp
    switch (color) {
        case Red: break;
        case ^  // suggest Green, Blue only — Red already used
  • 在 C++ 模式下将 nullptr 排在 NULL 之前

  • 根据命名信号提升排序优先级

    cpp
    auto foo = get^;  // boost getFoo() over getBar()
  • 将引用次数和文件邻近程度作为排序信号

  • 基于机器学习的排序模型

自动插入包含指令 ​

尚未实现。补全符号时不会插入 #include 指令。

  • 接受补全项时,为未解析的符号插入 #include

    cpp
    std::vec^  // on accept "vector", also insert #include <vector> at top of file
  • 检查包含关系图中的传递关系,避免重复包含

    cpp
    // <algorithm> already includes <iterator> transitively
    std::back_inserter^  // do NOT insert #include <iterator> again
  • 感知上下文:对于前置声明或仅通过指针/引用使用的情况,不插入包含指令(clangd#639)

    cpp
    class Foo;
    Foo*^  // no include needed — forward declaration suffices for pointer
  • 在 C 文件中插入 C 头文件,在 C++ 文件中插入 C++ 头文件

    c
    // in a .c file
    size_^  // insert #include <stddef.h>, not #include <cstddef>
  • 可配置的行为:always / iwyu-only / never

  • 优先使用相对于项目的路径,而非绝对路径

  • 遵循 IWYU 编译指示和头文件映射

  • 为 C++20 模块符号自动插入 import

文档 ​

尚未实现。补全项不包含文档。

  • 从声明和定义中提取文档注释

    cpp
    /// @brief Opens a file at the given path.
    /// @param path The file system path.
    void open(std::string path);
    
    op^  // completion popup shows the @brief doc
  • 无论定义位于何处(头文件、源文件或索引),都能提供文档

  • 将模板模式(template pattern)的文档传递给实例化结果

  • 集成标准库文档

  • 将宏定义用作文档(clangd#1485)

触发字符 ​

已注册:. < > : " / 和空格。

字符上下文行为
.成员访问语义补全
>通过 -> 触发指针成员补全;其他位置的 > 不触发
:通过 :: 触发作用域补全
<#include <包含路径补全
"#include "包含路径补全
/路径分隔符继续补全包含路径
import 之后模块名补全(受扩展限制)

协议 ​

  • completionItem/resolve,用于延迟加载文档和详细信息
  • CompletionList.isIncomplete 标志,用于增量筛选
  • commitCharacters,用于在按下特定按键时自动接受补全项
  • filterText / sortText,用于在客户端重新筛选