## Summary Replace the `didSave` sledgehammer (`pch_hashes.clear()` + mark-all-dirty) with precise per-file dependency tracking that avoids unnecessary recompilation. ### Two-layer staleness detection After each successful compilation, a `DepsSnapshot` is captured (interned path IDs + xxh3 content hashes + timestamp). On the next feature request, `deps_changed()` checks: - **Layer 1 (fast):** stat each dep, compare mtime against `build_at`. If mtime is older → skip. Uses strict `<` so same-second modifications fall through. - **Layer 2 (precise):** for files with newer mtime, re-hash content and compare. Catches touch-without-change (git checkout, backup restore) without false rebuilds. Special cases: files unreadable at build time (hash=0) always fall through to Layer 2; disappeared files are detected via stat failure. ### Consolidated PCH state Scatter of four maps (`pch_paths`, `pch_bounds`, `pch_hashes`, `pch_building`) → single `PCHState` struct with `path`, `bound`, `hash`, `deps`, `building` fields. `DepsSnapshot` and `SymbolInfo` moved out of `MasterServer` to namespace scope. ### Concurrent compilation dedup - **`ensure_compiled`:** `DocumentState::compiling` event prevents duplicate AST compilations. Waiters `co_await` the event and check `ast_dirty` after waking. When deps change is detected during an in-flight build, `generation` is bumped so the builder's generation check prevents it from incorrectly clearing `ast_dirty`. - **`ensure_pch`:** `PCHState::building` event deduplicates PCH builds. Waiters re-validate `preamble_hash` after waking to handle edits during the wait. The `bound==0` path waits for in-flight builds before erasing. Old PCH is deleted and path cleared before rebuild starts, so waiters never see a stale path on failure. ### `didSave` changes Removed the blanket `pch_hashes.clear()` + mark-all-dirty on save. Staleness is now detected lazily via `deps_changed()` at the next feature request. `didSave` still invalidates `CompileGraph` dependents for module deps. ### FIXME noted Rapid `didChange` edits (especially preamble changes) can cause the stateful worker to compile with stale/concatenated text. Root cause is in the worker, not in staleness tracking — noted as FIXME for a follow-up PR. ## Test plan 13 integration tests covering: - [x] Header mtime change → AST recompilation - [x] Preamble header change → PCH rebuild - [x] No change → fast path (cached AST reused) - [x] Touch without content change → Layer 2 hash skips recompile - [x] Header replaced with different content → detected - [x] Fix error in header → diagnostics clear - [x] Multiple files sharing header → each detects independently - [x] Transitive header change → detected through include chain - [x] didChange body edit → recompilation with updated diagnostics - [x] didClose + reopen → compiles new disk content - [x] didClose → hover returns None - [x] didSave header → dependent file recompiles - [x] didSave module → CompileGraph dependents invalidated 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
clice
clice is a next-generation language server designed for modern C++. Through excellent asynchronous task scheduling and intelligent caching, it achieves a lower memory footprint and faster response times.
Beyond performance, clice provides instantiation-aware template processing, supports switching header contexts between different source files (including non-self-contained headers), and offers comprehensive support for C++20 modules, from code completion to go-to-definition. Our goal is to provide C++ developers with a truly fast, precise, and intelligent development companion.
Important
Support for header contexts and C++20 modules are core features currently under active development. They will be progressively refined in upcoming releases. Stay tuned!
Getting started
Download the latest clice binary from the releases page and install the vscode extension. Then, add the following configuration to your .vscode/settings.json file:
{
// Optional: Set this to an empty string to turn off the clangd.
"clangd.path": "",
// Point this to the clice binary you downloaded.
"clice.executable": "/path/to/your/clice/executable",
}
Note
As an early version, please do not use it in a production environment. Crashes are expected, and we welcome you to submit issues.
Documentation
To learn more about building, installing, and configuring clice, or to dive deep into its features and architecture, please visit our official documentation at clice.io.