Skip to content

Commit 9c9e6b0

Browse files
16bit-ykikoclaude
andauthored
refactor: introduce Workspace/Session state model and clarify component responsibilities (#406)
## Summary Introduces a two-layer state model that cleanly separates disk-based project state from per-open-file editing state, and redistributes responsibilities across server components so each has a single, clear role. ## New types **Workspace** — all persistent, project-wide shared state: - CompilationDatabase, PathPool, DependencyGraph, CompileGraph - path_to_module mapping, PCH cache, PCM cache, PCM paths - ProjectIndex, MergedIndex shards - CliceConfig - Methods: on_file_saved(), on_file_closed(), load/save/cleanup_cache(), build_module_map(), fill_pcm_deps(), cancel_all() **Session** — volatile per-open-file editing state: - text, version, generation, ast_dirty - pch_ref (references Workspace.pch_cache), ast_deps, header_context - file_index (OpenFileIndex for unsaved buffer) - path_id member for self-identification ## Component responsibilities after refactor | Component | Role | Owns state? | |-----------|------|-------------| | **Workspace** | Disk truth + shared caches | Yes (all project state) | | **Session** | One open file editing state | Yes (per-file only) | | **Compiler** | Compilation pipeline, worker communication | No (references only) | | **Indexer** | Index queries + background indexing scheduling | Scheduling state only | | **MasterServer** | LSP protocol dispatch + lifecycle coordination | sessions map | ## What moved where **Into Workspace** (from Compiler/MasterServer): - PCH/PCM cache management (load_cache, save_cache, cleanup_cache) - Module map building (build_module_map, fill_pcm_deps) - File lifecycle hooks (on_file_saved, on_file_closed) - cancel_all, OpenFileIndex/MergedIndexShard type definitions **Into Session** (from Compiler documents map): - Document text, version, generation, ast_dirty - PCH reference, dependency snapshot, header context **Into Indexer** (from MasterServer): - Background indexing queue, scheduling state, idle timer - schedule(), enqueue(), run_background_indexing() **Into syntax/completion.h** (from Compiler): - detect_completion_context() — pure text parsing - complete_module_import() — prefix match on module names - complete_include_path() — directory listing against search paths **Inlined into MasterServer** (from Compiler): - didOpen/didChange/didClose/didSave handlers - switchContext/currentContext - publish_diagnostics/clear_diagnostics **Deleted from Compiler** (9 methods): - open_document, apply_changes, close_document, on_save - switch_context, get_active_context, invalidate_host_contexts - on_file_closed, on_file_saved, complete_include, complete_import ## Tests - 481 tests pass (465 existing + 16 new completion tests) - New: tests/unit/syntax/completion_tests.cpp ## Diff stats 15 files changed, +1857, -1555 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Enhanced completion support for include paths and module imports with improved context detection. * Added background indexing system for automatic project symbol indexing. * **Bug Fixes** * Improved reliability of document change tracking and compilation state management. * Better handling of header file compilation contexts. * **Tests** * Added unit tests for completion context detection and module/include path completion. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bb0b160 commit 9c9e6b0

File tree

17 files changed

+1946
-1577
lines changed

17 files changed

+1946
-1577
lines changed

src/server/compiler.cpp

Lines changed: 326 additions & 862 deletions
Large diffs are not rendered by default.

src/server/compiler.h

Lines changed: 54 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include <cstdint>
44
#include <functional>
5-
#include <memory>
65
#include <optional>
76
#include <string>
87
#include <unordered_map>
@@ -13,12 +12,10 @@
1312
#include "eventide/ipc/lsp/protocol.h"
1413
#include "eventide/ipc/peer.h"
1514
#include "eventide/serde/serde/raw_value.h"
16-
#include "server/compile_graph.h"
17-
#include "server/config.h"
18-
#include "server/indexer.h"
15+
#include "server/session.h"
1916
#include "server/worker_pool.h"
20-
#include "support/path_pool.h"
21-
#include "syntax/dependency_graph.h"
17+
#include "server/workspace.h"
18+
#include "syntax/completion.h"
2219

2320
#include "llvm/ADT/ArrayRef.h"
2421
#include "llvm/ADT/DenseMap.h"
@@ -30,230 +27,106 @@ namespace clice {
3027
namespace et = eventide;
3128
namespace protocol = et::ipc::protocol;
3229

33-
/// State of a document opened by the client.
34-
struct DocumentState {
35-
int version = 0;
36-
std::string text;
37-
std::uint64_t generation = 0;
38-
bool ast_dirty = true;
30+
/// Convert a file:// URI to a local file path.
31+
std::string uri_to_path(const std::string& uri);
3932

40-
/// Non-null while a compile is in flight. Callers wait on the event;
41-
/// the compile task runs independently and cannot be cancelled by LSP
42-
/// $/cancelRequest.
43-
struct PendingCompile {
44-
et::event done;
45-
bool succeeded = false;
46-
};
47-
48-
std::shared_ptr<PendingCompile> compiling;
49-
};
50-
51-
/// Context for compiling a header file that lacks its own CDB entry.
52-
struct HeaderFileContext {
53-
std::uint32_t host_path_id; /// Source file acting as host
54-
std::string preamble_path; /// Path to generated preamble file on disk
55-
std::uint64_t preamble_hash; /// Hash of preamble content for staleness
56-
};
57-
58-
/// Two-layer staleness snapshot for compilation artifacts (PCH, AST, etc.).
33+
/// Compilation service — drives worker processes to build ASTs, PCHs, and PCMs.
5934
///
60-
/// Layer 1 (fast): compare each file's current mtime against build_at.
61-
/// If all mtimes <= build_at, the artifact is fresh (zero I/O beyond stat).
35+
/// Compiler holds no persistent state of its own. All project-wide data
36+
/// lives in Workspace; per-file data lives in Session. Compiler reads from
37+
/// both and writes compilation results back to Session (file_index, pch_ref,
38+
/// ast_deps, diagnostics).
6239
///
63-
/// Layer 2 (precise): for files whose mtime changed, re-hash their content
64-
/// and compare against the stored hash. If the hash matches, the file was
65-
/// "touched" but not actually modified — skip the rebuild.
66-
struct DepsSnapshot {
67-
llvm::SmallVector<std::uint32_t> path_ids;
68-
llvm::SmallVector<std::uint64_t> hashes;
69-
std::int64_t build_at = 0;
70-
};
71-
72-
/// Cached PCH state for a single source file.
73-
struct PCHState {
74-
std::string path;
75-
std::uint32_t bound = 0;
76-
std::uint64_t hash = 0;
77-
DepsSnapshot deps;
78-
std::shared_ptr<et::event> building;
79-
};
80-
81-
/// Cached PCM state for a single module file.
82-
struct PCMState {
83-
std::string path;
84-
DepsSnapshot deps;
85-
};
86-
87-
enum class CompletionContext { None, IncludeQuoted, IncludeAngled, Import };
88-
89-
struct PreambleCompletionContext {
90-
CompletionContext kind = CompletionContext::None;
91-
std::string prefix;
92-
};
93-
94-
/// Manages the full compilation lifecycle: document state, compilation
95-
/// artifacts (PCH/PCM cache), compile argument resolution, header context,
96-
/// and feature request forwarding to workers.
40+
/// Responsibilities:
41+
/// - AST compilation lifecycle (ensure_compiled → ensure_pch → ensure_deps)
42+
/// - Feature request forwarding to stateful/stateless workers
43+
/// - Compile argument resolution (CDB lookup + header context fallback)
44+
/// - Compile graph initialization (module DAG setup)
9745
///
98-
/// MasterServer delegates all document and compilation operations here,
99-
/// keeping itself as a pure LSP handler registration layer.
46+
/// NOT responsible for:
47+
/// - Document lifecycle (didOpen/didChange/didClose) — handled by MasterServer
48+
/// - Index queries — handled by Indexer
49+
/// - Background indexing scheduling — handled by Indexer
10050
class Compiler {
10151
public:
10252
Compiler(et::event_loop& loop,
10353
et::ipc::JsonPeer& peer,
104-
PathPool& path_pool,
54+
Workspace& workspace,
10555
WorkerPool& pool,
106-
Indexer& indexer,
107-
const CliceConfig& config,
108-
CompilationDatabase& cdb,
109-
DependencyGraph& dep_graph);
110-
56+
llvm::DenseMap<std::uint32_t, Session>& sessions);
11157
~Compiler();
11258

113-
/// Convert a file:// URI to a local file path.
114-
static std::string uri_to_path(const std::string& uri);
115-
116-
/// Document lifecycle — called from MasterServer handlers.
117-
void open_document(const std::string& uri, std::string text, int version);
118-
void apply_changes(const protocol::DidChangeTextDocumentParams& params);
119-
std::uint32_t close_document(const std::string& uri);
120-
llvm::SmallVector<std::uint32_t> on_save(const std::string& uri);
121-
122-
/// Document accessors.
123-
bool is_file_open(std::uint32_t path_id) const;
124-
const DocumentState* get_document(std::uint32_t path_id) const;
125-
126-
/// Cache persistence.
127-
void load_cache();
128-
void save_cache();
129-
void cleanup_cache(int max_age_days = 7);
130-
131-
/// Build path_to_module reverse mapping from dependency graph.
132-
void build_module_map();
133-
134-
/// Initialize the CompileGraph for C++20 module compilation ordering.
13559
void init_compile_graph();
13660

13761
/// Fill compile arguments for a file (CDB lookup + header context fallback).
62+
/// @param session If non-null, used for header context resolution on open files.
13863
bool fill_compile_args(llvm::StringRef path,
13964
std::string& directory,
140-
std::vector<std::string>& arguments);
141-
142-
/// Fill PCM paths for all built modules (for background indexing).
143-
void fill_pcm_deps(std::unordered_map<std::string, std::string>& pcms,
144-
std::uint32_t exclude_path_id = UINT32_MAX) const;
65+
std::vector<std::string>& arguments,
66+
Session* session = nullptr);
14567

146-
/// Pull-based compilation entry point for user-opened files.
147-
et::task<bool> ensure_compiled(std::uint32_t path_id);
68+
/// Compile an open file's AST if dirty. On success, updates session's
69+
/// file_index, pch_ref, ast_deps, and publishes diagnostics.
70+
et::task<bool> ensure_compiled(Session& session);
14871

149-
/// Feature request forwarding to workers.
15072
using RawResult = et::task<et::serde::RawValue, et::ipc::Error>;
15173

152-
/// Forward a stateful AST query to the worker owning this file.
153-
RawResult forward_query(worker::QueryKind kind, const std::string& uri);
74+
/// Forward a query to the stateful worker that holds this file's AST.
75+
/// Ensures compilation first. For position-sensitive queries (hover,
76+
/// goto-definition), pass a Position. For range-sensitive queries
77+
/// (inlay hints), pass a Range.
15478
RawResult forward_query(worker::QueryKind kind,
155-
const std::string& uri,
156-
const protocol::Position& position);
79+
Session& session,
80+
std::optional<protocol::Position> position = {},
81+
std::optional<protocol::Range> range = {});
15782

