Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>Cadmus - Swift IndexingNew to Visual Studio Code? Get it now.
Cadmus - Swift Indexing

Cadmus - Swift Indexing

Cadmus

|
6 installs
| (0) | Free
Swift/Objective-C indexing for VS Code via SWBBuildService
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Cadmus

Swift/Objective-C code intelligence for VS Code and Cursor — powered by Xcode's build engine.

Cadmus brings Xcode-grade indexing to alternative editors. Jump to definition, autocomplete, diagnostics, find callers, rename symbol, find references, macro expansion, and cross-module navigation work out of the box for any Xcode project, including large monorepos with hundreds of targets.

What it does

Cadmus bridges Xcode's internal build service (SWBBuildService) with sourcekit-lsp through the Build Server Protocol. Instead of reimplementing Swift compilation logic, it speaks Xcode's native protocol to extract the exact compiler arguments your project needs.

Editor (VS Code / Cursor)
  └─ sourcekit-lsp
       └─ BSP (JSON-RPC)
            └─ cadmus-helper
                 └─ SWBBuildService (Xcode's build engine)
  • Every framework, SPM package, and Objective-C bridging header resolves correctly
  • Build settings, xcconfig files, and conditional compilation flags are respected
  • Swift macros expand correctly (@Observable, custom macros, etc.)
  • Cross-module jump-to-definition, diagnostics, and navigation work across targets
  • Background indexing populates the index store for find callers, rename, and find references
  • File changes automatically trigger re-indexing of affected and dependent targets
  • Projects with 500+ targets work without manual configuration

Quick start

Requirements

  • macOS with Xcode 26+ installed
  • Swift for VS Code — provides sourcekit-lsp integration. Cadmus supplies the build server; the Swift extension supplies the language server. Installed automatically as a dependency.

Install from release

Download the latest .vsix from Releases, then:

code --install-extension cadmus-*.vsix
# or for Cursor:
cursor --install-extension cadmus-*.vsix

Build from source

git clone https://github.com/gokberkince/cadmus-vscode.git
cd cadmus-vscode
make package
code --install-extension cadmus-*.vsix

What happens on first open

  1. Extension detects your .xcworkspace or .xcodeproj
  2. Writes buildServer.json so sourcekit-lsp uses Cadmus as its build server
  3. Spawns cadmus-helper which connects to Xcode's build service
  4. Progress notification shows each phase until ready
  5. Jump-to-definition, autocomplete, and diagnostics start working

Subsequent launches are fast — compiler args are cached on disk. When the SWB bridge is still alive from a previous session and the PIF hasn't changed, session reuse skips PIF transfer, dependency graph computation, and bootstrap builds entirely (under 1 second startup).

How it works

Initialization

  1. SPM Resolve — Runs swift package resolve for SPM dependencies
  2. PIF Generation — Parses xcodeproj/xcworkspace into Xcode's Project Interchange Format
  3. SWB Session — Connects to SWBBuildService, creates a session, transfers PIF
  4. Dependency Graph — Computes the full target dependency graph (explicit + implicit framework linking)
  5. Bootstrap Build — Creates build descriptions — compiler args become available
  6. Ready — sourcekit-lsp can now request compiler args for any file
  7. Full Workspace Build (background) — Creates build descriptions for all targets, not just the bootstrap target's transitive dependencies

After step 6, sourcekit-lsp's background indexer takes over immediately: waitForBuildSystemUpdates returns after the bootstrap build description (not the full workspace build), so LSP can start showing "Indexing N / M" progress right away. Targets not covered by bootstrap are handled lazily — when LSP sends buildTarget/prepare for a non-bootstrap target, cadmus-helper returns success immediately and queues it. When the full workspace build completes, buildTarget/didChange is sent only for those deferred targets (and their dependents), causing LSP to re-prepare them with the real full-workspace build description.

Background indexing

Cadmus advertises prepareProvider: true to sourcekit-lsp, which enables its SemanticIndexManager. This is what makes project-wide features work:

  1. sourcekit-lsp sends buildTarget/prepare for each target (sorted by dependency depth)
  2. cadmus-helper runs SWB's prepareForIndexing — builds all dependency .swiftmodule files
  3. sourcekit-lsp runs swiftc -index-file for each source file in the prepared target
  4. Index store gets populated — find callers, rename, find references all work

Editor operations take priority: when you open a file, sourcekit-lsp cancels any background prepare and triggers a high-priority prepare for that file's target first. cadmus-helper does not run any proactive prepare builds — all preparation is driven by sourcekit-lsp's own background indexing pipeline to avoid "multiple concurrent index build operations" errors in SWB.

File change flow

  1. sourcekit-lsp detects file changes and notifies cadmus-helper (workspace/didChangeWatchedFiles)
  2. For existing files: cadmus-helper maps files to targets, computes reverse dependencies (which targets import the changed targets), and sends buildTarget/didChange immediately (old build description IDs are still valid for existing files)
  3. For new source files: cadmus-helper updates the .pbxproj so the file maps to the correct target — for buildable folders (Xcode 16+) this updates membershipExceptions, for traditional groups it adds a PBXFileReference + PBXBuildFile. Then:
    • PIF reload: regenerates project structure (targeted single-project reload when possible, ~1s vs ~15s full)
    • CDG + Bootstrap rebuild: produces a new build description ID that includes the new file
    • buildTarget/didChange sent after the bootstrap rebuild completes (old build descriptions return empty for new files)
  4. sourcekit-lsp marks affected targets as needing re-preparation and schedules new buildTarget/prepare requests
  5. Affected .swiftmodule files get rebuilt, diagnostics and autocomplete refresh automatically

cadmus-helper does not manage file watching or build scheduling itself — sourcekit-lsp drives everything.

Caching

Layer Storage Survives restart
In-memory Dict in BSPServer actor No
Disk cache cadmus-args-cache.json in DerivedData Yes
SWB build cache SWBBuildService internal state Yes
SWB bridge Unix domain socket daemon Yes (across cadmus-helper restarts)

The disk cache stores compiler args keyed by target GUID with a fingerprint based on project.pbxproj modification times. The bootstrap build description ID is saved to disk immediately after bootstrap (not deferred until full workspace), so if the process dies before the full workspace build completes, the next launch still has a valid cached descID for the session reuse fast path. On restart, cached args are served instantly while SWB initializes in the background.

Sidebar

Cadmus adds a panel to the activity bar showing the current indexing state. From here you can inspect configuration, change the target platform, manage caches, re-trigger indexing, and open log files — all without leaving the editor.

CADMUS
├── Workspace: MyApp.xcworkspace          [edit]
├── Destination: iOS Simulator            [edit]
├── Helper Binary: cadmus-helper [bundled]
├── DerivedData: .cadmus
├── Cache: present                        [trash]
└── Logs
    ├── Extension Output
    ├── BSP Server Log
    └── SWB Bridge Log

Configuration

Setting Default Description
cadmus.helperPath Auto-detect Path to cadmus-helper binary
cadmus.platform macosx Target platform (macosx, iphonesimulator, etc.)
cadmus.derivedDataPath .cadmus/ Custom DerivedData path

Commands

Command Description
Cadmus: Select Xcode Workspace Choose which workspace/project to index
Cadmus: Restart Indexing Re-initialize from scratch
Cadmus: Change Destination Switch target platform (macOS, iOS Simulator, etc.)
Cadmus: Clear Cache Delete compiler args disk cache
Cadmus: Re-trigger Indexing "With Cache" (restart only) or "Clean" (delete cache + index data + restart)
Cadmus: Open BSP Log Open the BSP server log in the editor
Cadmus: Open Bridge Log Open the SWB bridge daemon log in the editor

Debugging

# BSP server logs (one per project)
tail -f /tmp/cadmus-bsp-*.log

# SWB bridge daemon logs
tail -f /tmp/cadmus-swb-*.log

# Progress file (JSONL, polled by extension)
cat /tmp/cadmus-progress-*.jsonl

Xcode projects only

Cadmus is designed for Xcode projects (.xcworkspace / .xcodeproj). It does not activate for pure Swift Package Manager packages — sourcekit-lsp already handles those natively.

Detection logic:

  • If the workspace root contains Package.swift but no .xcworkspace or .xcodeproj, Cadmus treats it as an SPM package and stays inactive
  • Nested Xcode projects inside SPM checkouts (.build/, .cadmus/, .swiftpm/) are ignored
  • If a stale buildServer.json from a previous Cadmus session is found in a non-Xcode workspace, it is automatically cleaned up

Process lifecycle

cadmus-helper writes its PID to /tmp/cadmus-bsp-<project>.pid on startup. This enables reliable cleanup:

  • stdin EOF: When sourcekit-lsp dies or the editor closes, cadmus-helper detects stdin EOF and force-exits after a 2-second grace period (the actor's TaskGroup may be blocked on in-progress SWB builds that will never complete).
  • Extension shutdown: The extension's deactivate() kills cadmus-helper via the PID file with SIGKILL, ensuring no zombie processes survive editor shutdown.
  • LSP restart: Before calling swift.restartLSPServer, the extension kills the old cadmus-helper via PID file. This breaks the old BSP connection so sourcekit-lsp can stop cleanly instead of blocking on a pending BSP response.

The SWB bridge daemon is unaffected by cadmus-helper restarts — it keeps SWBBuildService alive for session reuse.

Known limitations

  • Requires Xcode (uses SWBBuildService)
  • First launch for large projects (500+ targets) takes 1-3 minutes
  • macOS only

Contributing

The project has two main parts:

  • VS Code extension (src/) — TypeScript, handles activation, UI, and spawning cadmus-helper
  • cadmus-helper (cadmus-helper/) — Swift, the BSP server that talks to SWBBuildService

Getting started

git clone https://github.com/gokberkince/cadmus-vscode.git
cd cadmus-vscode

# Build the Swift backend
cd cadmus-helper && swift build && cd ..

# Install VS Code extension dependencies
npm install

Development workflow

  1. Make changes to cadmus-helper/ or src/
  2. Build: cd cadmus-helper && swift build
  3. Test with a real Xcode project — point the extension at your local build via cadmus.helperPath
  4. Check logs: tail -f /tmp/cadmus-bsp-*.log

Architecture

See cadmus-helper/ARCHITECTURE.md for detailed internals — PIF generation, SWB protocol, file change handling, caching, and dependency graph construction.

License

GPL-3.0 — see LICENSE

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft