C/C++ BlitzSource Insight–inspired C/C++ code navigation for VS Code, built for embedded codebases where the Microsoft C/C++ extension's IntelliSense struggles — heavy macros, non-standard ARM-family compilers, code that doesn't compile in a standard toolchain.
Open a C/C++ folder and indexing starts on its own — there is no project file, no
compile database, and nothing to configure. Features
Go to Definition —
|
| State | Status bar |
|---|---|
| Idle | C/C++ Blitz: 3,262,118 symbols — click for Search / Rebuild / Show log |
| Indexing | C/C++ Blitz: 42% · parsing 12,043/32,114 files · 1m 12s — click to Stop indexing |
| Not yet indexed | C/C++ Blitz: no index |
Every step of a run reports in the same shape — <percent>% · <what is happening> <counts> · <elapsed> — on both the notification and the status bar, and the percentage is derived
from the actual size of the work and re-calibrated on every run, so after one run the
estimate fits your machine and your codebase. It never runs backwards.
Stop indexing keeps the partial index and pauses automatic indexing until you say so — an unpacking archive or a long checkout can't restart a scan behind your back. A Resume indexing entry then appears in the menu; it finishes the job incrementally and re-enables automatic indexing.
Rebuild index clears the index and parses every file again, so it starts from the same state as a fresh install. It asks first: until it finishes, symbol search and Go to Definition find nothing. You rarely need it — new, changed and deleted files are picked up automatically as you work, on checkout, and at startup.
When one file behaves oddly — Show Index Info
Navigation is only as good as the parse behind it, and when a file defeats tree-sitter the symptoms are indirect: a local variable reads not found, or resolves to a same-named field from a file you have never opened. C/C++ Blitz: Show Index Info for This File (Command Palette) explains why, for the file you are in:
- which parser produced its rows — tree-sitter or the grep fallback — and, if it fell back,
the rule that fired with its measured value (
errorRatio 0.412 > 0.25); - what the index holds at your cursor, so a stale position is never mistaken for a missing symbol;
- how the workspace as a whole parsed, which separates one awkward file from a shared cause;
- and a plain-language verdict for all of it.
It re-parses the file as it runs (nothing is written) and compares that against the stored rows, so an index written before an update says so. The report goes to the output channel with a Copy report button — paste it into an issue and the whole picture travels with it.
Commands
All commands are prefixed C/C++ Blitz in the Command Palette.
| Command | How you normally reach it |
|---|---|
| Go to Definition | F12 / Ctrl+Click / Peek (via the provider); the command itself is Palette-only and never merges with other providers |
| Search Symbols | F10 |
| Find All References | Native Shift+F12 / right-click → Find All References resolve through our index; this command opens the References view directly |
| Look Up Symbol in Code Insight | Code Insight title bar 🔍 — points the view at a symbol without moving the editor |
| Refresh Code Insight | Code Insight title bar ↻ |
| Pin / Unpin Code Insight | Code Insight title bar toggle |
| Show Code Insight as Text | Code Insight title bar |
| Open Code Insight Result and Focus It | Alt+Enter on a Code Insight row (view-only, not in the Palette) |
| Open the Real File | Editor title bar, shown only on a preview-pane tab |
| Show Index Info for This File | Palette-only — why the open file indexed the way it did, ready to paste into an issue |
| Rebuild Index from Scratch | Status-bar menu → Rebuild index |
| Stop Indexing | Status-bar menu → Stop indexing (only while a run is active) |
Settings
Indexing scope
| Setting | Notes | Default |
|---|---|---|
cBlitz.fileExtensions |
File extensions to index | .c .h .cpp .cc .cxx .hpp .hh .hxx .hl |
cBlitz.exclude |
Glob patterns to exclude, applied gitignore-style | **/.git/**, **/out/**, **/_out/** |
cBlitz.include |
Exceptions that re-admit paths an exclude removed — not a whitelist (everything is indexed by default) | [] |
cBlitz.respectGitignore |
Also exclude what the workspace's .gitignore files ignore (root + nested, git's rules). exclude/include are applied after and win |
true |
Indexing behavior
| Setting | Notes | Default |
|---|---|---|
cBlitz.indexing.workerCount |
Parallel worker threads for bulk indexing | 8 |
cBlitz.indexing.deferQueriesMs |
How long F12/F10/Code Insight wait for an in-flight reindex before querying (0 = off) | 1500 |
cBlitz.indexing.verifyContentHash |
When a file's mtime changes, re-hash and skip re-parsing if byte-identical (e.g. after git checkout), at the cost of reading each changed file |
false |
cBlitz.db.location |
Database directory; an absolute path is allowed (e.g. a fast or large disk). On Remote-SSH this resolves on the remote host | workspace storage |
Parsing
| Setting | Notes | Default |
|---|---|---|
cBlitz.parse.maxFileSizeKB |
Larger files fall back to grep | 2048 |
cBlitz.parse.errorRatioThreshold |
ERROR-byte ratio threshold for grep fallback | 0.25 |
cBlitz.parse.timeoutMs |
Per-file parse timeout before grep fallback | 5000 |
Code Insight & search
| Setting | Notes | Default |
|---|---|---|
cBlitz.codeInsight.previewPane |
off / bottom / right — route every result click into one dedicated read-only pane. Overrides Split Direction |
off |
cBlitz.codeInsight.splitDirection |
vertical / horizontal / followLayout — how a click creates an editor group. Ignored while Preview Pane is on |
vertical |
cBlitz.codeInsight.focusAfterOpen |
Where focus goes after opening a result: view (stay on the list), editor (back to the editor you came from), target (into the opened code). Alt+Enter always goes to the code |
view |
cBlitz.codeInsight.debounceMs |
Cursor-follow delay | 200 |
cBlitz.codeInsight.hideEmptyCategories |
Hide categories with no entries for the current symbol | true |
cBlitz.codeInsight.show.* |
Per-category visibility (symbol/type/definition/declaration/calledBy/calls/references) | true |
cBlitz.fuzzy.maxResults |
F10 result cap | 200 |
External macros
| Setting | Notes | Default |
|---|---|---|
cBlitz.externalMacros.enabled |
Synthesize external macro symbols for #if/#ifdef names not #defined in-tree. Toggling forces a rescan |
true |
cBlitz.externalMacros.predefined |
Names your build injects ("NAME" or "NAME=value", -D style) — annotates Origin only; does not create symbols |
[] |
cBlitz.externalMacros.file |
Path (or list of paths) to a JSON file mapping injected macro names to values — annotates Origin; re-read live (no re-index needed) | "" |
How it works
- A pool of background worker threads (default 8,
cBlitz.indexing.workerCount) parses files with tree-sitter in parallel and writes to an on-disk SQLite database via Node's built-innode:sqlite(WAL mode — no native module to build or rebuild). The extension host only reads. References for a ~300 MB codebase can reach tens of millions of rows — far too much for memory — so everything lives in SQLite and is fetched with indexed point queries. There is no parallel in-memory copy: even F10 queries SQLite directly, on its own worker, ranking a bounded candidate set per keystroke. - The index is the cache. On restart only changed or new files are re-parsed (mtime diff) and deleted files are dropped.
- Reconciling after a
git checkoutcosts what the change costs, not what the repo costs. Scanning a large workspace to discover what moved is O(total files) — many seconds on a 100k-file tree, at every launch and every branch switch. Since the trigger is almost always a git operation, and git already computed the exact delta, automatic reconciliation asks git (diff --name-status∪status --porcelain) and stats only those paths. Non-git folders, git errors, or an unknown prior revision fall back to the full scan, which remains the correctness backstop and always owns Resume / Rebuild / filter changes. - Headers pick their grammar by content, not just extension. A
.hdefaults to C but is parsed as C++ when the text contains a decisive C++ marker (namespace X {,template <,enum class, an access specifier, …) — so Chromium-style trees that spell C++ headers.hindex correctly. A markerless plain C header keeps the C grammar. - Fallback: if tree-sitter fails or produces low-confidence results (parse error,
unsupported file, timeout, very large file, or too many ERROR nodes), the file is indexed
with a regex (grep) scanner instead — symbols are still found, refs and calls on a
best-effort basis. Two common shapes that used to force this fallback are handled before
parsing: a whole-file include guard is neutralized (one misparse deep in a guarded header
could otherwise collapse the entire file to grep), as are C++ export macros in a class
head (
class BASE_EXPORT Foo : public Bar {). - Macros in declaration position are resolved from facts, not guesses. A macro where a declaration is expected is genuinely ambiguous without a preprocessor, so the workspace is swept once up front to learn which names are function-like macros and which are typedefs; those facts then recover the swallowed declaration, and only when doing so demonstrably recovers a declaration without losing any. That sweep is the "reading macro definitions" phase, roughly 10% of parse time, and it survives restarts by being reconstructed from the index itself.
- External macros are synthesized (
cBlitz.externalMacros.enabled, default on): a table-wide pass inserts one definition-less symbol per#if/#ifdefname with no definition of any kind in the tree, anchored at its first use. It is maintained incrementally — an external macro appears or disappears live as edits add or remove its uses — and covers grep-parsed heavy-macro files too. - Comments are never indexed.
#if 0 … #endifblocks are indexed (a known, intentional limitation — there is no preprocessor). - Bulk indexing is cancelable and never overlaps itself. Bulk runs are serialized
(latest wins). Files added, changed, or removed reindex in the background behind a single
coalesced notification, and navigation queries briefly wait for an in-flight reindex to
settle (bounded by
cBlitz.indexing.deferQueriesMs) so they reflect your latest edits.
Requirements
- VS Code 1.106+. The hard technical floor is 1.99 (Electron 35+ / Node 22.13+ for
node:sqlite); the marketplace minimum is pinned higher on purpose, so a future update that adopts a newer VS Code API won't break old installs. - No native modules — the
.vsixis platform-independent and works on Remote-SSH with nothing to rebuild.
Limitations
- No compiler, preprocessor, macro expansion, or real type system — symbol resolution
is best-effort by design. Parameters and locals resolve within their function, and member
access (
a->x) is type-narrowed by walking the declared-type chain, but when a type cannot be resolved it falls back to every struct field namedx. When a set stays ambiguous, Code Insight lists every candidate rather than picking one. - C++ is supported structurally but not semantically. Qualified names
(
Widget::kWidth), member functions, and implicit-thismember lookup resolve; overload resolution is name + arity only, and base-class member lookup (inheritance) is not modeled — an inherited member use stays best-effort. - A macro in a declaration position is handled well, not perfectly. A decorator-stacked function (several macro prefixes) or a multi-line macro invocation may still not be recovered, and after a restart with no reindexing a live single-file edit sees only that file's own macros until the next full scan. In both cases the declaration is simply absent, never wrong, and the next automatic scan resolves it.
- A very large source file is indexed by name only. Past a size limit the structural parse is skipped, so the file is still searchable and its definitions are still found — but without scope, locals, or the finer resolution those give; past a much larger limit it also stops contributing its macros to the rest of the workspace. Generated blobs and amalgamated sources are the usual cases. Every indexing run lists such files in the log with their sizes, so nothing about it has to be guessed at.
- Unsaved-buffer edits re-index on a short debounce; saving always triggers re-indexing.
Install
C/C++ Blitz is published to both extension registries — both get the same
platform-independent .vsix:
- VS Code Marketplace — https://marketplace.visualstudio.com/items?itemName=tsj.sintra
- Open VSX (VSCodium, Cursor, Gitpod, Eclipse Theia, …) — https://open-vsx.org/extension/tsj/sintra
Support
If C/C++ Blitz put a smile on your face, consider buying me a coffee — Buy Me a Coffee (global) or Fairy (KRW, for Korean supporters).
Thanks
C/C++ Blitz is shaped by the people who actually navigate embedded code all day and told us what was in the way. Thanks for the feedback to:
- Dong Hoon Lee
- Austin Lee
- Eric Jung
- Donghyun Min
- Morris Ju
License
MIT © tsj — see the LICENSE file in the extension package.