Repository navigation
docs: comprehensive feature checklists, Chinese translations, and documentation overhaul #461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
9c5ef19
747c354
4b89664
c630f58
ccc84d3
5ea71c2
9a6e5fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,63 @@ | ||
| <!-- Begin section: Overview --> | ||
|
|
||
| # clice | ||
|
|
||
|  | ||
| [](https://github.com/clice-io/clice/blob/main/LICENSE) | ||
| [](https://github.com/clice-io/clice/actions) | ||
| [](https://github.com/clice-io/clice/actions) | ||
| [](https://docs.clice.io/clice/) | ||
| [](https://deepwiki.com/clice-io/clice) | ||
| [](https://discord.gg/PA3UxW2VA3) | ||
|
|
||
| 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. | ||
| A C++ language server written from scratch on LLVM/Clang, with selected components ported from [clangd](https://clangd.llvm.org/). clice redesigns key architectural decisions to solve long-standing problems in C++ tooling. | ||
|
|
||
| ## Why clice? | ||
|
|
||
| Some problems in C++ tooling require architectural changes that cannot be easily retrofitted into existing language servers. clice addresses these from the ground up: | ||
|
|
||
| **Template intelligence** — Type `vec2[0].` inside a template body and get full completions. clice uses pseudo-instantiation to resolve dependent types through nested typedefs and template specializations without needing concrete type arguments. Types like `std::vector<std::vector<T>>::reference` resolve to `std::vector<T>&`, enabling completion, hover, and go-to-definition inside generic code. | ||
|
|
||
| **Compilation context** — A first-class concept in clice. For source files, switch between different compilation commands (e.g. different build configurations). For header files, switch which source file provides the including context (preprocessor state, preceding declarations). This handles non-self-contained headers and context-dependent macros naturally, with automatic context switching as you navigate. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For source files with multiple CDB entries, Useful? React with 👍 / 👎. |
||
|
|
||
| **C++20 named modules** — Parallel module compilation driven by a dependency-aware compile graph with interest-counted cancellation. Pre-compiled module interfaces (BMIs) are content-addressed and cached across editor sessions, so reopening a project skips redundant builds. The LSP layer provides module name completion on `import` statements and module-aware semantic highlighting. | ||
|
16bit-ykiko marked this conversation as resolved.
Resolved
|
||
|
|
||
| ## Getting Started | ||
|
|
||
| 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. | ||
| ### Install | ||
|
|
||
| > [!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! | ||
| Download the latest binary from the [releases page](https://github.com/clice-io/clice/releases), or [build from source](https://docs.clice.io/clice/dev/build). | ||
|
|
||
| ## Getting started | ||
| **Platforms:** Linux (x64, ARM64), macOS (x64, ARM64), Windows (x64, ARM64) | ||
|
|
||
| Download the latest `clice` binary from the [releases page](https://github.com/clice-io/clice/releases) and install the [vscode extension](https://marketplace.visualstudio.com/items?itemName=ykiko.clice-vscode). Then, add the following configuration to your `.vscode/settings.json` file: | ||
| ### Editor Setup | ||
|
|
||
| ```jsonc | ||
| { | ||
| // Optional: Set this to an empty string to turn off the clangd. | ||
| "clangd.path": "", | ||
| | Editor | Setup | | ||
| | ----------- | -------------------------------------------------------------------------------------------------------------------------- | | ||
| | **VS Code** | Install the [clice extension](https://marketplace.visualstudio.com/items?itemName=ykiko.clice-vscode) from the Marketplace | | ||
| | **Neovim** | Add `editors/nvim` to your runtime path: `vim.opt.rtp:append("/path/to/clice/editors/nvim")` | | ||
| | **Zed** | Load `editors/zed` as a local extension | | ||
| | **Other** | Any LSP client works — point it at `clice server` | | ||
|
|
||
| // Point this to the clice binary you downloaded. | ||
| "clice.executable": "/path/to/your/clice/executable", | ||
| } | ||
| ### Project Setup | ||
|
|
||
| clice reads a [compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html) to understand your project. For CMake: | ||
|
|
||
| ```shell | ||
| cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON | ||
| ``` | ||
|
|
||
| By default clice searches the workspace root and its immediate subdirectories (e.g. `build/`) for `compile_commands.json`. See [configuration](https://docs.clice.io/clice/guide/configuration) for custom paths and other build systems. | ||
|
|
||
| > [!NOTE] | ||
| > As an early version, please do not use it in a production environment. Crashes are expected, and we welcome you to submit issues. | ||
| > clice is approaching its first stable release. Most features work well, but some edge cases remain. Bug reports via [issues](https://github.com/clice-io/clice/issues) are welcome. | ||
|
|
||
| ## 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**](https://docs.clice.io/clice/). | ||
| Full docs at [**docs.clice.io/clice**](https://docs.clice.io/clice/) covering configuration, architecture, and the feature checklist. | ||
|
|
||
| ## Contributing | ||
|
|
||
| See the [contribution guide](https://docs.clice.io/clice/dev/contribution) or join our [Discord](https://discord.gg/PA3UxW2VA3). | ||
|
|
||
| ```shell | ||
| pixi run build # configure + build | ||
| pixi run test # unit + integration + smoke tests | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,105 @@ | ||
| # Architecture | ||
|
|
||
| ## Protocol | ||
| clice uses a **multi-process** architecture: a master server coordinates multiple worker processes. The master handles LSP requests, manages state, and dispatches compilation tasks. Workers perform CPU-intensive work (parsing, indexing) in separate processes with memory limits. | ||
|
|
||
| Use C++ to describe type definitions in the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/). | ||
| The async runtime is provided by [kotatsu](https://github.com/clice-io/kotatsu) (`kota`), which wraps libuv with C++20 coroutines. clice never calls libuv directly. | ||
|
|
||
| ## AST | ||
| ## Source Modules | ||
|
|
||
| Some convenient wrappers for clang AST interfaces. | ||
| ### `src/command/` | ||
|
|
||
| ## Async | ||
| CLI parsing, compilation database loading, and toolchain detection. Implements the subcommand-based interface (`clice server`, `clice query`, etc.). | ||
|
|
||
| Wrapper for libuv coroutines using C++20 coroutines. | ||
| ### `src/compile/` | ||
|
|
||
| ## Compiler | ||
| Compilation unit abstraction. Wraps Clang's AST and preprocessor state, providing: | ||
|
|
||
| Wrapper for clang compilation interfaces, responsible for actual compilation processes and obtaining various compilation information. | ||
| - `CompilationUnitRef` — unified access to source locations, directives, file content | ||
| - `DiagnosticID` — diagnostic classification (deprecated, unused, etc.) | ||
| - Preprocessor directive tracking (includes, embeds, module declarations) | ||
|
|
||
| ## Feature | ||
| ### `src/feature/` | ||
|
|
||
| Specific implementations of various LSP features. | ||
| LSP feature implementations, each consuming a `CompilationUnitRef`: | ||
|
|
||
| ## Server | ||
| - Code completion, hover, signature help | ||
| - Semantic tokens, inlay hints, document symbols | ||
| - Document links, folding ranges, formatting, diagnostics | ||
|
|
||
| clice is a language server, first and foremost a server. It uses [libuv](https://github.com/libuv/libuv) as the event library, adopting a common event-driven compilation model. The main thread is responsible for handling requests and dispatching tasks, while the thread pool is responsible for executing time-consuming tasks, such as compilation tasks. Related code is located in the `Server` directory. | ||
| ### `src/index/` | ||
|
|
||
| ## Support | ||
| Symbol indexing for cross-TU queries: | ||
|
|
||
| Some other utility libraries. | ||
| - `TUIndex` — per-translation-unit symbol data | ||
| - `ProjectIndex` — global symbol index with workspace-wide queries | ||
| - `MergedIndex` — per-file shards merging header contexts | ||
| - Include graph tracking | ||
|
|
||
| Serialized with FlatBuffers for efficient disk storage. | ||
|
|
||
| ### `src/semantic/` | ||
|
|
||
| Semantic analysis beyond what raw Clang provides: | ||
|
|
||
| - `SemanticVisitor` — AST traversal recording symbol occurrences and relations | ||
| - `TemplateResolver` — pseudo-instantiation for dependent name resolution | ||
| - `find_target` — resolve nodes to their declaration targets | ||
| - Symbol kind/modifier classification | ||
|
|
||
| ### `src/syntax/` | ||
|
|
||
| Lightweight syntax-level processing (no full AST needed): | ||
|
|
||
| - `Lexer` (`lexer.h`) and directive scanning (`scan.h`, wraps Clang's `DependencyDirectivesScanner`) — token-level utilities | ||
| - `DependencyGraph` — module/include dependency tracking | ||
| - Include path completion, module import completion | ||
|
|
||
| ### `src/server/` | ||
|
|
||
| The server core, split into sub-modules: | ||
|
|
||
| #### `src/server/service/` | ||
|
|
||
| - `MasterServer` — top-level LSP server, routes requests to sessions/workers | ||
| - `LSPClient` — LSP protocol handler, capability registration | ||
| - `Session` — per-file state (active context, pending requests) | ||
|
|
||
| #### `src/server/compiler/` | ||
|
|
||
| - `Compiler` — compilation orchestration (PCH, PCM, index builds) | ||
| - `CompileGraph` — interest-counted module DAG with cancellation | ||
| - `Indexer` — background indexing, query resolution | ||
|
|
||
| #### `src/server/worker/` | ||
|
|
||
| - `StatefulWorker` — holds ASTs in memory, serves queries (hover, semantic tokens, etc.) | ||
| - `StatelessWorker` — ephemeral tasks (PCH/PCM builds, completion, signature help) | ||
| - `WorkerPool` — process lifecycle management | ||
|
|
||
| #### `src/server/workspace/` | ||
|
|
||
| - `Workspace` — project state, config, dependency graph | ||
| - `Config` — TOML/JSON config loading and validation | ||
|
|
||
| ### `src/support/` | ||
|
|
||
| Utilities: logging, filesystem helpers, JSON serialization, string operations. | ||
|
|
||
| ## Process Model | ||
|
|
||
| ```plaintext | ||
| ┌─────────────────────────────────────────────────┐ | ||
| │ Master Process │ | ||
| │ event loop (kota) + LSP I/O + state management │ | ||
| └────────────┬──────────────────┬─────────────────┘ | ||
| │ bincode IPC │ bincode IPC | ||
| ┌───────▼───────┐ ┌─────▼──────────┐ | ||
| │ Stateful │ │ Stateless │ | ||
| │ Workers (×2) │ │ Workers (×N/2) │ | ||
| │ hold ASTs, │ │ build PCH/PCM, │ | ||
| │ serve queries │ │ completion, │ | ||
| └───────────────┘ │ signature help │ | ||
| └─────────────────┘ | ||
| ``` | ||
|
|
||
| Workers communicate with the master via stdin/stdout using bincode serialization. Each worker runs in its own process with a configurable memory limit (default 4 GB). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,9 @@ | |
|
|
||
| ## Incremental Parsing | ||
|
|
||
| Every time you modify code, clice must re-parse the file. clice uses a mechanism called preamble to implement incremental compilation to speed up re-parsing. Preamble can be considered a special case of [Precompiled Header](https://clang.llvm.org/docs/PCHInternals.html) (embedded in source files). When opening a file, it builds the first few preprocessor directives at the beginning of the file (called preamble) into a PCH cache on disk. Later, when parsing, it can directly load the PCH file, thus skipping the first few preprocessor directives, which can greatly reduce the amount of code that needs to be re-parsed. | ||
| Every time you modify code, clice must re-parse the file. clice uses a mechanism called preamble to implement incremental compilation. The preamble is a special case of [Precompiled Header](https://clang.llvm.org/docs/PCHInternals.html) — it builds the initial preprocessor directives (the `#include` block at the top of a file) into a PCH cache on disk. Later, when parsing, it loads the PCH directly, skipping the preamble section entirely. | ||
|
|
||
| For example, for the following code: | ||
| For example: | ||
|
|
||
| ```cpp | ||
| #include <iostream> | ||
|
|
@@ -14,6 +14,55 @@ int main () { | |
| } | ||
| ``` | ||
|
|
||
| The `iostream` header file has about 20,000 lines of code. clice will first build the line `#include <iostream>` into a PCH file, and after completion, use this PCH file to parse the subsequent code. This way, the amount of code that needs to be re-parsed later is reduced to just 5 lines instead of the original 20,000 lines, making it very fast. Unless you modify the code in the preamble section, which requires building a new preamble. | ||
| The `iostream` header expands to ~20,000 lines. clice builds `#include <iostream>` into a PCH file, reducing subsequent re-parses from 20,000 lines to just 5 lines. Only modifying the preamble section (adding/removing includes) requires rebuilding the PCH. | ||
|
16bit-ykiko marked this conversation as resolved.
Resolved
|
||
|
|
||
| ### Staleness Detection | ||
|
|
||
| PCH freshness is checked via a two-layer scheme: | ||
|
|
||
| 1. **Mtime check** — fast path: if no dependency has a newer mtime than the cached PCH, it's still valid. | ||
| 2. **Content hash** — if mtime changed (e.g., a `touch` without real edits), hash the file contents. Only if the hash differs is the PCH rebuilt. | ||
|
|
||
| This avoids expensive rebuilds on timestamp-only changes (common with build systems that regenerate files in-place). | ||
|
|
||
| ## Multi-Process Pipeline | ||
|
|
||
| Compilation tasks are distributed across worker processes: | ||
|
|
||
| - **Stateless workers** handle PCH builds, PCM builds (C++20 modules), completion, and signature help. These are ephemeral — no state persists between requests. | ||
| - **Stateful workers** hold parsed ASTs in memory and serve queries (hover, semantic tokens, inlay hints, etc.). Files are cached via LRU eviction. | ||
|
|
||
| The master process never performs compilation directly. It dispatches tasks and collects results via bincode IPC over stdin/stdout. | ||
|
|
||
| ## C++20 Modules | ||
|
|
||
| Module compilation follows a dependency graph (`CompileGraph`). The master resolves module dependencies from the syntax-level `DependencyGraph`, then builds PCM files in topological order via stateless workers. | ||
|
|
||
| Each module unit has a `CompileUnit` in the compile graph with: | ||
|
|
||
| - A refcount (interest count) tracking how many downstream dependents need it | ||
| - A cancellation token that fires when the refcount drops to zero | ||
|
|
||
| When a file is modified, dependent module units are automatically recompiled in dependency order. | ||
|
|
||
| ## Cancel Compilation | ||
|
|
||
| clice uses **interest-counted cancellation** to avoid wasting CPU on compilations that are no longer needed. | ||
|
|
||
| ### The Problem | ||
|
|
||
| In a module DAG like `A → B → C`, if the user opens file C, compilation starts for A, then B, then C. But if the user closes C (or switches to another file) while A is still building, continuing to build B and C is pointless. | ||
|
|
||
| ### The Mechanism | ||
|
|
||
| Each `CompileUnit` in the compile graph maintains a reference count: | ||
|
|
||
| - When a file needs a module's PCM, it increments (`acquire`) that module's refcount. | ||
| - When it no longer needs it (file closed, edit superseded), it decrements (`release`). | ||
| - When refcount reaches zero, a one-tick grace period fires. If still zero after the grace period, the compilation's cancellation token is triggered. | ||
|
|
||
| The grace period prevents thrashing — rapid open/close sequences don't cause redundant cancel-and-restart cycles. | ||
|
|
||
| ### Supersede Logic | ||
|
|
||
| When a file is edited while its previous compilation is in-flight, the old build is "superseded" — its interest is released and a new build starts. The old worker task observes the cancellation token and aborts early. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The cancellation token is applied to the compile-graph coroutine, but the PCM dispatch itself is just Useful? React with 👍 / 👎. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current editor feature paths do not use
TemplateResolver: repo-wide usage only constructs it, while code completion goes through Clang code completion, hover uses Clang'sHeuristicResolver, and the stateful go-to-definition fallback is still a TODO. In dependent template bodies such as the example here, users should not be promised pseudo-instantiation-backed completion/hover/navigation until the resolver is actually wired into those LSP paths.Useful? React with 👍 / 👎.