Code Completion
Include Path Completion
Triggered by <, ", / characters. Handled before AST (preamble-level, no compilation needed). Quoted completion searches the configured include directories, not the includer's own directory (unless it is on the include path).
Quoted include paths — headers and directories from the configured search path, directories marked by a trailing slash
Answered by the server before any compilation, so only the server path exists for this fixture.
Example
cpp#include "snap"Angled include paths — the same search-path candidates in the angled form
Example
cpp#include <snap>
Trigger contexts
#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 statements — known module names complete after
import, with the closing semicolon insertedAnswered by the server from its module map, so only the server path exists for this fixture; the sibling module interface is opened first so the module is known. The statement stays unterminated — a
;on the line means the import is already complete and nothing is offered.Example
cppimport ma
Trigger 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
Members of a class — fields, methods, the destructor and operators complete with plain names
The destructor completes as
~Account(never~struct Account),operator=keeps no space before=, and a conversion operator spells its target type.Example
cpp// The member access expression is left dangling at the point. struct Wallet { int cents; }; struct Account { int balance; int bazzzz(int a, int b); operator Wallet(); }; void bar() { Account acc; acc. }Members of an instantiated class template — the destructor label keeps the written template arguments
Example
cpp// The member access expression is left dangling at the point. template <typename T> struct Box { T value; }; void bar() { Box<int> b; b. }Pointer member access —
->on a pointer completes the pointee's membersExample
cpp// The member access expression is left dangling at the point. struct Node { int value; Node* next; int compute(int a); }; void bar() { Node* p; p-> }Scope-qualified members — after
::static data, nested types, methods and the injected class name all listQualified completion is not filtered to the statically-reachable subset: instance fields and the destructor show up alongside the static members and nested types.
Example
cpp// The qualified-id is left dangling at the point. struct Config { static int shared_count; static int make(int seed); struct Nested { int a; }; int instance_field; }; void bar() { int v = Config::; }Inherited members — a derived object completes its own members and those of its base
Example
cpp// The member access expression is left dangling at the point. struct Base { int base_field; int base_method(); }; struct Derived : Base { int derived_field; }; void bar() { Derived d; d. }
->— 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 lookup with fuzzy prefix matching — strong prefix matches survive, weak subsequence matches and unqualified namespace members do not
Example
cpp// The completion expression dangles as an unfinished statement. namespace A { void fooooo(); } struct X { void operator()() {} }; void bar() { X functor; auto folded = [](int x) { }; fo; }Class template deduplication — a name that is also constructors and a deduction guide stays a single class entry
Example
cpp// The completion prefix dangles as an unfinished statement. template <typename T> struct Foo { Foo() {} Foo(T x) {} Foo(T x, T y) {} }; template <typename T> Foo(T) -> Foo<T>; void bar() { Fo }Constructor labels stay plain — class template constructors and deduction guides complete as the bare class name, never a templated spelling
Example
cpp// The completion prefix dangles as an unfinished statement. template <typename T, typename U> struct Bazzz { Bazzz() {} Bazzz(T x) {} Bazzz(T x, U y) {} }; template <typename T> Bazzz(T) -> Bazzz<T, int>; void bar() { Ba }Keyword patterns — keywords complete like any candidate, with plain insert text
Example
cpp// The completion prefix cuts the initializer mid-expression. int x = truNamespace-qualified lookup —
ns::lists the namespace's own membersExample
cpp// The qualified-id is left dangling at the point. namespace geometry { int area_of(int r); struct Point { int x; }; int origin; } // namespace geometry void bar() { int v = geometry::; }Enum members — a scoped enum lists through
Type::, an unscoped enumerator completes by bare nameExample
cpp// Both completion prefixes dangle; the statements stay // semicolon-terminated so the second marker is not dragged into recovery. enum class Color { Red, Green, Blue }; enum Fruit { Apple, Banana }; void bar() { Color c = Color::; int f = App; }Local shadowing a global — the shadowed global does not appear as a duplicate entry
Example
cpp// The completion prefix dangles as an unfinished statement. int counter = 0; void bar() { int counter = 1; int v = coun; }Using-declaration — a name pulled in with
usingcompletes unqualifiedExample
cpp// The completion prefix dangles as an unfinished statement. namespace lib { int helper_fn(int x); } using lib::helper_fn; void bar() { int v = help; }
Qualified name lookup (
std::)Argument-dependent lookup (ADL) candidates
Macro completion — macros are currently excluded from the candidate set
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
All options below live in the [code_completion] configuration section.
Signature and return type details — the parameter list and return type ride along as label details
Example
cpp// The completion prefix cuts the initializer mid-expression. double foooo(int x, float y); int x = foOverload bundling — an overload set collapses into one entry with an overload count
Example
cpp// The completion prefix cuts the initializer mid-expression. int foooo(int x); int foooo(int x, int y); double foooo(double d); int x = foooUnbundled overloads — with bundling off, every overload is its own entry with its own signature
Example
cpp// The completion prefix cuts the initializer mid-expression. int foooo(int x); int foooo(int x, int y); double foooo(double d); int x = foooParameter placeholder snippets — calls insert tab-stop placeholders per argument; a no-argument function stays plain text
Example
cpp// The completion prefixes dangle as unfinished statements. int foooo(int x, float y); void nothing_to_fill(); struct Foo { int bazzzz(int a, int b); }; void bar() { Foo f; fo; no; f.ba; }Snippets defer to bundling — while overloads are bundled, argument snippets stay off even when enabled
Example
cpp// The completion prefix cuts the initializer mid-expression. int foooo(int x); int foooo(int x, int y); int z = foDefault-argument parameters — a parameter with a default value drops out of the signature detail
The signature detail keeps only the required parameters; the trailing
int retries = 3is elided.Example
cpp// The completion prefix cuts the initializer mid-expression. int configure(int timeout, int retries = 3); int x = confiVariadic signature — a trailing
...shows in the parameter detailExample
cpp// The completion prefix cuts the initializer mid-expression. int printf_like(const char* fmt, ...); int x = printf
Template 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
Underscore filtering — underscore-prefixed internal symbols hide unless the typed prefix itself starts with one
Example
cpp// The completion prefixes are undeclared identifiers. The // statements stay semicolon-terminated: an unterminated one puts the // NEXT marker into a recovery context, which completion drops entirely. int _private_thing; int public_thing; int x = pu; int y = _p;Deprecated tagging — a [[deprecated]] candidate carries the Deprecated tag, its plain sibling does not
Example
cpp// The completion prefix cuts the initializer mid-expression. [[deprecated]] int old_thing(int x); int new_thing(int x); int z = thingWord-boundary fuzzy match — prefix
fbmatches the word starts offoo_bar_bazfrobnicateis only a weak scattered subsequence offband is dropped;foo_bar_bazmatches on thefoo/barword boundaries and survives.Example
cpp// The completion prefix dangles as an unfinished statement. int foo_bar_baz; int frobnicate; void bar() { int v = fb; }Case-insensitive prefix — a lowercase prefix matches a mixed-case identifier
Example
cpp// The completion prefix dangles as an unfinished statement. int MyLongName; void bar() { int v = mylong; }Prefix outranks subsequence — an exact-prefix candidate sorts above a scattered subsequence match
For prefix
fo,format_outputis a true prefix and outscoresfast_math_operation, which only matches as a subsequence.Example
cpp// The completion prefix dangles as an unfinished statement. int format_output; int fast_math_operation; void bar() { int v = fo; }
Fuzzy matching with word-boundary-aware scoring (camelCase, snake_case)
Filter out recovery context results (
CCC_Recovery)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 |
|---|---|---|
| 2026-04-06 | #include path completion and module import completion (flat prefix) | #394 |
| 2024-12-01 | Initial semantic completion | — |