feat(server): improve configuration file handling (#423)

## Summary

- **`[[rules]]`**: TOML array-of-tables config for per-file compilation
flag rules with glob pattern matching (`append`/`remove`). Patterns are
pre-compiled at config load time. Rules whose patterns all fail to
compile are dropped entirely (no silent no-op entries), and rules now
apply uniformly to every compilation — including the header-context
fallback path used when editing a header without its own CDB entry.
- **CDB auto-scan**: Default search scans workspace root + all immediate
subdirectories for `compile_commands.json`, replacing the hardcoded
directory list.
- **LSP `initializationOptions`**: Clients can pass config as JSON via
the LSP initialize request; priority is `initializationOptions >
clice.toml > defaults`.
- **XDG cache paths**: Default cache/index/logging paths prefer
`$XDG_CACHE_HOME/clice/<workspace-hash>/`; fall back to
`$HOME/.cache/clice/<hash>/`, then `<workspace>/.clice/`.
- **`${workspace}` substitution**: supported in `cache_dir`,
`index_dir`, `logging_dir`, and every `compile_commands_paths` entry.
No-op when `workspace_root` is empty.
- **Partial config support**: All TOML/JSON fields are optional via
`kota::meta::defaulted<T>`, so minimal config files work correctly.
- **Detailed diagnostics**: malformed `clice.toml` now logs line, column
and parser description (via toml++ direct parse); a malformed workspace
config surfaces a clear fallback warning instead of silently reverting
to defaults.

## Test plan

- [x] 28 unit tests for config (full suite 545 unit tests pass, Debug)
- [x] 119 integration tests pass
- [x] 2 smoke tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* XDG-based, workspace-scoped project cache (PCH/PCM and header caches
moved under project cache) with workspace fallback
* Initialization options JSON can override config (takes precedence over
file/defaults)
* Per-file pattern rules to append/remove compile flags; expanded
discovery of compilation databases (multiple paths)

* **Refactor**
* Configuration fields reorganized under a project scope; runtime
behavior now respects project-scoped values

* **Tests**
* New unit and integration tests for config parsing, rule matching, and
persistent cache behavior

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ykiko
2026-04-20 00:21:31 +08:00
committed by GitHub
parent 3fa653bcaf
commit 17e68010a0
21 changed files with 989 additions and 150 deletions

View File

@@ -256,7 +256,8 @@ kota::task<> scan_impl(CompilationDatabase& cdb,
DependencyGraph& graph,
ScanReport& report,
ScanCache* ext_cache,
kota::event_loop& loop) {
kota::event_loop& loop,
const RuleMatcher& rule_matcher) {
auto start_time = std::chrono::steady_clock::now();
// Reuse context groups and configs from cache when available (warm runs).
@@ -355,9 +356,19 @@ kota::task<> scan_impl(CompilationDatabase& cdb,
std::uint32_t config_id = next_config_id++;
context_to_config_id[context] = config_id;
auto representative_path = path_pool.resolve(file_ids[0]);
// Apply per-file rules so that `[[rules]]`-modified -I/-isystem/-std
// flags are reflected in the search config used by the scan.
// Rules are applied to the representative file and assumed to hold
// for the whole context group (same CompilationInfo).
std::vector<std::string> rule_append, rule_remove;
if(rule_matcher)
rule_matcher(representative_path, rule_append, rule_remove);
auto t0 = std::chrono::steady_clock::now();
configs[config_id] =
cdb.lookup_search_config(representative_path, {.query_toolchain = true});
configs[config_id] = cdb.lookup_search_config(
representative_path,
{.query_toolchain = true, .remove = rule_remove, .append = rule_append});
auto t1 = std::chrono::steady_clock::now();
lookup_us += std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
}
@@ -819,14 +830,15 @@ kota::task<> scan_impl(CompilationDatabase& cdb,
ScanReport scan_dependency_graph(CompilationDatabase& cdb,
PathPool& path_pool,
DependencyGraph& graph,
ScanCache* cache) {
ScanCache* cache,
const RuleMatcher& rule_matcher) {
ScanReport report;
if(cdb.get_entries().empty()) {
return report;
}
kota::event_loop loop;
loop.schedule(scan_impl(cdb, path_pool, graph, report, cache, loop));
loop.schedule(scan_impl(cdb, path_pool, graph, report, cache, loop, rule_matcher));
loop.run();
return report;
}