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>
This commit is contained in:
ykiko
2026-04-08 14:03:39 +08:00
committed by GitHub
parent bb0b160a28
commit 9c9e6b0bcb
17 changed files with 2038 additions and 1669 deletions

View File

@@ -3,7 +3,10 @@
#include <algorithm>
#include <format>
#include <string>
#include <type_traits>
#include <variant>
#include "eventide/ipc/lsp/position.h"
#include "eventide/ipc/lsp/protocol.h"
#include "eventide/ipc/lsp/uri.h"
#include "eventide/reflection/enum.h"
@@ -34,8 +37,19 @@ static serde_raw to_raw(const T& value) {
}
MasterServer::MasterServer(et::event_loop& loop, et::ipc::JsonPeer& peer, std::string self_path) :
loop(loop), peer(peer), pool(loop), indexer(path_pool),
compiler(loop, peer, path_pool, pool, indexer, config, cdb, dependency_graph),
loop(loop), peer(peer), pool(loop), compiler(loop, peer, workspace, pool, sessions),
indexer(loop,
workspace,
sessions,
pool,
compiler,
[this](uint32_t proj_path_id) {
// Bridge project-level path_id to server-level path_id.
// The two PathPools may assign different IDs to the same path.
auto path = workspace.project_index.path_pool.path(proj_path_id);
auto server_id = workspace.path_pool.intern(path);
return sessions.contains(server_id);
}),
self_path(std::move(self_path)) {}
MasterServer::~MasterServer() = default;
@@ -44,16 +58,18 @@ et::task<> MasterServer::load_workspace() {
if(workspace_root.empty())
co_return;
if(!config.cache_dir.empty()) {
auto ec = llvm::sys::fs::create_directories(config.cache_dir);
if(!workspace.config.cache_dir.empty()) {
auto ec = llvm::sys::fs::create_directories(workspace.config.cache_dir);
if(ec) {
LOG_WARN("Failed to create cache directory {}: {}", config.cache_dir, ec.message());
LOG_WARN("Failed to create cache directory {}: {}",
workspace.config.cache_dir,
ec.message());
} else {
LOG_INFO("Cache directory: {}", config.cache_dir);
LOG_INFO("Cache directory: {}", workspace.config.cache_dir);
}
for(auto* subdir: {"cache/pch", "cache/pcm"}) {
auto dir = path::join(config.cache_dir, subdir);
auto dir = path::join(workspace.config.cache_dir, subdir);
auto ec2 = llvm::sys::fs::create_directories(dir);
if(ec2) {
LOG_WARN("Failed to create {}: {}", dir, ec2.message());
@@ -62,17 +78,17 @@ et::task<> MasterServer::load_workspace() {
// Clean up stale files first, then load — load_cache() only restores
// entries still listed in cache.json, so cleanup won't delete live files.
compiler.cleanup_cache();
compiler.load_cache();
workspace.cleanup_cache();
workspace.load_cache();
}
std::string cdb_path;
if(!config.compile_commands_path.empty()) {
if(llvm::sys::fs::exists(config.compile_commands_path)) {
cdb_path = config.compile_commands_path;
if(!workspace.config.compile_commands_path.empty()) {
if(llvm::sys::fs::exists(workspace.config.compile_commands_path)) {
cdb_path = workspace.config.compile_commands_path;
} else {
LOG_WARN("Configured compile_commands_path not found: {}",
config.compile_commands_path);
workspace.config.compile_commands_path);
}
}
@@ -91,11 +107,11 @@ et::task<> MasterServer::load_workspace() {
co_return;
}
auto count = cdb.load(cdb_path);
auto count = workspace.cdb.load(cdb_path);
LOG_INFO("Loaded CDB from {} with {} entries", cdb_path, count);
auto report = scan_dependency_graph(cdb, path_pool, dependency_graph);
dependency_graph.build_reverse_map();
auto report = scan_dependency_graph(workspace.cdb, workspace.path_pool, workspace.dep_graph);
workspace.dep_graph.build_reverse_map();
auto unresolved = report.includes_found - report.includes_resolved;
double accuracy =
@@ -117,95 +133,21 @@ et::task<> MasterServer::load_workspace() {
LOG_WARN("{} unresolved includes", unresolved);
}
compiler.build_module_map();
indexer.load(config.index_dir);
workspace.build_module_map();
indexer.load(workspace.config.index_dir);
if(config.enable_indexing) {
for(auto& entry: cdb.get_entries()) {
auto file = cdb.resolve_path(entry.file);
auto server_id = path_pool.intern(file);
index_queue.push_back(server_id);
}
if(!index_queue.empty()) {
LOG_INFO("Queued {} files for background indexing", index_queue.size());
schedule_indexing();
if(workspace.config.enable_indexing) {
for(auto& entry: workspace.cdb.get_entries()) {
auto file = workspace.cdb.resolve_path(entry.file);
auto server_id = workspace.path_pool.intern(file);
indexer.enqueue(server_id);
}
indexer.schedule();
}
compiler.init_compile_graph();
}
void MasterServer::schedule_indexing() {
if(!config.enable_indexing || indexing_active || indexing_scheduled)
return;
indexing_scheduled = true;
if(!index_idle_timer) {
index_idle_timer = std::make_shared<et::timer>(et::timer::create(loop));
}
index_idle_timer->start(std::chrono::milliseconds(config.idle_timeout_ms));
loop.schedule(run_background_indexing());
}
et::task<> MasterServer::run_background_indexing() {
if(index_idle_timer) {
co_await index_idle_timer->wait();
}
indexing_scheduled = false;
if(index_queue_pos >= index_queue.size()) {
LOG_DEBUG("Background indexing: queue exhausted");
co_return;
}
indexing_active = true;
std::size_t processed = 0;
while(index_queue_pos < index_queue.size()) {
auto server_path_id = index_queue[index_queue_pos];
index_queue_pos++;
auto file_path = std::string(path_pool.resolve(server_path_id));
/// Skip open files — their index comes from the stateful worker.
if(compiler.is_file_open(server_path_id)) {
continue;
}
if(!indexer.need_update(file_path))
continue;
worker::BuildParams params;
params.kind = worker::BuildKind::Index;
params.file = file_path;
if(!compiler.fill_compile_args(file_path, params.directory, params.arguments))
continue;
compiler.fill_pcm_deps(params.pcms);
LOG_INFO("Background indexing: {}", file_path);
auto result = co_await pool.send_stateless(params);
if(result.has_value() && result.value().success && !result.value().tu_index_data.empty()) {
LOG_INFO("Background indexing got TUIndex for {}: {} bytes",
file_path,
result.value().tu_index_data.size());
indexer.merge(result.value().tu_index_data.data(), result.value().tu_index_data.size());
++processed;
} else if(result.has_value() && !result.value().success) {
LOG_WARN("Background index failed for {}: {}", file_path, result.value().error);
} else if(result.has_value() && result.value().tu_index_data.empty()) {
LOG_WARN("Background index returned empty TUIndex for {}", file_path);
} else {
LOG_WARN("Background index IPC error for {}: {}", file_path, result.error().message);
}
}
indexing_active = false;
LOG_INFO("Background indexing complete: {} files processed", processed);
indexer.save(config.index_dir);
}
void MasterServer::register_handlers() {
using StringVec = std::vector<std::string>;
@@ -217,7 +159,7 @@ void MasterServer::register_handlers() {
auto& init = params.lsp__initialize_params;
if(init.root_uri.has_value()) {
workspace_root = Compiler::uri_to_path(*init.root_uri);
workspace_root = uri_to_path(*init.root_uri);
}
lifecycle = ServerLifecycle::Initialized;
@@ -300,27 +242,27 @@ void MasterServer::register_handlers() {
});
peer.on_notification([this](const protocol::InitializedParams& params) {
config = CliceConfig::load_from_workspace(workspace_root);
workspace.config = CliceConfig::load_from_workspace(workspace_root);
if(!config.logging_dir.empty()) {
if(!workspace.config.logging_dir.empty()) {
auto now = std::chrono::system_clock::now();
auto pid = llvm::sys::Process::getProcessId();
auto session_dir =
path::join(config.logging_dir, std::format("{:%Y-%m-%d_%H-%M-%S}_{}", now, pid));
auto session_dir = path::join(workspace.config.logging_dir,
std::format("{:%Y-%m-%d_%H-%M-%S}_{}", now, pid));
logging::file_logger("master", session_dir, logging::options);
session_log_dir = session_dir;
}
LOG_INFO("Server ready (stateful={}, stateless={}, idle={}ms)",
config.stateful_worker_count,
config.stateless_worker_count,
config.idle_timeout_ms);
workspace.config.stateful_worker_count,
workspace.config.stateless_worker_count,
workspace.config.idle_timeout_ms);
WorkerPoolOptions pool_opts;
pool_opts.self_path = self_path;
pool_opts.stateful_count = config.stateful_worker_count;
pool_opts.stateless_count = config.stateless_worker_count;
pool_opts.worker_memory_limit = config.worker_memory_limit;
pool_opts.stateful_count = workspace.config.stateful_worker_count;
pool_opts.stateless_count = workspace.config.stateless_worker_count;
pool_opts.worker_memory_limit = workspace.config.worker_memory_limit;
pool_opts.log_dir = session_log_dir;
if(!pool.start(pool_opts)) {
LOG_ERROR("Failed to start worker pool");
@@ -330,7 +272,7 @@ void MasterServer::register_handlers() {
lifecycle = ServerLifecycle::Ready;
compiler.on_indexing_needed = [this]() {
schedule_indexing();
indexer.schedule();
};
loop.schedule(load_workspace());
@@ -348,8 +290,8 @@ void MasterServer::register_handlers() {
lifecycle = ServerLifecycle::Exited;
LOG_INFO("Exit notification received");
indexer.save(config.index_dir);
compiler.save_cache();
indexer.save(workspace.config.index_dir);
workspace.save_cache();
loop.schedule([this]() -> et::task<> {
co_await pool.stop();
@@ -357,105 +299,225 @@ void MasterServer::register_handlers() {
}());
});
/// Document lifecycle — delegate to Compiler.
/// Document lifecycle — handled directly by MasterServer.
peer.on_notification([this](const protocol::DidOpenTextDocumentParams& params) {
if(lifecycle != ServerLifecycle::Ready)
return;
compiler.open_document(params.text_document.uri,
params.text_document.text,
params.text_document.version);
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto [it, _] = sessions.try_emplace(path_id);
auto& session = it->second;
session.path_id = path_id;
session.version = params.text_document.version;
session.text = params.text_document.text;
session.generation++;
LOG_DEBUG("didOpen: {} (v{})", path, params.text_document.version);
});
peer.on_notification([this](const protocol::DidChangeTextDocumentParams& params) {
if(lifecycle != ServerLifecycle::Ready)
return;
compiler.apply_changes(params);
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto it = sessions.find(path_id);
if(it == sessions.end())
return;
auto& session = it->second;
session.version = params.text_document.version;
for(auto& change: params.content_changes) {
std::visit(
[&](auto& c) {
using T = std::remove_cvref_t<decltype(c)>;
if constexpr(std::is_same_v<T,
protocol::TextDocumentContentChangeWholeDocument>) {
session.text = c.text;
} else {
auto& range = c.range;
lsp::PositionMapper mapper(session.text, lsp::PositionEncoding::UTF16);
auto start = mapper.to_offset(range.start);
auto end = mapper.to_offset(range.end);
if(start && end && *start <= *end) {
session.text.replace(*start, *end - *start, c.text);
}
}
},
change);
}
session.generation++;
session.ast_dirty = true;
LOG_DEBUG("didChange: path={} version={} gen={}",
path,
session.version,
session.generation);
worker::DocumentUpdateParams update;
update.path = path;
update.version = session.version;
pool.notify_stateful(path_id, update);
});
peer.on_notification([this](const protocol::DidCloseTextDocumentParams& params) {
if(lifecycle != ServerLifecycle::Ready)
return;
auto path_id = compiler.close_document(params.text_document.uri);
index_queue.push_back(path_id);
schedule_indexing();
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
workspace.on_file_closed(path_id);
pool.notify_stateful(path_id, worker::EvictParams{path});
// Clear diagnostics for the closed file.
protocol::PublishDiagnosticsParams diag_params;
diag_params.uri = params.text_document.uri;
peer.send_notification(diag_params);
sessions.erase(path_id);
indexer.enqueue(path_id);
indexer.schedule();
LOG_DEBUG("didClose: {}", path);
});
peer.on_notification([this](const protocol::DidSaveTextDocumentParams& params) {
if(lifecycle != ServerLifecycle::Ready)
return;
auto to_index = compiler.on_save(params.text_document.uri);
for(auto id: to_index)
index_queue.push_back(id);
schedule_indexing();
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto dirtied = workspace.on_file_saved(path_id);
for(auto dirty_id: dirtied) {
if(auto sit = sessions.find(dirty_id); sit != sessions.end()) {
sit->second.ast_dirty = true;
} else {
indexer.enqueue(dirty_id);
}
}
// Invalidate header contexts for sessions whose host is this file.
for(auto& [hdr_id, session]: sessions) {
if(session.header_context && session.header_context->host_path_id == path_id) {
session.header_context.reset();
session.ast_dirty = true;
}
}
indexer.schedule();
LOG_DEBUG("didSave: {}", path);
});
/// Feature requests — stateful forwarding.
peer.on_request([this](RequestContext& ctx, const protocol::HoverParams& params) -> RawResult {
co_return co_await compiler.forward_query(
worker::QueryKind::Hover,
params.text_document_position_params.text_document.uri,
params.text_document_position_params.position);
auto path = uri_to_path(params.text_document_position_params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_query(worker::QueryKind::Hover,
sit->second,
params.text_document_position_params.position);
});
peer.on_request([this](RequestContext& ctx,
const protocol::SemanticTokensParams& params) -> RawResult {
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_query(worker::QueryKind::SemanticTokens, sit->second);
});
peer.on_request(
[this](RequestContext& ctx, const protocol::SemanticTokensParams& params) -> RawResult {
co_return co_await compiler.forward_query(worker::QueryKind::SemanticTokens,
params.text_document.uri);
});
peer.on_request(
[this](RequestContext& ctx, const protocol::InlayHintParams& params) -> RawResult {
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_query(worker::QueryKind::InlayHints,
params.text_document.uri);
sit->second,
{},
params.range);
});
peer.on_request(
[this](RequestContext& ctx, const protocol::FoldingRangeParams& params) -> RawResult {
co_return co_await compiler.forward_query(worker::QueryKind::FoldingRange,
params.text_document.uri);
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_query(worker::QueryKind::FoldingRange, sit->second);
});
peer.on_request(
[this](RequestContext& ctx, const protocol::DocumentSymbolParams& params) -> RawResult {
co_return co_await compiler.forward_query(worker::QueryKind::DocumentSymbol,
params.text_document.uri);
});
peer.on_request([this](RequestContext& ctx,
const protocol::DocumentSymbolParams& params) -> RawResult {
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_query(worker::QueryKind::DocumentSymbol, sit->second);
});
peer.on_request(
[this](RequestContext& ctx, const protocol::DocumentLinkParams& params) -> RawResult {
co_return co_await compiler.forward_query(worker::QueryKind::DocumentLink,
params.text_document.uri);
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_query(worker::QueryKind::DocumentLink, sit->second);
});
peer.on_request(
[this](RequestContext& ctx, const protocol::CodeActionParams& params) -> RawResult {
co_return co_await compiler.forward_query(worker::QueryKind::CodeAction,
params.text_document.uri);
auto path = uri_to_path(params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_query(worker::QueryKind::CodeAction, sit->second);
});
/// Resolve URI to the context needed for index queries.
/// Helper: resolve URI to path, path_id, and Session pointer.
auto resolve_uri = [this](const std::string& uri) {
struct Result {
std::string path;
std::uint32_t path_id;
const std::string* doc_text;
Session* session;
};
auto path = Compiler::uri_to_path(uri);
auto path_id = path_pool.intern(path);
auto* doc = compiler.get_document(path_id);
return Result{std::move(path), path_id, doc ? &doc->text : nullptr};
auto path = uri_to_path(uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
Session* session = (sit != sessions.end()) ? &sit->second : nullptr;
return Result{std::move(path), path_id, session};
};
auto lookup_at = [this, resolve_uri](const std::string& uri, const protocol::Position& pos) {
auto [path, path_id, doc_text] = resolve_uri(uri);
return indexer.lookup_symbol(uri, path, path_id, pos, doc_text);
auto [path, path_id, session] = resolve_uri(uri);
return indexer.lookup_symbol(uri, path, pos, session);
};
auto query_at = [this, resolve_uri](const std::string& uri,
const protocol::Position& pos,
RelationKind kind) -> std::vector<protocol::Location> {
auto [path, path_id, doc_text] = resolve_uri(uri);
return indexer.query_relations(path, path_id, pos, kind, doc_text);
auto [path, path_id, session] = resolve_uri(uri);
return indexer.query_relations(path, pos, kind, session);
};
auto resolve_item =
@@ -463,11 +525,12 @@ void MasterServer::register_handlers() {
resolve_uri](const std::string& uri,
const protocol::Range& range,
const std::optional<protocol::LSPAny>& data) -> std::optional<SymbolInfo> {
auto [path, path_id, doc_text] = resolve_uri(uri);
return indexer.resolve_hierarchy_item(uri, path, path_id, range, data, doc_text);
auto [path, path_id, session] = resolve_uri(uri);
return indexer.resolve_hierarchy_item(uri, path, range, data, session);
};
/// Feature requests — index-based with AST fallback.
peer.on_request([this, query_at](RequestContext& ctx,
const protocol::DefinitionParams& params) -> RawResult {
auto& uri = params.text_document_position_params.text_document.uri;
@@ -478,7 +541,14 @@ void MasterServer::register_handlers() {
co_return to_raw(result);
}
co_return co_await compiler.forward_query(worker::QueryKind::GoToDefinition, uri, pos);
auto path = uri_to_path(uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_query(worker::QueryKind::GoToDefinition,
sit->second,
pos);
});
peer.on_request([this, query_at](RequestContext& ctx,
@@ -516,22 +586,32 @@ void MasterServer::register_handlers() {
});
/// Feature requests — stateless forwarding.
peer.on_request(
[this](RequestContext& ctx, const protocol::CompletionParams& params) -> RawResult {
co_return co_await compiler.handle_completion(
params.text_document_position_params.text_document.uri,
params.text_document_position_params.position);
});
peer.on_request([this](RequestContext& ctx,
const protocol::CompletionParams& params) -> RawResult {
auto path = uri_to_path(params.text_document_position_params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.handle_completion(params.text_document_position_params.position,
sit->second);
});
peer.on_request(
[this](RequestContext& ctx, const protocol::SignatureHelpParams& params) -> RawResult {
co_return co_await compiler.forward_build(
worker::BuildKind::SignatureHelp,
params.text_document_position_params.text_document.uri,
params.text_document_position_params.position);
auto path = uri_to_path(params.text_document_position_params.text_document.uri);
auto path_id = workspace.path_pool.intern(path);
auto sit = sessions.find(path_id);
if(sit == sessions.end())
co_return serde_raw{"null"};
co_return co_await compiler.forward_build(worker::BuildKind::SignatureHelp,
params.text_document_position_params.position,
sit->second);
});
/// Hierarchy queries — index-based.
peer.on_request(
[this, lookup_at](RequestContext& ctx,
const protocol::CallHierarchyPrepareParams& params) -> RawResult {
@@ -624,21 +704,22 @@ void MasterServer::register_handlers() {
});
/// clice/ extension commands.
peer.on_request(
"clice/queryContext",
[this](RequestContext& ctx, const ext::QueryContextParams& params) -> RawResult {
auto path = Compiler::uri_to_path(params.uri);
auto path_id = path_pool.intern(path);
auto path = uri_to_path(params.uri);
auto path_id = workspace.path_pool.intern(path);
int offset_val = std::max(0, params.offset.value_or(0));
constexpr int page_size = 10;
ext::QueryContextResult result;
std::vector<ext::ContextItem> all_items;
auto hosts = dependency_graph.find_host_sources(path_id);
auto hosts = workspace.dep_graph.find_host_sources(path_id);
for(auto host_id: hosts) {
auto host_path = path_pool.resolve(host_id);
auto host_cdb = cdb.lookup(host_path, {.suppress_logging = true});
auto host_path = workspace.path_pool.resolve(host_id);
auto host_cdb = workspace.cdb.lookup(host_path, {.suppress_logging = true});
if(host_cdb.empty())
continue;
auto host_uri_opt = lsp::URI::from_file_path(std::string(host_path));
@@ -652,7 +733,7 @@ void MasterServer::register_handlers() {
}
if(hosts.empty()) {
auto entries = cdb.lookup(path, {.suppress_logging = true});
auto entries = workspace.cdb.lookup(path, {.suppress_logging = true});
for(std::size_t i = 0; i < entries.size(); ++i) {
auto& entry = entries[i];
std::string desc;
@@ -693,13 +774,13 @@ void MasterServer::register_handlers() {
peer.on_request(
"clice/currentContext",
[this](RequestContext& ctx, const ext::CurrentContextParams& params) -> RawResult {
auto path = Compiler::uri_to_path(params.uri);
auto path_id = path_pool.intern(path);
auto path = uri_to_path(params.uri);
auto path_id = workspace.path_pool.intern(path);
ext::CurrentContextResult result;
auto active_ctx = compiler.get_active_context(path_id);
if(active_ctx) {
auto ctx_path = path_pool.resolve(*active_ctx);
auto sit = sessions.find(path_id);
if(sit != sessions.end() && sit->second.active_context) {
auto ctx_path = workspace.path_pool.resolve(*sit->second.active_context);
auto ctx_uri_opt = lsp::URI::from_file_path(std::string(ctx_path));
if(ctx_uri_opt) {
ext::ContextItem item;
@@ -715,20 +796,31 @@ void MasterServer::register_handlers() {
peer.on_request(
"clice/switchContext",
[this](RequestContext& ctx, const ext::SwitchContextParams& params) -> RawResult {
auto path = Compiler::uri_to_path(params.uri);
auto path_id = path_pool.intern(path);
auto context_path = Compiler::uri_to_path(params.context_uri);
auto context_path_id = path_pool.intern(context_path);
auto path = uri_to_path(params.uri);
auto path_id = workspace.path_pool.intern(path);
auto context_path = uri_to_path(params.context_uri);
auto context_path_id = workspace.path_pool.intern(context_path);
ext::SwitchContextResult result;
auto context_cdb = cdb.lookup(context_path, {.suppress_logging = true});
auto context_cdb = workspace.cdb.lookup(context_path, {.suppress_logging = true});
if(context_cdb.empty()) {
result.success = false;
co_return to_raw(result);
}
compiler.switch_context(path_id, context_path_id);
auto sit = sessions.find(path_id);
if(sit == sessions.end()) {
result.success = false;
co_return to_raw(result);
}
sit->second.active_context = context_path_id;
sit->second.header_context.reset();
sit->second.pch_ref.reset();
sit->second.ast_deps.reset();
sit->second.ast_dirty = true;
result.success = true;
co_return to_raw(result);
});