Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>Search Enhancement — ctags Symbol SearchNew to Visual Studio Code? Get it now.
Search Enhancement — ctags Symbol Search

Search Enhancement — ctags Symbol Search

jeffreyhc

|
82 installs
| (0) | Free
Find any function, variable, or macro in million-line C/C++ codebases as fast as you can type. Multi-keyword AND matching backed by Universal Ctags. Built for FreeRTOS, kernel, embedded, and legacy projects where IntelliSense is slow or unavailable.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

English | 繁體中文

search-enhancement banner

Search Enhancement — ctags Symbol Search

Marketplace Installs CI License

Find any function, variable, or macro in million-line C/C++ codebases as fast as you can type. Multi-keyword search backed by a Universal Ctags index — no language server, no build system, no IntelliSense database. Tuned for FreeRTOS, kernel, embedded, and legacy projects; works with any language ctags can index.

Type "task create", click a result, then search the same words in reverse order — same four hits

Type task create, click a result to jump to it, then search create task — same four hits, order doesn't matter.

The numbers behind every release — parse time, search latency, memory — are in the CHANGELOG.

Why this over Ctrl+T (Go to Symbol in Workspace)?

VS Code's built-in symbol search asks the active language server. For C/C++ that means clangd or cpptools needs a complete project setup — compile_commands.json, include paths, an IntelliSense database. On many real-world projects (FreeRTOS, Zephyr, Linux, vendor SDKs, legacy build systems) that setup is fragile, slow, or simply doesn't exist, and the results are fuzzy.

This extension reads a ctags index instead:

  • No build-system dependency. If ctags can parse it, you can search it.
  • Whole-word, multi-keyword AND search. task create finds PSF_EVENT_TASK_CREATE and trcKERNEL_HOOKS_TASK_CREATE_FAILED, in any word order, and nothing that merely looks similar.
  • Built for scale. A 1.65 M-symbol index parses in ~3 s and searches in ~100 ms; filtering never blocks the UI.
  • Ready before you type. The index is parsed in the background when VS Code starts, so even the first search is warm.
  • Macros, typedefs, anything ctags knows. Including the symbols a language server often misses.

Same query: this extension (bottom) vs built-in Ctrl+T (top)

Same query, opt tcp, same FreeRTOS tree. Built-in Ctrl+T (top) returns optdata, OPT_BYTES_SIZE, options…; this extension (bottom) returns the 28 TCP_OPT_* symbols.

Get started

  1. Install Universal Ctags — Windows, Linux, macOS. Put it on your PATH.
  2. Build the index from the workspace root:
    ctags -R --languages=C,C++ --fields=+n --extras=+q -f .tags
    
    Drop --languages=C,C++ to index every language ctags recognises.
  3. Install the extension from the Marketplace (VS Code 1.96 or newer), then press Ctrl + Alt + F in an editor, or run Search Symbols by Keywords from the command palette. Type keywords, click a result to jump to the line.

The panel lives in its own activity-bar icon; drag it to the secondary side bar or the bottom panel if you prefer. Its text follows your VS Code display language (English, 繁體中文, 简体中文).

Tips

  • Add .tags to .gitignore; the index is easy to rebuild and can be large.
  • Line numbers come from the index, so re-run ctags after you change code. A VS Code task puts that one keystroke away — .vscode/tasks.json:
    {
      "version": "2.0.0",
      "tasks": [
        {
          "label": "Rebuild ctags index",
          "type": "shell",
          "command": "ctags -R --languages=C,C++ --fields=+n --extras=+q -f .tags",
          "problemMatcher": []
        }
      ]
    }
    
  • Several index files (for example one per SDK) can be combined with the tagsFilePaths setting.

How matching works

Keywords are separated by spaces. Every keyword must match (AND), order doesn't matter, and matching is case-insensitive. Symbol names are split into words at underscores; camelCase is not split, so xTaskCreate counts as one word.

You type Word match (default) Partial match
task create Names containing the whole words task and create: PSF_EVENT_TASK_CREATE, trcKERNEL_HOOKS_TASK_CREATE_FAILED Also names where the keyword is only part of a word: xTaskCreateStatic, vTaskCreate
opt tcp ms Nothing — ms is not a whole word of any symbol TCP_OPT_MSS, TCP_OPT_MSS_LEN, TCP_BUILD_MSS_OPTION
tcp_opt Joined with underscores, the words must be adjacent and in that order: TCP_OPT_MSS, LWIP_TCP_OPT_LENGTH. A name with the two words apart or reversed does not match Same rule, but each word may be a substring

Switch modes from the panel's ... menu (Partial Match Mode). Partial match is the one to reach for on camelCase codebases and abbreviations; word match keeps big C trees noise-free.

