Code Completion
Include Path Completion
Triggered by <, ", / characters. Handled before AST (preamble-level, no compilation needed).
Trigger contexts
#include <— system/angled include paths#include "— quoted include paths from configured search directories (does not search the includer's own directory unless it is on the include path)#include_next— must detect that the directive is#include_next, not#include, and adjust search to start from the directory after the one that provided the current filecpp// 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()— trigger include path completion inside these constructscpp#if __has_include(<^>) // suggest headers, same as #include <#embeddirective completioncpp#embed <^> // suggest embeddable resource files
Candidates and ranking
Traverse compiler search paths from compilation database
Both files and directories are candidates; directories are distinguished by a trailing
/in the labelFilter out already-included headers
cpp#include <vector> #include <^> // should not suggest "vector" againDeprioritize private/internal headers — paths that normal users should not include directly:
- Single
_prefix: lower priority (e.g._ctype.h) - Double
__prefix: even lower priority (compiler built-in internals like__config,__bit_reference) - Keywords like
detail,internal,impl,bitsin the path (third-party library private headers likeboost/detail/,bits/stdc++.h)
cpp#include <^> // __config, _ctype.h, bits/stdc++.h rank near bottom #include <boost/^> // boost/detail/ ranks lower than boost/asio/- Single
Path-distance-based ranking: headers closer to the current file in the project tree rank higher
Insertion behavior
Directory completion should NOT insert the trailing
/— let the user type it to re-trigger completion for the next level (currently the/is baked into the inserted text, which prevents the editor from auto-triggering the next completion round) (clangd#395)cpp#include <sys^> // accept "sys" → inserts "sys", user types "/" → next completion fires
Module Completion
Detected via text context analysis. Handled before AST (preamble-level, no compilation needed).
Import
Triggered when cursor is after import or export import.
import/export import— suggest all known module names from workspacePrefix filtering (typing narrows results)
Auto-append
;viainsert_textCompletionItemKind::ModuleiconTrigger on space character (#460)
Requires two-layer gating to avoid firing on every space keystroke:
- Server-side: register
(space) as a trigger character so the client sends completion requests on space. - Extension-side middleware: intercept space-triggered requests and only forward them when the current line matches
importorexport import(cheap string check, zero IPC overhead for non-import spaces). All other spaces return empty immediately.
This follows the same pattern used by TypeScript/Haxe language extensions (vscode#67714).
- Server-side: register
Exclude self-module from results (self-import is invalid) — FIXME
Partition import within the same module
cpp// inside module foo import :^ // suggest :core, :io (only foo's own partitions)Note:
import M:part;is not valid C++ — partitions can only be imported via the short formimport :part;from within the same module.Hierarchical dot-completion
cppimport std.^ // suggest io, compat, etc.Note: dots in module names are a naming convention, not language-level hierarchy, but dot-triggered completion is still valuable UX.
Filter out non-exported (internal) partitions of other modules
Header unit import
cppimport <^> // suggest importable headers (same candidates as #include) import "^" // same, quoted formAuto-insert
importstatement on symbol completion (like auto-include for headers)cppstd::vector^ // on accept, also insert "import std;" at the top
Declaration
Completion within module declaration contexts (module / export module).
import/modulekeyword completioncppimp^ // suggest "import" keyword mod^ // suggest "module" keywordModule name completion after
module/export modulecppmodule my^ // suggest existing module names (useful when writing implementation units)Partition name completion after
:cppexport module mylib:^ // suggest existing partition names of mylib module mylib:^ // same, for partition implementation unitmodule :private;completion (private module fragment)cppmodule :^ // suggest "private"export import :partitionre-export completion in primary interface unitcpp// in primary interface unit of mylib export import :^ // suggest mylib's interface partitions that need re-exporting
Semantic Code Completion
Triggered by ., ->, ::, or quickSuggestions. Forwarded to Clang CodeCompleteConsumer via stateless worker.
Member Access
.— struct/class members->— pointer member access (with Clang fixup)::— namespace/class scope membersDot-to-arrow: typing
.on a pointer triggers->member completion with automatic replacement (clangd#1349)cppstd::unique_ptr<Foo> ptr; ptr.^ // suggest Foo's members, insert as ptr->bar()Show free functions whose first parameter matches the object type alongside member results
cppstd::vector<int> v; v.^ // also suggest std::sort(v, ...), std::find(v, ...) etc.operator[],operator->,operator()in member suggestionsPrioritize direct members for the operator typed (
.members for.,->members for->)
Designated Initializers
Sort completions in declaration order (required by C++20 designated initializers) (clangd#965)
cppstruct Cfg { int width; int height; bool fullscreen; }; Cfg c = { .^ // suggest: .width, .height, .fullscreen (in this order)Filter out already-used designators
cppCfg c = { .width = 800, .^ // only suggest .height, .fullscreenCompound literal designated initializers (
(struct T){ .field = })Anonymous struct/union member designators
cppstruct S { union { int i; float f; }; }; S s = { .^ // suggest .i, .f"Fill all members" snippet
cppCfg c = { ^ // first item: .width = ${1}, .height = ${2}, .fullscreen = ${3}
Override & Out-of-line Definition
Virtual function override completion with full signature and
overridekeywordcppstruct Base { virtual void draw(int x, int y) const; }; struct Derived : Base { ^ // suggest: void draw(int x, int y) const override };Full inheritance hierarchy traversal for override candidates (clangd#226, clangd#2374)
cppstruct A { virtual void f(); }; struct B : A { }; struct C : B { ^ // suggest: void f() override (from A, through B) };Out-of-line definition completion
cpp// in .cpp file void MyClass::^ // suggest all member functions with full signature + body snippetShow all members (including private/protected) in definition contexts
cppclass Foo { private: void secret(); }; void Foo::^ // must include "secret" — this is a definition, not a callConstructors after
::in definition contextsSuppress redundant template parameters for constructors/destructors in class templates
cpptemplate<typename T> struct Vec { Vec(); ~Vec(); }; template<typename T> Vec<T>::^ // suggest "Vec()" and "~Vec()", not "Vec<T>()" or "~Vec<T>()"
Symbols
Unqualified name lookup (local vars, functions, types)
Qualified name lookup (
std::)Argument-dependent lookup (ADL) candidates
Keyword completion (if, for, while, etc.)
Macro completion
Snippet patterns with placeholders (function bodies, control flow)
C++ attribute completion
cpp[[^]] // suggest: nodiscard, deprecated, maybe_unused, likely, ...Cross-scope completion including class/struct-scoped symbols (inner types, static methods)
cppstruct Outer { struct Inner {}; static int count; }; Inn^ // suggest Outer::Inner from a different scopeRespect namespace aliases in inserted qualifiers (prefer shortest valid qualifier)
cppnamespace fs = std::filesystem; fs::ex^ // insert "fs::exists", not "std::filesystem::exists"Language-aware filtering (no C++ symbols in C files in mixed projects)
Function-argument comment completion (
/*param=*/style parameter hints)Identifier-based fallback completion when semantic analysis is unavailable
Functions & Snippets
Function overload grouping (
bundle_overloads, default: on)Parameter placeholder snippets (
enable_function_arguments_snippet, default: off — not yet wired on the LSP path)Signature in
label_details.detail, return type inlabel_details.descriptionTemplate argument placeholders (
enable_template_arguments_snippet)Auto-insert parentheses (
insert_paren_in_function_call)Look-ahead for existing parentheses/brackets to avoid duplicate insertion
cppfoo^(10, 20); // should NOT insert another pair of parens → foo(10, 20)Context-sensitive snippet: insert name only (no call syntax) in function pointer contexts
cppvoid (*fp)(int) = my_fun^; // insert "my_func", not "my_func(${1:int x})"Strip C++23 explicit object parameter from signatures and snippets
cppstruct S { void f(this S& self, int x); }; S s; s.f(^ // show signature "(int x)", not "(this S& self, int x)"Show default parameter values in signatures (clangd#100)
cppvoid open(std::string path, int mode = 0644); open(^ // detail shows "(string path, int mode = 0644)"Resolve lambda types to actual signatures
cppauto cmp = [](int a, int b) -> bool { return a < b; }; cmp^ // show "(int a, int b) -> bool", not "<lambda>"Resolve forwarding function parameters (clangd#447)
cppstruct Widget { Widget(int w, int h); }; auto p = std::make_unique<Widget>(^ // show "(int w, int h)"InsertReplaceEditsupport (provide both insert and replace ranges for mid-word completion)cpprefact^orize // insert: "refactoring^orize", replace: "refactoring"Set
InsertTextFormat::PlainTextwhen no placeholders are present
Templates & Concepts
Concept-aware completion: infer available members from concept constraints on template parameters (clangd#1103)
cpptemplate<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 }Dependent type member completion in uninstantiated templates
cpptemplate<typename T> void process(std::vector<std::vector<T>>& matrix) { matrix[0].^ // resolve operator[] → vector<T>&, suggest push_back(), size() etc. }Use single-instantiation information for generic lambda completion — when a generic lambda is only called from one site, use that site's argument types to provide completion inside the lambda body
cppstd::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 });cppauto results = names | std::views::transform([](const auto& s) { return s.^ // s is deducible as std::string });Suppress template parameter snippet for injected class name inside class template body
cpptemplate<typename T> struct Vec { Vec^ // suggest "Vec", not "Vec<${1:T}>" — injected class name };
Macros
Macro name completion from AST
Fuzzy matching for macros (same matcher as other symbols)
Correct
CompletionItemKind:Functionfor function-like,Constantfor object-like (currentlyUnitfor all) (clangd#2002)Show macro definition/expansion as documentation (clangd#1485)
cpp#define MAX_BUF 4096 MAX^ // completion detail shows: #define MAX_BUF 4096Parameter placeholders for function-like macros (respect snippet settings)
cpp#define CHECK(cond, msg) ... CHECK^ // insert: CHECK(${1:cond}, ${2:msg})Completion inside macro arguments with fallback to enclosing context
cpp#define WRAP(...) __VA_ARGS__ WRAP(some_obj.^) // should still offer some_obj's members
Filtering & Ranking
Fuzzy matching with word-boundary-aware scoring (camelCase, snake_case)
Fuzzy filtering and prefix matching
Filter out recovery context results (
CCC_Recovery)Filter
_-prefixed internal symbols (unless user typed_)Deprecated symbol tagging
Result limit (
CodeCompletionOptions.limit)Frecency/recently-used boosting
Treat digit-letter boundaries as word breaks (clangd#1236)
cppi32^ // should match int32_t (digit-letter boundary: "32" → "t")Scope-aware relevance tiers: locals > members > namespace-scope > cross-scope
Context-based type boosting (suggest matching enum members when expected type is an enum) (clangd#462)
cppenum Color { Red, Green, Blue }; void paint(Color c); paint(^ // boost Red, Green, Blue to topFilter already-used enum values in switch statements
cppswitch (color) { case Red: break; case ^ // suggest Green, Blue only — Red already usedRank
nullptraboveNULLin C++ modeNaming signal boosting
cppauto foo = get^; // boost getFoo() over getBar()Reference-count and file-proximity ranking signals
Machine-learned ranking model
Auto-Include Insertion
Not yet implemented. Completing a symbol does not insert #include directives.
Insert
#includefor unresolved symbols on completion acceptcppstd::vec^ // on accept "vector", also insert #include <vector> at top of fileCheck transitive include graph to avoid duplicate includes
cpp// <algorithm> already includes <iterator> transitively std::back_inserter^ // do NOT insert #include <iterator> againContext-aware: no include insertion for forward declarations or pointer/reference-only usage (clangd#639)
cppclass Foo; Foo*^ // no include needed — forward declaration suffices for pointerInsert C headers in C files, C++ headers in C++ files
c// in a .c file size_^ // insert #include <stddef.h>, not #include <cstddef>Configurable behavior:
always/iwyu-only/neverPrefer project-relative paths over absolute paths
Respect IWYU pragmas and header mappings
Auto-insert
importfor C++20 module symbols
Documentation in Completions
Not yet implemented. Completion items do not include documentation.
Extract doc comments from declarations and definitions
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 docAvailable regardless of where the definition lives (header, source, index)
Propagate template pattern documentation to instantiations
Standard library documentation integration
Trigger Characters
Registered: . < > : " / *. Space () is planned but not yet merged (#460).
| Character | Context | Behavior |
|---|---|---|
. | Member access | Semantic completion |
-> | Pointer member | [ ] Not yet working — dot-to-arrow fix-its not propagated |
:: | Via : trigger | Scope completion |
< | #include < | Include path completion |
> | Template close | Semantic completion |
" | #include " | Include path completion |
/ | Path separator | Include path continuation |
* | Pointer deref | Semantic completion |
| After import | Module name completion (extension-gated) — pending #460 |
LSP Protocol Features
-
completionItem/resolvefor lazy-loading documentation and details -
CompletionList.isIncompleteflag for incremental filtering -
commitCharactersfor auto-accepting completions on specific keystrokes -
filterText/sortTextfor client-side re-filtering
Changelog
| Date | Change | PR |
|---|---|---|
| — | Initial include/semantic completion | — |
| — | Module import completion (flat prefix) | — |