158-
/// Forward a stateless build request (completion/signatureHelp).
83+
/// Forward a build request (signature help, etc.) to a stateless worker.
84+
/// Sends the full buffer content and compile arguments.
15985
RawResult forward_build(worker::BuildKind kind,
160-
const std::string& uri,
161-
const protocol::Position& position);
86+
const protocol::Position& position,
87+
Session& session);
16288

163-
/// Completion with preamble-aware include/import handling.
164-
RawResult handle_completion(const std::string& uri, const protocol::Position& position);
89+
/// Handle completion requests. Detects preamble context (include/import)
90+
/// and serves those locally; delegates code completion to a stateless worker.
91+
RawResult handle_completion(const protocol::Position& position, Session& session);
16592

166-
/// Header context management.
167-
void switch_context(std::uint32_t path_id, std::uint32_t context_path_id);
168-
std::optional<std::uint32_t> get_active_context(std::uint32_t path_id) const;
169-
void invalidate_host_contexts(std::uint32_t host_path_id,
170-
llvm::SmallVectorImpl<std::uint32_t>& stale_headers);
171-
172-
CompileGraph* compile_graph_ptr() {
173-
return compile_graph.get();
174-
}
175-
176-
const llvm::DenseMap<std::uint32_t, std::string>& module_map() const {
177-
return path_to_module;
178-
}
179-
180-
void cancel_all();
93+
/// Send an empty diagnostics notification to clear stale markers in the editor.
94+
void clear_diagnostics(const std::string& uri);
18195

182-
/// Callback invoked when indexing should be scheduled (e.g. after compile success).
96+
/// Callback invoked when indexing should be scheduled.
18397
std::function<void()> on_indexing_needed;
18498

18599
private:
186-
/// Compile module dependencies, build/reuse PCH, and fill PCM paths.
187-
et::task<bool> ensure_deps(std::uint32_t path_id,
188-
llvm::StringRef path,
189-
const std::string& text,
100+
et::task<bool> ensure_deps(Session& session,
190101
const std::string& directory,
191102
const std::vector<std::string>& arguments,
192103
std::pair<std::string, uint32_t>& pch,
193104
std::unordered_map<std::string, std::string>& pcms);
194105

195-
/// Build or reuse PCH for a source file.
196-
et::task<bool> ensure_pch(std::uint32_t path_id,
197-
llvm::StringRef path,
198-
const std::string& text,
106+
et::task<bool> ensure_pch(Session& session,
199107
const std::string& directory,
200108
const std::vector<std::string>& arguments);
201109

202-
/// Check if a file's AST or PCH deps have changed since last compile.
203-
bool is_stale(std::uint32_t path_id);
204-
205-
/// Record dependency snapshot after a successful compile.
206-
void record_deps(std::uint32_t path_id, llvm::ArrayRef<std::string> deps);
110+
bool is_stale(const Session& session);
111+
void record_deps(Session& session, llvm::ArrayRef<std::string> deps);
207112

208113
void publish_diagnostics(const std::string& uri, int version, const et::serde::RawValue& diags);
209-
void clear_diagnostics(const std::string& uri);
210114

211-
/// Clean up compilation state for a closed file.
212-
void on_file_closed(std::uint32_t path_id);
115+
std::optional<HeaderFileContext> resolve_header_context(std::uint32_t header_path_id,
116+
Session* session);
213117

214-
/// Invalidate artifacts after a file save.
215-
/// Returns path_ids of all files dirtied (via compile_graph cascade).
216-
llvm::SmallVector<std::uint32_t> on_file_saved(std::uint32_t path_id);
217-
218-
/// Header context resolution.
219-
std::optional<HeaderFileContext> resolve_header_context(std::uint32_t header_path_id);
220118
bool fill_header_context_args(llvm::StringRef path,
221119
std::uint32_t path_id,
222120
std::string& directory,
223-
std::vector<std::string>& arguments);
224-
225-
/// Include/import completion helpers.
226-
PreambleCompletionContext detect_completion_context(const std::string& text, uint32_t offset);
227-
et::serde::RawValue complete_include(const PreambleCompletionContext& ctx,
228-
llvm::StringRef path);
229-
et::serde::RawValue complete_import(const PreambleCompletionContext& ctx);
121+
std::vector<std::string>& arguments,
122+
Session* session);
230123

231124
private:
232125
et::event_loop& loop;
233126
et::ipc::JsonPeer& peer;
234-
PathPool& path_pool;
127+
Workspace& workspace;
235128
WorkerPool& pool;
236-
Indexer& indexer;
237-
const CliceConfig& config;
238-
CompilationDatabase& cdb;
239-
DependencyGraph& dep_graph;
240-
241-
/// Open document state, keyed by server-level path_id.
242-
llvm::DenseMap<std::uint32_t, DocumentState> documents;
243-
244-
/// PCH/PCM cache state.
245-
llvm::DenseMap<std::uint32_t, PCHState> pch_states;
246-
llvm::DenseMap<std::uint32_t, PCMState> pcm_states;
247-
llvm::DenseMap<std::uint32_t, std::string> pcm_paths;
248-
249-
/// Module compilation ordering.
250-
llvm::DenseMap<std::uint32_t, std::string> path_to_module;
251-
std::unique_ptr<CompileGraph> compile_graph;
252-
253-
/// Per-file compilation state.
254-
llvm::DenseMap<std::uint32_t, DepsSnapshot> ast_deps;
255-
llvm::DenseMap<std::uint32_t, HeaderFileContext> header_file_contexts;
256-
llvm::DenseMap<std::uint32_t, std::uint32_t> active_contexts;
129+
llvm::DenseMap<std::uint32_t, Session>& sessions;
257130
};
258131

259132
} // namespace clice

0 commit comments

Comments
 (0)