Word match finds nothing for "opt tcp ms"; switching on Partial Match Mode finds 13 symbols

opt tcp ms finds nothing in word match; Partial Match Mode finds the 13 *_MSS_* symbols.

Settings

All settings live under the searchEnhancement.* namespace. Open the Settings UI (Ctrl + ,) and search for Search Enhancement, or edit settings.json directly.

Setting Type Default Description
tagsFilePaths string[] [] (falls back to ${workspaceFolder}/.tags) One or more ctags index files. Absolute paths or ${workspaceFolder}/... templates are both accepted. resource-scoped so each folder of a multi-root workspace can configure its own.
debounceTime number 600 Milliseconds to wait after the last keystroke before firing a search. Lower = more responsive but more CPU; higher = laggier but cheaper.
defaultGroupBy "name" | "file" "name" Initial grouping of results when the panel opens. Can be switched live from the panel's ... menu without reloading.
warmTagsCache "startup" | "viewOpen" | "off" "startup" When to pre-parse the configured .tags files in the background. startup warms as soon as VS Code finishes starting; viewOpen waits until the search view opens; off parses on the first search instead. Warming keeps the parsed symbols in memory: negligible for a typical project, ~500–750 MB for a 1.65 M-symbol index.
precomputeSegments boolean true Precompute lowercased, underscore-split words at parse time so per-keystroke filtering can skip the work. Roughly 3–4× faster searches on large indexes; costs roughly 50–100 MB of memory per million symbols.
profileSearch boolean false Log per-stage timings to the Search Enhancement output channel for each search. Use it when reporting a slow search; leave off in normal use.
Advanced: index paths, memory trade-off, diagnosing slow searches

Tags file paths

  • When tagsFilePaths is empty:
    • If the legacy searchEnhancement.tagsFilePath (deprecated) has a custom value, it is migrated into tagsFilePaths[0] and persisted to your settings.
    • Otherwise the default ${workspaceFolder}/.tags is used at runtime without modifying any settings file.
  • searchEnhancement.tagsFilePath (singular) is retained only for migration compatibility; new setups should use tagsFilePaths.
  • searchEnhancement.warmTagsCacheOnViewOpen (boolean) is deprecated in favour of warmTagsCache. An explicit false is still honoured as off while warmTagsCache is unset.

Memory / speed trade-off (precomputeSegments)

The default (true) is tuned for typical dev machines and large codebases — on a 1.65 M-symbol index it cuts the per-keystroke filter stage from ~0.4 s to ~0.1 s. (The rest of the 0.5.0 speed-up, ~3 s to ~100 ms, came from skipping the cross-file dedupe when only one .tags file is configured; this setting does not affect that.) If you index a smaller project or are on a memory-constrained machine, set it to false to save the per-symbol cache (~50–100 MB per million symbols). Toggling takes effect on the next search; the parsed-tags cache is cleared automatically.

Diagnosing slow searches (profileSearch)

Enable the setting, then open View → Output and pick Search Enhancement from the channel picker on the right. On a cache miss, parser phases and a heap snapshot are logged before the search block:

[00:55:05] Parse .tags "D:\project\.tags"
  read file             737.8ms  (290.6 MiB)
  split lines           113.2ms  (1652591 lines)
  parse rows           1565.2ms  (1652526 symbols)
  precompute segments   545.0ms
  ---
  parse total          2961.2ms
  heap used snapshot              31.5 -> 777.9 MiB  (+746.5 MiB)

Each search then appends a block like:

[14:32:05] Search "port" partial=false groupBy=name
  resolve paths            0.2ms
  tags cache              45.3ms  (1 files, 1 miss)
  dedupe                   0.0ms  (47288 symbols)
  filter                  18.7ms  (47288 → 137 matches)
  build results            0.4ms
  post message             0.1ms
  ---
  extension total         64.7ms
  webview render          12.5ms  (137 results)
  ---
  end-to-end total        77.2ms

To compare a cold first search against background warm-up, enable profileSearch, set warmTagsCache to off, reload the window, and run one search. Then set it back to startup, reload again, wait for the Tags warm-up profile block, and run the same search. Reloading between runs prevents one mode's parsed cache from affecting the other.

If background warm-up is off or has not finished yet, the first search pays the one-time tags cache parse cost proportional to the index size; later searches reuse the parsed result.

Contributing

Contributions, bug reports and feature requests are welcome. See CONTRIBUTING.md for details.

Developing

npm install
npm run compile

Press F5 in VS Code to launch a development host with the extension loaded.

Tests:

npm test                  # unit + integration
npm run test:unit         # unit only — runs in plain Node, no VS Code needed
npm run test:integration  # e2e against a real VS Code instance

License

This project is licensed under the MIT license.

Acknowledgements

Icon adapted from SVG Repo.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft