{"ts":8,"msg":"{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"initialize\",\"params\":{\"processId\":1707078,\"clientInfo\":{\"name\":\"Visual Studio Code\",\"version\":\"1.114.0\"},\"locale\":\"en\",\"rootPath\":\"/home/ykiko/C++/clice/tests/data/hello_world\",\"rootUri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world\",\"capabilities\":{\"workspace\":{\"applyEdit\":true,\"workspaceEdit\":{\"documentChanges\":true,\"resourceOperations\":[\"create\",\"rename\",\"delete\"],\"failureHandling\":\"textOnlyTransactional\",\"normalizesLineEndings\":true,\"changeAnnotationSupport\":{\"groupsOnLabel\":true}},\"configuration\":true,\"didChangeWatchedFiles\":{\"dynamicRegistration\":true,\"relativePatternSupport\":true},\"symbol\":{\"dynamicRegistration\":true,\"symbolKind\":{\"valueSet\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},\"tagSupport\":{\"valueSet\":[1]},\"resolveSupport\":{\"properties\":[\"location.range\"]}},\"codeLens\":{\"refreshSupport\":true},\"executeCommand\":{\"dynamicRegistration\":true},\"didChangeConfiguration\":{\"dynamicRegistration\":true},\"workspaceFolders\":true,\"foldingRange\":{\"refreshSupport\":true},\"semanticTokens\":{\"refreshSupport\":true},\"fileOperations\":{\"dynamicRegistration\":true,\"didCreate\":true,\"didRename\":true,\"didDelete\":true,\"willCreate\":true,\"willRename\":true,\"willDelete\":true},\"inlineValue\":{\"refreshSupport\":true},\"inlayHint\":{\"refreshSupport\":true},\"diagnostics\":{\"refreshSupport\":true}},\"textDocument\":{\"publishDiagnostics\":{\"relatedInformation\":true,\"versionSupport\":false,\"tagSupport\":{\"valueSet\":[1,2]},\"codeDescriptionSupport\":true,\"dataSupport\":true},\"synchronization\":{\"dynamicRegistration\":true,\"willSave\":true,\"willSaveWaitUntil\":true,\"didSave\":true},\"completion\":{\"dynamicRegistration\":true,\"contextSupport\":true,\"completionItem\":{\"snippetSupport\":true,\"commitCharactersSupport\":true,\"documentationFormat\":[\"markdown\",\"plaintext\"],\"deprecatedSupport\":true,\"preselectSupport\":true,\"tagSupport\":{\"valueSet\":[1]},\"insertReplaceSupport\":true,\"resolveSupport\":{\"properties\":[\"documentation\",\"detail\",\"additionalTextEdits\"]},\"insertTextModeSupport\":{\"valueSet\":[1,2]},\"labelDetailsSupport\":true},\"insertTextMode\":2,\"completionItemKind\":{\"valueSet\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]},\"completionList\":{\"itemDefaults\":[\"commitCharacters\",\"editRange\",\"insertTextFormat\",\"insertTextMode\",\"data\"]}},\"hover\":{\"dynamicRegistration\":true,\"contentFormat\":[\"markdown\",\"plaintext\"]},\"signatureHelp\":{\"dynamicRegistration\":true,\"signatureInformation\":{\"documentationFormat\":[\"markdown\",\"plaintext\"],\"parameterInformation\":{\"labelOffsetSupport\":true},\"activeParameterSupport\":true},\"contextSupport\":true},\"definition\":{\"dynamicRegistration\":true,\"linkSupport\":true},\"references\":{\"dynamicRegistration\":true},\"documentHighlight\":{\"dynamicRegistration\":true},\"documentSymbol\":{\"dynamicRegistration\":true,\"symbolKind\":{\"valueSet\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},\"hierarchicalDocumentSymbolSupport\":true,\"tagSupport\":{\"valueSet\":[1]},\"labelSupport\":true},\"codeAction\":{\"dynamicRegistration\":true,\"isPreferredSupport\":true,\"disabledSupport\":true,\"dataSupport\":true,\"resolveSupport\":{\"properties\":[\"edit\"]},\"codeActionLiteralSupport\":{\"codeActionKind\":{\"valueSet\":[\"\",\"quickfix\",\"refactor\",\"refactor.extract\",\"refactor.inline\",\"refactor.rewrite\",\"source\",\"source.organizeImports\"]}},\"honorsChangeAnnotations\":true},\"codeLens\":{\"dynamicRegistration\":true},\"formatting\":{\"dynamicRegistration\":true},\"rangeFormatting\":{\"dynamicRegistration\":true,\"rangesSupport\":true},\"onTypeFormatting\":{\"dynamicRegistration\":true},\"rename\":{\"dynamicRegistration\":true,\"prepareSupport\":true,\"prepareSupportDefaultBehavior\":1,\"honorsChangeAnnotations\":true},\"documentLink\":{\"dynamicRegistration\":true,\"tooltipSupport\":true},\"typeDefinition\":{\"dynamicRegistration\":true,\"linkSupport\":true},\"implementation\":{\"dynamicRegistration\":true,\"linkSupport\":true},\"colorProvider\":{\"dynamicRegistration\":true},\"foldingRange\":{\"dynamicRegistration\":true,\"rangeLimit\":5000,\"lineFoldingOnly\":true,\"foldingRangeKind\":{\"valueSet\":[\"comment\",\"imports\",\"region\"]},\"foldingRange\":{\"collapsedText\":false}},\"declaration\":{\"dynamicRegistration\":true,\"linkSupport\":true},\"selectionRange\":{\"dynamicRegistration\":true},\"callHierarchy\":{\"dynamicRegistration\":true},\"semanticTokens\":{\"dynamicRegistration\":true,\"tokenTypes\":[\"namespace\",\"type\",\"class\",\"enum\",\"interface\",\"struct\",\"typeParameter\",\"parameter\",\"variable\",\"property\",\"enumMember\",\"event\",\"function\",\"method\",\"macro\",\"keyword\",\"modifier\",\"comment\",\"string\",\"number\",\"regexp\",\"operator\",\"decorator\"],\"tokenModifiers\":[\"declaration\",\"definition\",\"readonly\",\"static\",\"deprecated\",\"abstract\",\"async\",\"modification\",\"documentation\",\"defaultLibrary\"],\"formats\":[\"relative\"],\"requests\":{\"range\":true,\"full\":{\"delta\":true}},\"multilineTokenSupport\":false,\"overlappingTokenSupport\":false,\"serverCancelSupport\":true,\"augmentsSyntaxTokens\":true},\"linkedEditingRange\":{\"dynamicRegistration\":true},\"typeHierarchy\":{\"dynamicRegistration\":true},\"inlineValue\":{\"dynamicRegistration\":true},\"inlayHint\":{\"dynamicRegistration\":true,\"resolveSupport\":{\"properties\":[\"tooltip\",\"textEdits\",\"label.tooltip\",\"label.location\",\"label.command\"]}},\"diagnostic\":{\"dynamicRegistration\":true,\"relatedDocumentSupport\":false}},\"window\":{\"showMessage\":{\"messageActionItem\":{\"additionalPropertiesSupport\":true}},\"showDocument\":{\"support\":true},\"workDoneProgress\":true},\"general\":{\"staleRequestSupport\":{\"cancel\":true,\"retryOnContentModified\":[\"textDocument/semanticTokens/full\",\"textDocument/semanticTokens/range\",\"textDocument/semanticTokens/full/delta\"]},\"regularExpressions\":{\"engine\":\"ECMAScript\",\"version\":\"ES2020\"},\"markdown\":{\"parser\":\"marked\",\"version\":\"1.1.0\"},\"positionEncodings\":[\"utf-16\"]},\"notebookDocument\":{\"synchronization\":{\"dynamicRegistration\":true,\"executionSummarySupport\":true}}},\"trace\":\"off\",\"workspaceFolders\":[{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world\",\"name\":\"hello_world\"}]}}"}
{"ts":82,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"initialized\",\"params\":{}}"}
{"ts":87,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include <iostream>\\n\\nint add(int a, int b) {\\n    return a + b;\\n}\\n\\nint main() {\\n    std::cout << \\\"hello world\\\" << std::endl;\\n\\n    int result = add(1, 2);\\n    return result;\\n}\\n\"}}}"}
{"ts":144,"msg":"{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":144,"msg":"{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":9,\"character\":27},\"end\":{\"line\":9,\"character\":27}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":144,"msg":"{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"textDocument/documentLink\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":144,"msg":"{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":12,\"character\":0}}}}"}
{"ts":226,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":4}}"}
{"ts":226,"msg":"{\"jsonrpc\":\"2.0\",\"id\":5,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":12,\"character\":0}}}}"}
{"ts":342,"msg":"{\"jsonrpc\":\"2.0\",\"id\":6,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":12,\"character\":0}}}}"}
{"ts":405,"msg":"{\"jsonrpc\":\"2.0\",\"id\":7,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":406,"msg":"{\"jsonrpc\":\"2.0\",\"id\":8,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":438,"msg":"{\"jsonrpc\":\"2.0\",\"id\":9,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":15}}}"}
{"ts":578,"msg":"{\"jsonrpc\":\"2.0\",\"id\":10,\"method\":\"textDocument/semanticTokens/full\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":842,"msg":"{\"jsonrpc\":\"2.0\",\"id\":11,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":19}}}"}
{"ts":1478,"msg":"{\"jsonrpc\":\"2.0\",\"id\":12,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":19}}}"}
{"ts":2350,"msg":"{\"jsonrpc\":\"2.0\",\"id\":13,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":25}}}"}
{"ts":2639,"msg":"{\"jsonrpc\":\"2.0\",\"id\":14,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":12}}}"}
{"ts":3694,"msg":"{\"jsonrpc\":\"2.0\",\"id\":15,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":18},\"end\":{\"line\":10,\"character\":18}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":4741,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/src/compile/compilation_unit.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include \\\"compile/implement.h\\\"\\n#include \\\"index/usr.h\\\"\\n#include \\\"semantic/ast_utility.h\\\"\\n\\nnamespace clice {\\n\\nCompilationKind CompilationUnitRef::kind() {\\n    return self->kind;\\n}\\n\\nCompilationStatus CompilationUnitRef::status() {\\n    return self->status;\\n}\\n\\nauto CompilationUnitRef::file_id(clang::FileEntryRef entry) -> clang::FileID {\\n    return self->SM().translateFile(entry);\\n}\\n\\nauto CompilationUnitRef::file_id(llvm::StringRef file) -> clang::FileID {\\n    auto entry = self->SM().getFileManager().getFileRef(file);\\n    if(entry) {\\n        return file_id(*entry);\\n    }\\n\\n    return clang::FileID();\\n}\\n\\nauto CompilationUnitRef::decompose_location(clang::SourceLocation location)\\n    -> std::pair<clang::FileID, std::uint32_t> {\\n    assert(location.isFileID() && \\\"Decompose macro location is meaningless!\\\");\\n    return self->SM().getDecomposedLoc(location);\\n}\\n\\nauto CompilationUnitRef::decompose_range(clang::SourceRange range)\\n    -> std::pair<clang::FileID, LocalSourceRange> {\\n    auto [begin, end] = range;\\n    assert(begin.isValid() && end.isValid() && \\\"Invalid source range\\\");\\n    assert(begin.isFileID() && end.isValid() && \\\"Input source range should be a file range\\\");\\n\\n    if(begin == end) {\\n        auto [fid, offset] = decompose_location(begin);\\n        return {\\n            fid,\\n            {offset, offset + token_length(end)}\\n        };\\n    } else {\\n        auto [begin_fid, begin_offset] = decompose_location(begin);\\n        auto [end_fid, end_offset] = decompose_location(end);\\n\\n        if(begin_fid == end_fid) {\\n            end_offset += token_length(end);\\n        } else {\\n            auto content = file_content(begin_fid);\\n            end_offset = content.size();\\n        }\\n\\n        return {\\n            begin_fid,\\n            {begin_offset, end_offset}\\n        };\\n    }\\n}\\n\\nauto CompilationUnitRef::decompose_expansion_range(clang::SourceRange range)\\n    -> std::pair<clang::FileID, LocalSourceRange> {\\n    auto [begin, end] = range;\\n    if(begin == end) {\\n        return decompose_range(expansion_location(begin));\\n    } else {\\n        return decompose_range(\\n            clang::SourceRange(expansion_location(begin), expansion_location(end)));\\n    }\\n}\\n\\nauto CompilationUnitRef::file_id(clang::SourceLocation location) -> clang::FileID {\\n    return self->SM().getFileID(location);\\n}\\n\\nauto CompilationUnitRef::file_offset(clang::SourceLocation location) -> std::uint32_t {\\n    return self->SM().getFileOffset(location);\\n}\\n\\nauto CompilationUnitRef::file_path(clang::FileID fid) -> llvm::StringRef {\\n    assert(fid.isValid() && \\\"Invalid fid\\\");\\n    if(auto it = self->path_cache.find(fid); it != self->path_cache.end()) {\\n        return it->second;\\n    }\\n\\n    auto entry = self->SM().getFileEntryRefForID(fid);\\n    if(!entry) {\\n        return {};\\n    }\\n\\n    llvm::SmallString<128> path;\\n\\n    /// Try to get the real path of the file.\\n    auto name = entry->getName();\\n    if(auto error = llvm::sys::fs::real_path(name, path)) {\\n        /// If failed, use the virtual path.\\n        path = name;\\n    }\\n    assert(!path.empty() && \\\"Invalid file path\\\");\\n\\n    /// Allocate the path in the storage.\\n    auto size = path.size();\\n    auto data = self->path_storage.Allocate<char>(size + 1);\\n    memcpy(data, path.data(), size);\\n    data[size] = '\\\\0';\\n\\n    auto [it, inserted] = self->path_cache.try_emplace(fid, llvm::StringRef(data, size));\\n    assert(inserted && \\\"File path already exists\\\");\\n    return it->second;\\n}\\n\\nauto CompilationUnitRef::file_content(clang::FileID fid) -> llvm::StringRef {\\n    return self->SM().getBufferData(fid);\\n}\\n\\nauto CompilationUnitRef::interested_file() -> clang::FileID {\\n    return self->SM().getMainFileID();\\n}\\n\\nauto CompilationUnitRef::interested_content() -> llvm::StringRef {\\n    return file_content(interested_file());\\n}\\n\\nbool CompilationUnitRef::is_builtin_file(clang::FileID fid) {\\n    // No FileEntryRef => built-in/command line/scratch.\\n    if(!self->SM().getFileEntryRefForID(fid)) {\\n        if(auto buffer = self->SM().getBufferOrNone(fid)) {\\n            auto name = buffer->getBufferIdentifier();\\n            return name == \\\"<built-in>\\\" || name == \\\"<command line>\\\" || name == \\\"<scratch space>\\\";\\n        }\\n    }\\n\\n    return false;\\n}\\n\\nauto CompilationUnitRef::start_location(clang::FileID fid) -> clang::SourceLocation {\\n    return self->SM().getLocForStartOfFile(fid);\\n}\\n\\nauto CompilationUnitRef::end_location(clang::FileID fid) -> clang::SourceLocation {\\n    return self->SM().getLocForEndOfFile(fid);\\n}\\n\\nauto CompilationUnitRef::spelling_location(clang::SourceLocation loc) -> clang::SourceLocation {\\n    return self->SM().getSpellingLoc(loc);\\n}\\n\\nauto CompilationUnitRef::expansion_location(clang::SourceLocation location)\\n    -> clang::SourceLocation {\\n    return self->SM().getExpansionLoc(location);\\n}\\n\\nauto CompilationUnitRef::file_location(clang::SourceLocation location) -> clang::SourceLocation {\\n    return self->SM().getFileLoc(location);\\n}\\n\\nauto CompilationUnitRef::include_location(clang::FileID fid) -> clang::SourceLocation {\\n    return self->SM().getIncludeLoc(fid);\\n}\\n\\nauto CompilationUnitRef::presumed_location(clang::SourceLocation location) -> clang::PresumedLoc {\\n    return self->SM().getPresumedLoc(location, false);\\n}\\n\\nauto CompilationUnitRef::create_location(clang::FileID fid, std::uint32_t offset)\\n    -> clang::SourceLocation {\\n    return self->SM().getComposedLoc(fid, offset);\\n}\\n\\nauto CompilationUnitRef::spelled_tokens(clang::FileID fid) -> TokenRange {\\n    return self->buffer->spelledTokens(fid);\\n}\\n\\nauto CompilationUnitRef::spelled_tokens(clang::SourceRange range) -> TokenRange {\\n    auto tokens = self->buffer->spelledForExpanded(self->buffer->expandedTokens(range));\\n    if(!tokens) {\\n        return {};\\n    }\\n\\n    return *tokens;\\n}\\n\\nauto CompilationUnitRef::spelled_tokens_touch(clang::SourceLocation location) -> TokenRange {\\n    return clang::syntax::spelledTokensTouching(location, *self->buffer);\\n}\\n\\nauto CompilationUnitRef::expanded_tokens() -> TokenRange {\\n    return self->buffer->expandedTokens();\\n}\\n\\nauto CompilationUnitRef::expanded_tokens(clang::SourceRange range) -> TokenRange {\\n    return self->buffer->expandedTokens(range);\\n}\\n\\nauto CompilationUnitRef::expansions_overlapping(TokenRange spelled_tokens)\\n    -> std::vector<clang::syntax::TokenBuffer::Expansion> {\\n    return self->buffer->expansionsOverlapping(spelled_tokens);\\n}\\n\\nauto CompilationUnitRef::token_length(clang::SourceLocation location) -> std::uint32_t {\\n    return clang::Lexer::MeasureTokenLength(location, self->SM(), self->instance->getLangOpts());\\n}\\n\\nauto CompilationUnitRef::token_spelling(clang::SourceLocation location) -> llvm::StringRef {\\n    return llvm::StringRef(self->SM().getCharacterData(location), token_length(location));\\n}\\n\\nauto CompilationUnitRef::module_name() -> llvm::StringRef {\\n    return self->instance->getPreprocessor().getNamedModuleName();\\n}\\n\\nbool CompilationUnitRef::is_module_interface_unit() {\\n    return self->instance->getPreprocessor().isInNamedInterfaceUnit();\\n}\\n\\nauto CompilationUnitRef::diagnostics() -> std::vector<Diagnostic>& {\\n    return self->diagnostics;\\n}\\n\\nauto CompilationUnitRef::top_level_decls() -> llvm::ArrayRef<clang::Decl*> {\\n    return self->top_level_decls;\\n}\\n\\nstd::chrono::milliseconds CompilationUnitRef::build_at() {\\n    return self->build_at;\\n}\\n\\nstd::chrono::milliseconds CompilationUnitRef::build_duration() {\\n    return self->build_duration;\\n}\\n\\nclang::LangOptions& CompilationUnitRef::lang_options() {\\n    return self->instance->getLangOpts();\\n}\\n\\nstd::vector<std::string> CompilationUnitRef::deps() {\\n    llvm::StringSet<> deps;\\n\\n    /// FIXME: consider `#embed` and `__has_embed`.\\n\\n    for(auto& [fid, directive]: directives()) {\\n        for(auto& include: directive.includes) {\\n            if(!include.skipped) {\\n                auto path = file_path(include.fid);\\n                if(!path.empty()) {\\n                    deps.try_emplace(path);\\n                }\\n            }\\n        }\\n\\n        for(auto& has_include: directive.has_includes) {\\n            if(has_include.fid.isValid()) {\\n                auto path = file_path(has_include.fid);\\n                if(!path.empty()) {\\n                    deps.try_emplace(path);\\n                }\\n            }\\n        }\\n    }\\n\\n    std::vector<std::string> result;\\n\\n    for(auto& deps: deps) {\\n        result.emplace_back(deps.getKey().str());\\n    }\\n\\n    return result;\\n}\\n\\nindex::SymbolID CompilationUnitRef::getSymbolID(const clang::NamedDecl* decl) {\\n    uint64_t hash;\\n    auto iter = self->symbol_hash_cache.find(decl);\\n    if(iter != self->symbol_hash_cache.end()) {\\n        hash = iter->second;\\n    } else {\\n        llvm::SmallString<128> usr;\\n        index::generateUSRForDecl(decl, usr);\\n        hash = llvm::xxh3_64bits(usr);\\n        self->symbol_hash_cache.try_emplace(decl, hash);\\n    }\\n    return index::SymbolID{hash, ast::name_of(decl)};\\n}\\n\\nindex::SymbolID CompilationUnitRef::getSymbolID(const clang::MacroInfo* macro) {\\n    std::uint64_t hash;\\n    auto name = token_spelling(macro->getDefinitionLoc());\\n    auto iter = self->symbol_hash_cache.find(macro);\\n    if(iter != self->symbol_hash_cache.end()) {\\n        hash = iter->second;\\n    } else {\\n        llvm::SmallString<128> usr;\\n        index::generateUSRForMacro(name, macro->getDefinitionLoc(), self->SM(), usr);\\n        hash = llvm::xxh3_64bits(usr);\\n        self->symbol_hash_cache.try_emplace(macro, hash);\\n    }\\n    return index::SymbolID{hash, name.str()};\\n}\\n\\nconst llvm::DenseSet<clang::FileID>& CompilationUnitRef::files() {\\n    if(self->all_files.empty()) {\\n        /// FIXME: handle preamble and embed file id.\\n        for(auto& [fid, directive]: directives()) {\\n            for(auto& include: directive.includes) {\\n                if(!include.skipped && include.fid.isValid()) {\\n                    self->all_files.insert(include.fid);\\n                }\\n            }\\n        }\\n        self->all_files.insert(self->SM().getMainFileID());\\n    }\\n    return self->all_files;\\n}\\n\\nclang::TranslationUnitDecl* CompilationUnitRef::tu() {\\n    return self->instance->getASTContext().getTranslationUnitDecl();\\n}\\n\\nllvm::DenseMap<clang::FileID, Directive>& CompilationUnitRef::directives() {\\n    return self->directives;\\n}\\n\\nTemplateResolver& CompilationUnitRef::resolver() {\\n    assert(self->resolver && \\\"Template resolver is not available\\\");\\n    return *self->resolver;\\n}\\n\\nclang::ASTContext& CompilationUnitRef::context() {\\n    return self->instance->getASTContext();\\n}\\n\\nclang::syntax::TokenBuffer& CompilationUnitRef::token_buffer() {\\n    return *self->buffer;\\n}\\n\\nCompilationUnit::~CompilationUnit() {\\n    delete self;\\n}\\n\\n}  // namespace clice\\n\"}}}"}
{"ts":4741,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/src/server/config.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include \\\"server/config.h\\\"\\n\\n#include <algorithm>\\n#include <thread>\\n\\n#include \\\"eventide/serde/toml.h\\\"\\n#include \\\"support/filesystem.h\\\"\\n#include \\\"support/logging.h\\\"\\n\\nnamespace clice {\\n\\n/// Replace all occurrences of ${workspace} with the workspace root.\\nstatic void substitute_workspace(std::string& value, const std::string& workspace_root) {\\n    constexpr std::string_view placeholder = \\\"${workspace}\\\";\\n    std::string::size_type pos = 0;\\n    while((pos = value.find(placeholder, pos)) != std::string::npos) {\\n        value.replace(pos, placeholder.size(), workspace_root);\\n        pos += workspace_root.size();\\n    }\\n}\\n\\nvoid CliceConfig::apply_defaults(const std::string& workspace_root) {\\n    auto cpu_count = std::thread::hardware_concurrency();\\n    if(cpu_count == 0)\\n        cpu_count = 4;\\n\\n    if(stateful_worker_count == 0) {\\n        stateful_worker_count = 2;\\n    }\\n    if(stateless_worker_count == 0) {\\n        stateless_worker_count = 3;\\n    }\\n    if(worker_memory_limit == 0) {\\n        worker_memory_limit = 4ULL * 1024 * 1024 * 1024;  // 4GB default\\n    }\\n    if(cache_dir.empty() && !workspace_root.empty()) {\\n        cache_dir = path::join(workspace_root, \\\".clice\\\");\\n    }\\n\\n    if(index_dir.empty() && !cache_dir.empty()) {\\n        index_dir = path::join(cache_dir, \\\"index\\\");\\n    }\\n\\n    if(logging_dir.empty() && !cache_dir.empty()) {\\n        logging_dir = path::join(cache_dir, \\\"logs\\\");\\n    }\\n\\n    // Apply variable substitution to string fields\\n    substitute_workspace(compile_commands_path, workspace_root);\\n    substitute_workspace(cache_dir, workspace_root);\\n    substitute_workspace(index_dir, workspace_root);\\n    substitute_workspace(logging_dir, workspace_root);\\n}\\n\\nstd::optional<CliceConfig> CliceConfig::load(const std::string& path,\\n                                             const std::string& workspace_root) {\\n    auto content = fs::read(path);\\n    if(!content) {\\n        return std::nullopt;\\n    }\\n\\n    auto result = eventide::serde::toml::parse<CliceConfig>(*content);\\n    if(!result) {\\n        LOG_WARN(\\\"Failed to parse config file {}\\\", path);\\n        return std::nullopt;\\n    }\\n\\n    auto config = std::move(*result);\\n    config.apply_defaults(workspace_root);\\n\\n    LOG_INFO(\\\"Loaded config from {}\\\", path);\\n    return config;\\n}\\n\\nCliceConfig CliceConfig::load_from_workspace(const std::string& workspace_root) {\\n    if(!workspace_root.empty()) {\\n        // Try standard config file locations\\n        for(auto* name: {\\\"clice.toml\\\", \\\".clice/config.toml\\\"}) {\\n            auto config_path = path::join(workspace_root, name);\\n            if(llvm::sys::fs::exists(config_path)) {\\n                auto config = load(config_path, workspace_root);\\n                if(config)\\n                    return std::move(*config);\\n            }\\n        }\\n    }\\n\\n    // No config file found; use defaults\\n    CliceConfig config;\\n    config.apply_defaults(workspace_root);\\n    LOG_INFO(\\n        \\\"No clice.toml found, using default configuration \\\" \\\"(stateful={}, stateless={}, memory_limit={}MB)\\\",\\n        config.stateful_worker_count,\\n        config.stateless_worker_count,\\n        config.worker_memory_limit / (1024 * 1024));\\n    return config;\\n}\\n\\n}  // namespace clice\\n\"}}}"}
{"ts":4744,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/src/server/master_server.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include \\\"server/master_server.h\\\"\\n\\n#include <algorithm>\\n#include <chrono>\\n#include <format>\\n#include <optional>\\n#include <string>\\n#include <type_traits>\\n#include <variant>\\n#include <vector>\\n\\n#include \\\"command/search_config.h\\\"\\n#include \\\"eventide/ipc/json_codec.h\\\"\\n#include \\\"eventide/ipc/lsp/position.h\\\"\\n#include \\\"eventide/ipc/lsp/uri.h\\\"\\n#include \\\"eventide/reflection/enum.h\\\"\\n#include \\\"eventide/serde/json/json.h\\\"\\n#include \\\"eventide/serde/serde/raw_value.h\\\"\\n#include \\\"index/tu_index.h\\\"\\n#include \\\"semantic/symbol_kind.h\\\"\\n#include \\\"server/protocol.h\\\"\\n#include \\\"support/filesystem.h\\\"\\n#include \\\"support/logging.h\\\"\\n#include \\\"syntax/dependency_graph.h\\\"\\n#include \\\"syntax/include_resolver.h\\\"\\n#include \\\"syntax/scan.h\\\"\\n\\n#include \\\"llvm/Support/Chrono.h\\\"\\n#include \\\"llvm/Support/FileSystem.h\\\"\\n#include \\\"llvm/Support/Path.h\\\"\\n#include \\\"llvm/Support/Process.h\\\"\\n#include \\\"llvm/Support/raw_ostream.h\\\"\\n#include \\\"llvm/Support/xxhash.h\\\"\\n\\nnamespace clice {\\n\\nnamespace protocol = eventide::ipc::protocol;\\nnamespace lsp = eventide::ipc::lsp;\\nnamespace refl = eventide::refl;\\nusing et::ipc::RequestResult;\\nusing RequestContext = et::ipc::JsonPeer::RequestContext;\\n\\n/// Hash a file's content using xxh3_64bits. Returns 0 on read failure.\\nstatic std::uint64_t hash_file(llvm::StringRef path) {\\n    auto buf = llvm::MemoryBuffer::getFile(path);\\n    if(!buf)\\n        return 0;\\n    return llvm::xxh3_64bits((*buf)->getBuffer());\\n}\\n\\n/// Capture a two-layer staleness snapshot after a successful compilation.\\n/// Interns dependency paths into the PathPool and hashes each file's content.\\nstatic DepsSnapshot capture_deps_snapshot(PathPool& pool, llvm::ArrayRef<std::string> deps) {\\n    DepsSnapshot snap;\\n    // Capture timestamp BEFORE hashing to avoid TOCTOU: if a file is modified\\n    // during hashing, its mtime will be > build_at, triggering Layer 2 re-hash.\\n    snap.build_at = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());\\n    snap.path_ids.reserve(deps.size());\\n    snap.hashes.reserve(deps.size());\\n    for(const auto& file: deps) {\\n        snap.path_ids.push_back(pool.intern(file));\\n        snap.hashes.push_back(hash_file(file));\\n    }\\n    return snap;\\n}\\n\\n/// Two-layer staleness check.\\n///\\n/// Layer 1 (fast): stat each dep file, compare mtime against build_at.\\n///   If all mtimes <= build_at → nothing changed, return false immediately.\\n///\\n/// Layer 2 (precise): for files with mtime > build_at, re-hash their content.\\n///   If the hash matches the stored hash → file was touched but not modified.\\n///   If any hash differs → truly changed, return true.\\nstatic bool deps_changed(const PathPool& pool, const DepsSnapshot& snap) {\\n    for(std::size_t i = 0; i < snap.path_ids.size(); ++i) {\\n        auto path = pool.resolve(snap.path_ids[i]);\\n        llvm::sys::fs::file_status status;\\n        if(auto ec = llvm::sys::fs::status(path, status)) {\\n            // File disappeared — definitely changed.\\n            if(snap.hashes[i] != 0)\\n                return true;\\n            continue;\\n        }\\n\\n        // Layer 1: mtime check (cheap, stat only).\\n        auto current_mtime = llvm::sys::toTimeT(status.getLastModificationTime());\\n        if(current_mtime <= snap.build_at)\\n            continue;\\n\\n        // Layer 2: mtime is newer — re-hash content to confirm actual change.\\n        auto current_hash = hash_file(path);\\n        if(current_hash != snap.hashes[i])\\n            return true;\\n    }\\n    return false;\\n}\\n\\nMasterServer::MasterServer(et::event_loop& loop, et::ipc::JsonPeer& peer, std::string self_path) :\\n    loop(loop), peer(peer), pool(loop), self_path(std::move(self_path)) {}\\n\\nMasterServer::~MasterServer() {\\n    if(compile_graph) {\\n        compile_graph->cancel_all();\\n    }\\n}\\n\\nstd::string MasterServer::uri_to_path(const std::string& uri) {\\n    auto parsed = lsp::URI::parse(uri);\\n    if(parsed.has_value()) {\\n        auto path = parsed->file_path();\\n        if(path.has_value()) {\\n            return std::move(*path);\\n        }\\n    }\\n    return uri;\\n}\\n\\nvoid MasterServer::publish_diagnostics(const std::string& uri,\\n                                       int version,\\n                                       const et::serde::RawValue& diagnostics_json) {\\n    std::vector<protocol::Diagnostic> diagnostics;\\n    if(!diagnostics_json.empty()) {\\n        auto status = et::serde::json::from_json(diagnostics_json.data, diagnostics);\\n        if(!status) {\\n            LOG_WARN(\\\"Failed to deserialize diagnostics JSON for {}\\\", uri);\\n        }\\n    }\\n    protocol::PublishDiagnosticsParams params;\\n    params.uri = uri;\\n    params.version = version;\\n    params.diagnostics = std::move(diagnostics);\\n    peer.send_notification(params);\\n}\\n\\nvoid MasterServer::clear_diagnostics(const std::string& uri) {\\n    protocol::PublishDiagnosticsParams params;\\n    params.uri = uri;\\n    params.diagnostics = {};\\n    peer.send_notification(params);\\n}\\n\\n/// Serializable cache structures for cache.json persistence.\\n/// Paths are stored in a shared table and referenced by index to avoid\\n/// redundant storage (a single file can depend on thousands of headers,\\n/// many of which are shared across entries).\\nnamespace {\\n\\nstruct CacheDepEntry {\\n    std::uint32_t path;  // index into CacheData::paths\\n    std::uint64_t hash;\\n};\\n\\nstruct CachePCHEntry {\\n    std::string filename;\\n    std::uint32_t source_file;  // index into CacheData::paths\\n    std::uint64_t hash;\\n    std::uint32_t bound;\\n    std::int64_t build_at;\\n    std::vector<CacheDepEntry> deps;\\n};\\n\\nstruct CachePCMEntry {\\n    std::string filename;\\n    std::uint32_t source_file;  // index into CacheData::paths\\n    std::string module_name;\\n    std::int64_t build_at;\\n    std::vector<CacheDepEntry> deps;\\n};\\n\\nstruct CacheData {\\n    std::vector<std::string> paths;\\n    std::vector<CachePCHEntry> pch;\\n    std::vector<CachePCMEntry> pcm;\\n};\\n\\n}  // namespace\\n\\nvoid MasterServer::load_cache() {\\n    if(config.cache_dir.empty())\\n        return;\\n\\n    auto cache_path = path::join(config.cache_dir, \\\"cache\\\", \\\"cache.json\\\");\\n    auto content = fs::read(cache_path);\\n    if(!content) {\\n        LOG_DEBUG(\\\"No cache.json found at {}\\\", cache_path);\\n        return;\\n    }\\n\\n    CacheData data;\\n    auto status = et::serde::json::from_json(*content, data);\\n    if(!status) {\\n        LOG_WARN(\\\"Failed to parse cache.json\\\");\\n        return;\\n    }\\n\\n    auto resolve = [&](std::uint32_t idx) -> llvm::StringRef {\\n        return idx < data.paths.size() ? llvm::StringRef(data.paths[idx]) : \\\"\\\";\\n    };\\n\\n    for(auto& entry: data.pch) {\\n        auto pch_path = path::join(config.cache_dir, \\\"cache\\\", \\\"pch\\\", entry.filename);\\n        auto source = resolve(entry.source_file);\\n        if(!llvm::sys::fs::exists(pch_path) || source.empty())\\n            continue;\\n\\n        DepsSnapshot deps;\\n        deps.build_at = entry.build_at;\\n        for(auto& dep: entry.deps) {\\n            auto dep_path = resolve(dep.path);\\n            if(dep_path.empty())\\n                continue;\\n            deps.path_ids.push_back(path_pool.intern(dep_path));\\n            deps.hashes.push_back(dep.hash);\\n        }\\n\\n        auto path_id = path_pool.intern(source);\\n        auto& st = pch_states[path_id];\\n        st.path = pch_path;\\n        st.hash = entry.hash;\\n        st.bound = entry.bound;\\n        st.deps = std::move(deps);\\n\\n        LOG_DEBUG(\\\"Loaded cached PCH: {} -> {}\\\", source, pch_path);\\n    }\\n\\n    for(auto& entry: data.pcm) {\\n        auto pcm_path = path::join(config.cache_dir, \\\"cache\\\", \\\"pcm\\\", entry.filename);\\n        auto source = resolve(entry.source_file);\\n        if(!llvm::sys::fs::exists(pcm_path) || source.empty())\\n            continue;\\n\\n        DepsSnapshot deps;\\n        deps.build_at = entry.build_at;\\n        for(auto& dep: entry.deps) {\\n            auto dep_path = resolve(dep.path);\\n            if(dep_path.empty())\\n                continue;\\n            deps.path_ids.push_back(path_pool.intern(dep_path));\\n            deps.hashes.push_back(dep.hash);\\n        }\\n\\n        auto path_id = path_pool.intern(source);\\n        pcm_states[path_id] = {pcm_path, std::move(deps)};\\n        pcm_paths[path_id] = pcm_path;\\n\\n        LOG_DEBUG(\\\"Loaded cached PCM: {} (module {}) -> {}\\\", source, entry.module_name, pcm_path);\\n    }\\n\\n    LOG_INFO(\\\"Loaded cache.json: {} PCH entries, {} PCM entries\\\",\\n             pch_states.size(),\\n             pcm_states.size());\\n}\\n\\nvoid MasterServer::save_cache() {\\n    if(config.cache_dir.empty())\\n        return;\\n\\n    CacheData data;\\n    std::unordered_map<std::string, std::uint32_t> index_map;\\n\\n    auto intern = [&](std::uint32_t runtime_path_id) -> std::uint32_t {\\n        auto path = std::string(path_pool.resolve(runtime_path_id));\\n        auto [it, inserted] =\\n            index_map.try_emplace(path, static_cast<std::uint32_t>(data.paths.size()));\\n        if(inserted) {\\n            data.paths.push_back(path);\\n        }\\n        return it->second;\\n    };\\n\\n    for(auto& [path_id, st]: pch_states) {\\n        if(st.path.empty())\\n            continue;\\n\\n        CachePCHEntry entry;\\n        entry.filename = std::string(path::filename(st.path));\\n        entry.source_file = intern(path_id);\\n        entry.hash = st.hash;\\n        entry.bound = st.bound;\\n        entry.build_at = st.deps.build_at;\\n        for(std::size_t i = 0; i < st.deps.path_ids.size(); ++i) {\\n            entry.deps.push_back({intern(st.deps.path_ids[i]), st.deps.hashes[i]});\\n        }\\n        data.pch.push_back(std::move(entry));\\n    }\\n\\n    for(auto& [path_id, st]: pcm_states) {\\n        if(st.path.empty())\\n            continue;\\n\\n        CachePCMEntry entry;\\n        entry.filename = std::string(path::filename(st.path));\\n        entry.source_file = intern(path_id);\\n        auto mod_it = path_to_module.find(path_id);\\n        entry.module_name = mod_it != path_to_module.end() ? mod_it->second : \\\"\\\";\\n        entry.build_at = st.deps.build_at;\\n        for(std::size_t i = 0; i < st.deps.path_ids.size(); ++i) {\\n            entry.deps.push_back({intern(st.deps.path_ids[i]), st.deps.hashes[i]});\\n        }\\n        data.pcm.push_back(std::move(entry));\\n    }\\n\\n    auto json_str = et::serde::json::to_json(data);\\n    if(!json_str) {\\n        LOG_WARN(\\\"Failed to serialize cache.json\\\");\\n        return;\\n    }\\n\\n    auto cache_path = path::join(config.cache_dir, \\\"cache\\\", \\\"cache.json\\\");\\n    auto tmp_path = cache_path + \\\".tmp\\\";\\n    auto write_result = fs::write(tmp_path, *json_str);\\n    if(!write_result) {\\n        LOG_WARN(\\\"Failed to write cache.json.tmp: {}\\\", write_result.error().message());\\n        return;\\n    }\\n    auto rename_result = fs::rename(tmp_path, cache_path);\\n    if(!rename_result) {\\n        LOG_WARN(\\\"Failed to rename cache.json.tmp to cache.json: {}\\\",\\n                 rename_result.error().message());\\n    }\\n}\\n\\nvoid MasterServer::cleanup_cache(int max_age_days) {\\n    if(config.cache_dir.empty())\\n        return;\\n\\n    auto now = std::chrono::system_clock::now();\\n    auto max_age = std::chrono::hours(max_age_days * 24);\\n\\n    for(auto* subdir: {\\\"cache/pch\\\", \\\"cache/pcm\\\"}) {\\n        auto dir = path::join(config.cache_dir, subdir);\\n        std::error_code ec;\\n        for(auto it = llvm::sys::fs::directory_iterator(dir, ec);\\n            !ec && it != llvm::sys::fs::directory_iterator();\\n            it.increment(ec)) {\\n            llvm::sys::fs::file_status status;\\n            if(auto stat_ec = llvm::sys::fs::status(it->path(), status))\\n                continue;\\n\\n            auto mtime = status.getLastModificationTime();\\n            auto age = now - mtime;\\n            if(age > max_age) {\\n                llvm::sys::fs::remove(it->path());\\n                LOG_DEBUG(\\\"Cleaned up stale cache file: {}\\\", it->path());\\n            }\\n        }\\n    }\\n}\\n\\net::task<> MasterServer::load_workspace() {\\n    if(workspace_root.empty())\\n        co_return;\\n\\n    // Create cache directory if configured\\n    if(!config.cache_dir.empty()) {\\n        auto ec = llvm::sys::fs::create_directories(config.cache_dir);\\n        if(ec) {\\n            LOG_WARN(\\\"Failed to create cache directory {}: {}\\\", config.cache_dir, ec.message());\\n        } else {\\n            LOG_INFO(\\\"Cache directory: {}\\\", config.cache_dir);\\n        }\\n\\n        // Create cache/pch/ and cache/pcm/ subdirectories\\n        for(auto* subdir: {\\\"cache/pch\\\", \\\"cache/pcm\\\"}) {\\n            auto dir = path::join(config.cache_dir, subdir);\\n            auto ec2 = llvm::sys::fs::create_directories(dir);\\n            if(ec2) {\\n                LOG_WARN(\\\"Failed to create {}: {}\\\", dir, ec2.message());\\n            }\\n        }\\n\\n        // Clean up stale files first, then load — load_cache() only restores\\n        // entries still listed in cache.json, so cleanup won't delete live files.\\n        cleanup_cache();\\n        load_cache();\\n    }\\n\\n    // Search for compile_commands.json\\n    std::string cdb_path;\\n\\n    // If the config specifies a CDB path, use it\\n    if(!config.compile_commands_path.empty()) {\\n        if(llvm::sys::fs::exists(config.compile_commands_path)) {\\n            cdb_path = config.compile_commands_path;\\n        } else {\\n            LOG_WARN(\\\"Configured compile_commands_path not found: {}\\\",\\n                     config.compile_commands_path);\\n        }\\n    }\\n\\n    // Otherwise auto-detect in common locations\\n    if(cdb_path.empty()) {\\n        for(auto* subdir: {\\\"build\\\", \\\"cmake-build-debug\\\", \\\"cmake-build-release\\\", \\\"out\\\", \\\".\\\"}) {\\n            auto candidate = path::join(workspace_root, subdir, \\\"compile_commands.json\\\");\\n            if(llvm::sys::fs::exists(candidate)) {\\n                cdb_path = std::move(candidate);\\n                break;\\n            }\\n        }\\n    }\\n\\n    if(cdb_path.empty()) {\\n        LOG_WARN(\\\"No compile_commands.json found in workspace {}\\\", workspace_root);\\n        co_return;\\n    }\\n\\n    auto count = cdb.load(cdb_path);\\n    LOG_INFO(\\\"Loaded CDB from {} with {} entries\\\", cdb_path, count);\\n\\n    auto report = scan_dependency_graph(cdb, path_pool, dependency_graph);\\n\\n    auto unresolved = report.includes_found - report.includes_resolved;\\n    double accuracy =\\n        report.includes_found > 0\\n            ? 100.0 * static_cast<double>(report.includes_resolved) / report.includes_found\\n            : 100.0;\\n    LOG_INFO(\\n        \\\"Dependency scan: {}ms, {} files ({} source + {} header), \\\" \\\"{} edges, {}/{} resolved ({:.1f}%), {} waves\\\",\\n        report.elapsed_ms,\\n        report.total_files,\\n        report.source_files,\\n        report.header_files,\\n        report.total_edges,\\n        report.includes_resolved,\\n        report.includes_found,\\n        accuracy,\\n        report.waves);\\n    if(unresolved > 0) {\\n        LOG_WARN(\\\"{} unresolved includes\\\", unresolved);\\n    }\\n\\n    // Build reverse mapping: path_id -> module name.\\n    for(auto& [module_name, path_ids]: dependency_graph.modules()) {\\n        for(auto path_id: path_ids) {\\n            path_to_module[path_id] = module_name.str();\\n        }\\n    }\\n\\n    // Load persisted index from disk.\\n    load_index();\\n\\n    // Build index queue from CDB entries (all source files).\\n    // CDB entries use the CDB's internal path_ids; convert to server path_ids.\\n    if(config.enable_indexing) {\\n        for(auto& entry: cdb.get_entries()) {\\n            auto file = cdb.resolve_path(entry.file);\\n            auto server_id = path_pool.intern(file);\\n            index_queue.push_back(server_id);\\n        }\\n        if(!index_queue.empty()) {\\n            LOG_INFO(\\\"Queued {} files for background indexing\\\", index_queue.size());\\n            for(auto sid: index_queue) {\\n                LOG_INFO(\\\"  queue entry: server_path_id={} path='{}'\\\", sid, path_pool.resolve(sid));\\n            }\\n            schedule_indexing();\\n        }\\n    }\\n\\n    if(path_to_module.empty()) {\\n        LOG_INFO(\\\"No C++20 modules detected, skipping CompileGraph\\\");\\n        co_return;\\n    }\\n\\n    // Lazy dependency resolver: scans a module file on demand to discover imports.\\n    auto resolve = [this](std::uint32_t path_id) -> llvm::SmallVector<std::uint32_t> {\\n        auto file_path = path_pool.resolve(path_id);\\n        auto results = cdb.lookup(file_path, {.query_toolchain = true, .suppress_logging = true});\\n        if(results.empty()) {\\n            return {};\\n        }\\n\\n        auto& ctx = results[0];\\n        auto scan_result = scan_precise(ctx.arguments, ctx.directory);\\n\\n        llvm::SmallVector<std::uint32_t> deps;\\n        for(auto& mod_name: scan_result.modules) {\\n            auto mod_ids = dependency_graph.lookup_module(mod_name);\\n            if(!mod_ids.empty()) {\\n                deps.push_back(mod_ids[0]);\\n            }\\n        }\\n\\n        // Module implementation units implicitly depend on their interface unit.\\n        if(!scan_result.module_name.empty() && !scan_result.is_interface_unit) {\\n            auto mod_ids = dependency_graph.lookup_module(scan_result.module_name);\\n            if(!mod_ids.empty()) {\\n                deps.push_back(mod_ids[0]);\\n            }\\n        }\\n\\n        return deps;\\n    };\\n\\n    // Dispatch: sends BuildPCM request to a stateless worker.\\n    auto dispatch = [this](std::uint32_t path_id) -> et::task<bool> {\\n        auto mod_it = path_to_module.find(path_id);\\n        if(mod_it == path_to_module.end()) {\\n            co_return false;\\n        }\\n\\n        auto file_path = std::string(path_pool.resolve(path_id));\\n\\n        worker::BuildPCMParams pcm_params;\\n        pcm_params.file = file_path;\\n        if(!fill_compile_args(file_path, pcm_params.directory, pcm_params.arguments)) {\\n            co_return false;\\n        }\\n\\n        // Compute deterministic content-addressed PCM path.\\n        // Replace ':' with '-' in module name for filesystem safety.\\n        // Hash includes file path AND compile arguments so that argument\\n        // changes (e.g. -DFOO) invalidate the cached PCM.\\n        auto safe_module_name = mod_it->second;\\n        std::ranges::replace(safe_module_name, ':', '-');\\n        std::string hash_input = file_path;\\n        for(auto& arg: pcm_params.arguments) {\\n            hash_input += arg;\\n        }\\n        auto args_hash = llvm::xxh3_64bits(llvm::StringRef(hash_input));\\n        auto pcm_filename = std::format(\\\"{}-{:016x}.pcm\\\", safe_module_name, args_hash);\\n        auto pcm_path = path::join(config.cache_dir, \\\"cache\\\", \\\"pcm\\\", pcm_filename);\\n\\n        // Check if cached PCM is still valid.\\n        if(auto pcm_it = pcm_states.find(path_id); pcm_it != pcm_states.end()) {\\n            if(!pcm_it->second.path.empty() && llvm::sys::fs::exists(pcm_it->second.path) &&\\n               !deps_changed(path_pool, pcm_it->second.deps)) {\\n                pcm_paths[path_id] = pcm_it->second.path;\\n                co_return true;\\n            }\\n        }\\n\\n        pcm_params.module_name = mod_it->second;\\n        pcm_params.output_path = pcm_path;\\n\\n        // Clang needs ALL transitive PCM deps, not just direct imports.\\n        for(auto& [pid, existing_pcm_path]: pcm_paths) {\\n            auto dep_mod_it = path_to_module.find(pid);\\n            if(dep_mod_it != path_to_module.end()) {\\n                pcm_params.pcms[dep_mod_it->second] = existing_pcm_path;\\n            }\\n        }\\n\\n        auto result = co_await pool.send_stateless(pcm_params);\\n        if(!result.has_value() || !result.value().success) {\\n            LOG_WARN(\\\"BuildPCM failed for module {}: {}\\\",\\n                     mod_it->second,\\n                     result.has_value() ? result.value().error : result.error().message);\\n            co_return false;\\n        }\\n\\n        pcm_paths[path_id] = result.value().pcm_path;\\n        pcm_states[path_id] = {result.value().pcm_path,\\n                               capture_deps_snapshot(path_pool, result.value().deps)};\\n        LOG_INFO(\\\"Built PCM for module {}: {}\\\", mod_it->second, result.value().pcm_path);\\n\\n        // Persist cache metadata after successful build.\\n        save_cache();\\n\\n        co_return true;\\n    };\\n\\n    compile_graph = std::make_unique<CompileGraph>(std::move(dispatch), std::move(resolve));\\n    LOG_INFO(\\\"CompileGraph initialized with {} module(s)\\\", path_to_module.size());\\n}\\n\\nbool MasterServer::fill_compile_args(llvm::StringRef path,\\n                                     std::string& directory,\\n                                     std::vector<std::string>& arguments) {\\n    auto results = cdb.lookup(path, {.query_toolchain = true});\\n    if(results.empty()) {\\n        LOG_WARN(\\\"No CDB entry for {}\\\", path);\\n        return false;\\n    }\\n    auto& ctx = results.front();\\n    directory = ctx.directory.str();\\n    arguments.clear();\\n    for(auto* arg: ctx.arguments) {\\n        arguments.emplace_back(arg);\\n    }\\n    return true;\\n}\\n\\net::task<bool> MasterServer::ensure_pch(std::uint32_t path_id,\\n                                        llvm::StringRef path,\\n                                        const std::string& text,\\n                                        const std::string& directory,\\n                                        const std::vector<std::string>& arguments) {\\n    auto bound = compute_preamble_bound(text);\\n    if(bound == 0) {\\n        // No preamble directives — PCH would be empty. Clear any stale entry.\\n        pch_states.erase(path_id);\\n        co_return true;\\n    }\\n\\n    auto preamble_hash = llvm::xxh3_64bits(llvm::StringRef(text).substr(0, bound));\\n\\n    // Deterministic content-addressed PCH path.\\n    auto pch_path =\\n        path::join(config.cache_dir, \\\"cache\\\", \\\"pch\\\", std::format(\\\"{:016x}.pch\\\", preamble_hash));\\n\\n    // Reuse existing PCH if preamble content and deps haven't changed.\\n    if(auto it = pch_states.find(path_id); it != pch_states.end()) {\\n        auto& st = it->second;\\n        if(st.hash == preamble_hash && !st.path.empty() && !deps_changed(path_pool, st.deps)) {\\n            st.bound = bound;\\n            co_return true;\\n        }\\n    }\\n\\n    // Preamble incomplete (user still typing) — defer rebuild, reuse old PCH if available.\\n    if(!is_preamble_complete(text, bound)) {\\n        LOG_DEBUG(\\\"Preamble incomplete for {}, deferring PCH rebuild\\\", path);\\n        co_return pch_states.count(path_id) && !pch_states[path_id].path.empty();\\n    }\\n\\n    // If another coroutine is already building PCH for this file, wait for it.\\n    if(auto it = pch_states.find(path_id); it != pch_states.end() && it->second.building) {\\n        co_await it->second.building->wait();\\n        co_return !pch_states[path_id].path.empty();\\n    }\\n\\n    // Register in-flight build so concurrent requests wait on us.\\n    auto completion = std::make_shared<et::event>();\\n    pch_states[path_id].building = completion;\\n\\n    // Build a new PCH via stateless worker.\\n    worker::BuildPCHParams pch_params;\\n    pch_params.file = std::string(path);\\n    pch_params.directory = directory;\\n    pch_params.arguments = arguments;\\n    pch_params.content = text;\\n    pch_params.preamble_bound = bound;\\n    pch_params.output_path = pch_path;\\n\\n    LOG_DEBUG(\\\"Building PCH for {}, bound={}, output={}\\\", path, bound, pch_path);\\n\\n    auto result = co_await pool.send_stateless(pch_params);\\n\\n    if(!result.has_value() || !result.value().success) {\\n        LOG_WARN(\\\"PCH build failed for {}: {}\\\",\\n                 path,\\n                 result.has_value() ? result.value().error : result.error().message);\\n        pch_states[path_id].building.reset();\\n        completion->set();\\n        co_return false;\\n    }\\n\\n    // Update state — no need to delete old file; content-addressed names differ\\n    // when content differs, and the 7-day cleanup handles orphaned files.\\n    auto& st = pch_states[path_id];\\n    st.path = result.value().pch_path;\\n    st.bound = bound;\\n    st.hash = preamble_hash;\\n    st.deps = capture_deps_snapshot(path_pool, result.value().deps);\\n    st.building.reset();\\n\\n    LOG_INFO(\\\"PCH built for {}: {}\\\", path, result.value().pch_path);\\n\\n    // Persist cache metadata after successful build.\\n    save_cache();\\n\\n    completion->set();\\n    co_return true;\\n}\\n\\n/// Compile module dependencies, build/reuse PCH, and fill PCM paths.\\n/// Shared preparation step used by both ensure_compiled() (stateful path)\\n/// and forward_stateless() (completion/signatureHelp path).\\net::task<bool> MasterServer::ensure_deps(std::uint32_t path_id,\\n                                         llvm::StringRef path,\\n                                         const std::string& text,\\n                                         const std::string& directory,\\n                                         const std::vector<std::string>& arguments,\\n                                         std::pair<std::string, uint32_t>& pch,\\n                                         std::unordered_map<std::string, std::string>& pcms) {\\n    // Compile C++20 module dependencies (PCMs).\\n    if(compile_graph && !co_await compile_graph->compile_deps(path_id)) {\\n        co_return false;\\n    }\\n\\n    // Scan buffer text for module imports that might not be in compile_graph yet.\\n    // When a user adds `import std;` without saving, the compile_graph (disk-based)\\n    // doesn't know about the new dependency. Scan the in-memory text to find them.\\n    {\\n        auto scan_result = scan(text);\\n        for(auto& mod_name: scan_result.modules) {\\n            if(mod_name.empty()) {\\n                continue;\\n            }\\n            bool found = false;\\n            for(auto& [pid, name]: path_to_module) {\\n                if(name == mod_name) {\\n                    // If PCM not already built, try to build it.\\n                    if(pcm_paths.find(pid) == pcm_paths.end()) {\\n                        if(compile_graph && compile_graph->has_unit(pid)) {\\n                            co_await compile_graph->compile_deps(pid);\\n                        }\\n                    }\\n                    found = true;\\n                    break;\\n                }\\n            }\\n            if(!found) {\\n                LOG_DEBUG(\\\"Buffer imports unknown module '{}', skipping\\\", mod_name);\\n            }\\n        }\\n    }\\n\\n    // Build or reuse PCH.\\n    auto pch_ok = co_await ensure_pch(path_id, path, text, directory, arguments);\\n    if(pch_ok) {\\n        if(auto pch_it = pch_states.find(path_id); pch_it != pch_states.end()) {\\n            pch = {pch_it->second.path, pch_it->second.bound};\\n        }\\n    }\\n\\n    // Fill all available PCM paths so clang can resolve transitive imports.\\n    // Exclude the file's own PCM to avoid \\\"multiple module declarations\\\".\\n    for(auto& [pid, pcm_path]: pcm_paths) {\\n        if(pid == path_id)\\n            continue;\\n        auto mod_it = path_to_module.find(pid);\\n        if(mod_it != path_to_module.end()) {\\n            pcms[mod_it->second] = pcm_path;\\n        }\\n    }\\n\\n    co_return true;\\n}\\n\\n/// Pull-based compilation entry point for user-opened files.\\n///\\n/// Called lazily by forward_stateful() / forward_stateless() before every\\n/// feature request (hover, semantic tokens, etc.). Guarantees that when it\\n/// returns true the stateful worker assigned to `path_id` holds an up-to-date\\n/// AST and diagnostics have been published to the client.\\n///\\n/// Lifecycle overview (pull-based model):\\n///\\n///   didOpen / didChange          – only update DocumentState, mark ast_dirty\\n///   didSave                      – mark dependents dirty, queue indexing\\n///   feature request arrives      – calls ensure_compiled() first\\n///     1. Fast-path exit if AST is already clean (!ast_dirty).\\n///     2. Compile any C++20 module dependencies (PCMs) via CompileGraph.\\n///     3. Build / reuse the precompiled header (PCH) via ensure_pch().\\n///     4. Send CompileParams to the stateful worker, which builds the AST.\\n///     5. On success: publish diagnostics, clear ast_dirty, schedule indexing.\\n///     6. On generation mismatch (user edited during compile): keep dirty,\\n///        the next feature request will trigger another compile cycle.\\n///\\n/// Only the opened file itself is remapped (its in-memory text is sent to the\\n/// worker); every other file is read from disk by the compiler.\\n///\\n/// Concurrency: multiple concurrent feature requests for the same file will\\n/// each call ensure_compiled(). The first one triggers the actual compilation;\\n/// subsequent ones observe ast_dirty == false after the first completes and\\n/// take the fast path. This is safe because forward_stateful serialises\\n/// requests per worker slot.\\net::task<bool> MasterServer::ensure_compiled(std::uint32_t path_id) {\\n    auto it = documents.find(path_id);\\n    if(it == documents.end()) {\\n        LOG_DEBUG(\\\"ensure_compiled: doc not found for path_id={}\\\", path_id);\\n        co_return false;\\n    }\\n\\n    auto& doc = it->second;\\n    LOG_DEBUG(\\\"ensure_compiled: path_id={} version={} gen={} ast_dirty={}\\\",\\n              path_id,\\n              doc.version,\\n              doc.generation,\\n              doc.ast_dirty);\\n\\n    if(!doc.ast_dirty) {\\n        bool changed = false;\\n        auto ast_deps_it = ast_deps.find(path_id);\\n        if(ast_deps_it != ast_deps.end() && deps_changed(path_pool, ast_deps_it->second)) {\\n            changed = true;\\n        }\\n        if(!changed) {\\n            auto pch_it = pch_states.find(path_id);\\n            if(pch_it != pch_states.end() && deps_changed(path_pool, pch_it->second.deps)) {\\n                changed = true;\\n            }\\n        }\\n        if(!changed) {\\n            LOG_INFO(\\\"ensure_compiled: fast path, ast clean path_id={}\\\", path_id);\\n            co_return true;\\n        }\\n        LOG_INFO(\\\"ensure_compiled: deps changed, marking dirty path_id={}\\\", path_id);\\n        doc.ast_dirty = true;\\n    }\\n    LOG_INFO(\\\"ensure_compiled: ast_dirty=true, compiling={} path_id={}\\\",\\n             (bool)doc.compiling,\\n             path_id);\\n\\n    // After co_await suspension points the iterator may be invalidated (the\\n    // documents map could have been modified by didClose on another file).\\n    auto recheck = [&]() -> bool {\\n        it = documents.find(path_id);\\n        return it != documents.end();\\n    };\\n\\n    // If another ensure_compiled() is already in flight for this file,\\n    // wait for it to finish instead of sending a duplicate compile request.\\n    // Loop because multiple waiters wake up simultaneously — only the first\\n    // one should start a new compile; the rest must wait again.\\n    while(it->second.compiling) {\\n        LOG_INFO(\\\"ensure_compiled: waiting on in-flight compile for path_id={}\\\", path_id);\\n        co_await it->second.compiling->wait();\\n        LOG_INFO(\\\"ensure_compiled: in-flight compile done for path_id={}\\\", path_id);\\n        if(!recheck())\\n            co_return false;\\n        if(!it->second.ast_dirty) {\\n            LOG_INFO(\\\"ensure_compiled: ast clean after wait, path_id={}\\\", path_id);\\n            co_return true;\\n        }\\n        // ast still dirty — loop back to check if someone else already\\n        // started a new compile; only fall through if no one has.\\n    }\\n\\n    // Snapshot the generation counter *before* any co_await.  After compilation\\n    // we compare it with the current value to detect edits that arrived while\\n    // we were suspended — if they differ, the result is stale and we must not\\n    // mark the AST as clean.\\n    auto gen = doc.generation;\\n\\n    // Register in-flight compile so concurrent callers wait on us.\\n    auto completion = std::make_shared<et::event>();\\n    doc.compiling = completion;\\n    LOG_INFO(\\\"ensure_compiled: starting compile path_id={} gen={}\\\", path_id, gen);\\n\\n    // Signal waiters and clear compiling flag on any return path.\\n    auto finish_compile = [&]() {\\n        LOG_INFO(\\\"ensure_compiled: finish_compile path_id={} gen={}\\\", path_id, gen);\\n        if(auto doc_it = documents.find(path_id); doc_it != documents.end()) {\\n            doc_it->second.compiling.reset();\\n        }\\n        completion->set();\\n    };\\n\\n    auto file_path = std::string(path_pool.resolve(path_id));\\n    auto uri = lsp::URI::from_file_path(file_path);\\n    std::string uri_str = uri.has_value() ? uri->str() : file_path;\\n\\n    // ── Phase 1–3: Module deps, PCH, PCM paths ─────────────────────────\\n    worker::CompileParams params;\\n    params.path = file_path;\\n    if(!recheck()) {\\n        finish_compile();\\n        co_return false;\\n    }\\n    params.version = it->second.version;\\n    params.text = it->second.text;\\n    if(!fill_compile_args(path_pool.resolve(path_id), params.directory, params.arguments)) {\\n        finish_compile();\\n        co_return false;\\n    }\\n\\n    LOG_INFO(\\\"ensure_compiled: ensure_deps start path_id={} gen={}\\\", path_id, gen);\\n    if(!co_await ensure_deps(path_id,\\n                             params.path,\\n                             params.text,\\n                             params.directory,\\n                             params.arguments,\\n                             params.pch,\\n                             params.pcms)) {\\n        LOG_WARN(\\\"Dependency preparation failed for {}, skipping compile\\\", uri_str);\\n        finish_compile();\\n        co_return false;\\n    }\\n\\n    if(!recheck()) {\\n        finish_compile();\\n        co_return false;\\n    }\\n\\n    // ── Phase 4: Dispatch to stateful worker ────────────────────────────\\n    //\\n    // The stateful worker receives the full document text and compile args,\\n    // builds the AST, and caches it for subsequent feature requests.  The\\n    // response carries diagnostics collected during compilation.\\n    LOG_INFO(\\\"ensure_compiled: ensure_deps done, sending compile path_id={} gen={}\\\", path_id, gen);\\n    auto result = co_await pool.send_stateful(path_id, params);\\n    LOG_INFO(\\\"ensure_compiled: compile response received path_id={} gen={}\\\", path_id, gen);\\n\\n    // Re-lookup: the document may have been closed while we were compiling.\\n    it = documents.find(path_id);\\n    if(it == documents.end()) {\\n        finish_compile();\\n        co_return false;\\n    }\\n\\n    auto& doc2 = it->second;\\n\\n    // ── Phase 5: Handle result ──────────────────────────────────────────\\n    //\\n    // Generation mismatch means the user edited the file while we compiled.\\n    // The AST we just built corresponds to an older version of the text, so\\n    // we discard the diagnostics and leave ast_dirty == true.  The next\\n    // feature request will trigger another compile cycle with the latest text.\\n    if(doc2.generation != gen) {\\n        LOG_INFO(\\\"ensure_compiled: generation mismatch ({} vs {}) for {}\\\",\\n                 doc2.generation,\\n                 gen,\\n                 uri_str);\\n        finish_compile();\\n        co_return false;\\n    }\\n\\n    if(!result.has_value()) {\\n        LOG_WARN(\\\"Compile failed for {}: {}\\\", uri_str, result.error().message);\\n        // Clear stale diagnostics so the editor doesn't show errors from a\\n        // previous successful compilation that no longer apply.\\n        clear_diagnostics(uri_str);\\n        finish_compile();\\n        co_return false;\\n    }\\n\\n    LOG_INFO(\\\"ensure_compiled: success path_id={} gen={}, publishing diagnostics\\\", path_id, gen);\\n    doc2.ast_dirty = false;\\n    ast_deps[path_id] = capture_deps_snapshot(path_pool, result.value().deps);\\n    finish_compile();\\n\\n    // Publish diagnostics AFTER marking compile as done, so that concurrent\\n    // forward_stateful() calls can proceed immediately instead of waiting\\n    // for the (potentially large) diagnostics to be written to the client pipe.\\n    publish_diagnostics(uri_str, doc2.version, result.value().diagnostics);\\n    schedule_indexing();\\n    co_return true;\\n}\\n\\n// =========================================================================\\n// Index integration\\n// =========================================================================\\n\\nvoid MasterServer::merge_index_result(const void* tu_index_data, std::size_t size) {\\n    auto tu_index = index::TUIndex::from(tu_index_data);\\n\\n    // Merge symbols into ProjectIndex, get TU-local path_id -> global path_id mapping.\\n    auto file_ids_map = project_index.merge(tu_index);\\n\\n    auto main_tu_path_id = static_cast<std::uint32_t>(tu_index.graph.paths.size() - 1);\\n\\n    // Merge a single file's index into the corresponding MergedIndex shard.\\n    auto merge_file_index = [&](std::uint32_t tu_path_id, index::FileIndex& file_idx) {\\n        auto global_path_id = file_ids_map[tu_path_id];\\n        auto& merged = merged_indices[global_path_id];\\n\\n        if(tu_path_id == main_tu_path_id) {\\n            // Main file (source file) gets a compilation context with include locations.\\n            // Collect ALL include locations with path_ids remapped to project-level ids.\\n            std::vector<index::IncludeLocation> include_locs;\\n            for(auto& loc: tu_index.graph.locations) {\\n                index::IncludeLocation remapped = loc;\\n                remapped.path_id = file_ids_map[loc.path_id];\\n                include_locs.push_back(remapped);\\n            }\\n            // Read the file content from disk for position mapping in queries.\\n            auto file_path = project_index.path_pool.path(global_path_id);\\n            llvm::StringRef file_content;\\n            std::string file_content_storage;\\n            auto buf = llvm::MemoryBuffer::getFile(file_path);\\n            if(buf) {\\n                file_content_storage = (*buf)->getBuffer().str();\\n                file_content = file_content_storage;\\n            }\\n            merged.merge(global_path_id,\\n                         tu_index.built_at,\\n                         std::move(include_locs),\\n                         file_idx,\\n                         file_content);\\n        } else {\\n            // Header files get a header context keyed by include location.\\n            std::uint32_t include_id = 0;\\n            for(std::uint32_t i = 0; i < tu_index.graph.locations.size(); ++i) {\\n                if(tu_index.graph.locations[i].path_id == tu_path_id) {\\n                    include_id = i;\\n                    break;\\n                }\\n            }\\n            // Read header file content for position mapping in queries.\\n            auto header_path = project_index.path_pool.path(global_path_id);\\n            llvm::StringRef header_content;\\n            std::string header_content_storage;\\n            auto header_buf = llvm::MemoryBuffer::getFile(header_path);\\n            if(header_buf) {\\n                header_content_storage = (*header_buf)->getBuffer().str();\\n                header_content = header_content_storage;\\n            }\\n            merged.merge(global_path_id, include_id, file_idx, header_content);\\n        }\\n    };\\n\\n    // Merge from path_file_indices (deserialized TUIndex from IPC).\\n    for(auto& [tu_path_id, file_idx]: tu_index.path_file_indices) {\\n        merge_file_index(tu_path_id, file_idx);\\n    }\\n\\n    // Merge main file index.\\n    merge_file_index(main_tu_path_id, tu_index.main_file_index);\\n\\n    LOG_INFO(\\\"Merged TUIndex: {} paths, {} symbols, {} merged_shards\\\",\\n             tu_index.graph.paths.size(),\\n             tu_index.symbols.size(),\\n             merged_indices.size());\\n    for(auto& [pid, _]: merged_indices) {\\n        LOG_INFO(\\\"  shard proj_path_id={} path='{}'\\\", pid, project_index.path_pool.path(pid));\\n    }\\n}\\n\\nvoid MasterServer::save_index() {\\n    if(config.index_dir.empty())\\n        return;\\n\\n    auto ec = llvm::sys::fs::create_directories(config.index_dir);\\n    if(ec) {\\n        LOG_WARN(\\\"Failed to create index directory {}: {}\\\", config.index_dir, ec.message());\\n        return;\\n    }\\n\\n    // Save ProjectIndex.\\n    auto project_path = path::join(config.index_dir, \\\"project.idx\\\");\\n    {\\n        std::error_code write_ec;\\n        llvm::raw_fd_ostream os(project_path, write_ec);\\n        if(!write_ec) {\\n            project_index.serialize(os);\\n            LOG_INFO(\\\"Saved ProjectIndex to {}\\\", project_path);\\n        } else {\\n            LOG_WARN(\\\"Failed to save ProjectIndex: {}\\\", write_ec.message());\\n        }\\n    }\\n\\n    // Save MergedIndex shards.\\n    auto shards_dir = path::join(config.index_dir, \\\"shards\\\");\\n    ec = llvm::sys::fs::create_directories(shards_dir);\\n    if(ec) {\\n        LOG_WARN(\\\"Failed to create shards directory: {}\\\", ec.message());\\n        return;\\n    }\\n\\n    std::size_t saved = 0;\\n    for(auto& [path_id, merged]: merged_indices) {\\n        if(!merged.need_rewrite())\\n            continue;\\n        auto shard_path = path::join(shards_dir, std::to_string(path_id) + \\\".idx\\\");\\n        std::error_code write_ec;\\n        llvm::raw_fd_ostream os(shard_path, write_ec);\\n        if(!write_ec) {\\n            merged.serialize(os);\\n            ++saved;\\n        }\\n    }\\n    LOG_INFO(\\\"Saved {} MergedIndex shards (of {} total)\\\", saved, merged_indices.size());\\n}\\n\\nvoid MasterServer::load_index() {\\n    if(config.index_dir.empty())\\n        return;\\n\\n    // Load ProjectIndex.\\n    auto project_path = path::join(config.index_dir, \\\"project.idx\\\");\\n    auto buf = llvm::MemoryBuffer::getFile(project_path);\\n    if(buf) {\\n        project_index = index::ProjectIndex::from((*buf)->getBufferStart());\\n        LOG_INFO(\\\"Loaded ProjectIndex: {} symbols\\\", project_index.symbols.size());\\n    }\\n\\n    // Load MergedIndex shards.\\n    auto shards_dir = path::join(config.index_dir, \\\"shards\\\");\\n    std::error_code ec;\\n    for(auto it = llvm::sys::fs::directory_iterator(shards_dir, ec);\\n        !ec && it != llvm::sys::fs::directory_iterator();\\n        it.increment(ec)) {\\n        auto filename = llvm::sys::path::filename(it->path());\\n        if(!filename.ends_with(\\\".idx\\\"))\\n            continue;\\n\\n        auto stem = filename.drop_back(4);  // remove \\\".idx\\\"\\n        std::uint32_t path_id = 0;\\n        if(stem.getAsInteger(10, path_id))\\n            continue;\\n\\n        merged_indices[path_id] = index::MergedIndex::load(it->path());\\n    }\\n\\n    if(!merged_indices.empty()) {\\n        LOG_INFO(\\\"Loaded {} MergedIndex shards\\\", merged_indices.size());\\n    }\\n}\\n\\nvoid MasterServer::schedule_indexing() {\\n    LOG_INFO(\\n        \\\"schedule_indexing called: enable={} active={} scheduled={} queue_size={} queue_pos={}\\\",\\n        config.enable_indexing,\\n        indexing_active,\\n        indexing_scheduled,\\n        index_queue.size(),\\n        index_queue_pos);\\n    if(!config.enable_indexing || indexing_active || indexing_scheduled)\\n        return;\\n    indexing_scheduled = true;\\n\\n    // Create or reset idle timer.\\n    if(!index_idle_timer) {\\n        index_idle_timer = std::make_shared<et::timer>(et::timer::create(loop));\\n    }\\n    index_idle_timer->start(std::chrono::milliseconds(config.idle_timeout_ms));\\n    loop.schedule(run_background_indexing());\\n}\\n\\net::task<> MasterServer::run_background_indexing() {\\n    // Wait for idle timeout before starting.\\n    if(index_idle_timer) {\\n        co_await index_idle_timer->wait();\\n    }\\n    indexing_scheduled = false;\\n\\n    if(index_queue_pos >= index_queue.size()) {\\n        LOG_DEBUG(\\\"Background indexing: queue exhausted\\\");\\n        co_return;\\n    }\\n\\n    indexing_active = true;\\n    std::size_t processed = 0;\\n\\n    while(index_queue_pos < index_queue.size()) {\\n        auto server_path_id = index_queue[index_queue_pos];\\n        index_queue_pos++;\\n\\n        auto file_path = std::string(path_pool.resolve(server_path_id));\\n\\n        // Note: we do NOT skip open documents here.  Index data is needed for\\n        // cross-file features (references, call hierarchy, type hierarchy, etc.)\\n        // regardless of whether the file is open.\\n\\n        // Check if the index needs update by checking mtime against existing shard.\\n        // If the file is not yet in the project_index path pool, it has never been\\n        // indexed — always proceed.  Only skip when we already have a shard that is\\n        // still fresh.\\n        auto cache_it = project_index.path_pool.find(file_path);\\n        if(cache_it != project_index.path_pool.cache.end()) {\\n            auto proj_path_id = cache_it->second;\\n            auto merged_it = merged_indices.find(proj_path_id);\\n            if(merged_it != merged_indices.end()) {\\n                // Build path mapping for need_update check.\\n                llvm::SmallVector<llvm::StringRef> path_mapping;\\n                for(auto& p: project_index.path_pool.paths) {\\n                    path_mapping.push_back(p);\\n                }\\n                if(!merged_it->second.need_update(path_mapping))\\n                    continue;\\n            }\\n        }\\n\\n        // Prepare IndexParams for the stateless worker.\\n        worker::IndexParams params;\\n        params.file = file_path;\\n        if(!fill_compile_args(file_path, params.directory, params.arguments))\\n            continue;\\n\\n        // Fill PCM deps for module-aware indexing.\\n        for(auto& [pid, pcm_path]: pcm_paths) {\\n            auto mod_it = path_to_module.find(pid);\\n            if(mod_it != path_to_module.end()) {\\n                params.pcms[mod_it->second] = pcm_path;\\n            }\\n        }\\n\\n        LOG_INFO(\\\"Background indexing: {}\\\", file_path);\\n\\n        auto result = co_await pool.send_stateless(params);\\n        if(result.has_value() && result.value().success && !result.value().tu_index_data.empty()) {\\n            LOG_INFO(\\\"Background indexing got TUIndex for {}: {} bytes\\\",\\n                     file_path,\\n                     result.value().tu_index_data.size());\\n            merge_index_result(result.value().tu_index_data.data(),\\n                               result.value().tu_index_data.size());\\n            ++processed;\\n        } else if(result.has_value() && !result.value().success) {\\n            LOG_WARN(\\\"Background index failed for {}: {}\\\", file_path, result.value().error);\\n        } else if(result.has_value() && result.value().tu_index_data.empty()) {\\n            LOG_WARN(\\\"Background index returned empty TUIndex for {}\\\", file_path);\\n        } else {\\n            LOG_WARN(\\\"Background index IPC error for {}: {}\\\", file_path, result.error().message);\\n        }\\n    }\\n\\n    indexing_active = false;\\n    LOG_INFO(\\\"Background indexing complete: {} files processed\\\", processed);\\n\\n    // Persist index to disk after a full pass.\\n    save_index();\\n}\\n\\n// =========================================================================\\n// Include/import completion (handled in master)\\n// =========================================================================\\n\\nPreambleCompletionContext MasterServer::detect_completion_context(const std::string& text,\\n                                                                  uint32_t offset) {\\n    // Find the start of the line containing offset.\\n    auto line_start = text.rfind('\\\\n', offset > 0 ? offset - 1 : 0);\\n    line_start = (line_start == std::string::npos) ? 0 : line_start + 1;\\n\\n    // Find the end of the line.\\n    auto line_end = text.find('\\\\n', offset);\\n    if(line_end == std::string::npos)\\n        line_end = text.size();\\n\\n    // Extract the line up to the cursor position.\\n    auto line = llvm::StringRef(text).slice(line_start, offset);\\n\\n    // Strip leading whitespace.\\n    auto trimmed = line.ltrim();\\n\\n    // Check for #include \\\"prefix or #include <prefix\\n    if(trimmed.starts_with(\\\"#\\\")) {\\n        auto directive = trimmed.drop_front(1).ltrim();\\n        if(directive.consume_front(\\\"include\\\")) {\\n            directive = directive.ltrim();\\n            if(directive.consume_front(\\\"\\\\\\\"\\\")) {\\n                return {CompletionContext::IncludeQuoted, directive.str()};\\n            }\\n            if(directive.consume_front(\\\"<\\\")) {\\n                return {CompletionContext::IncludeAngled, directive.str()};\\n            }\\n        }\\n        // Line starts with # but isn't #include — not a completion context.\\n        return {};\\n    }\\n\\n    // Check for [export] import prefix (without trailing semicolon).\\n    auto import_check = trimmed;\\n    if(import_check.consume_front(\\\"export\\\")) {\\n        import_check = import_check.ltrim();\\n    }\\n    if(import_check.consume_front(\\\"import\\\")) {\\n        import_check = import_check.ltrim();\\n        // Only treat as import if there's no semicolon in what follows.\\n        auto rest_of_line = llvm::StringRef(text).slice(line_start, line_end);\\n        if(!rest_of_line.contains(';')) {\\n            return {CompletionContext::Import, import_check.str()};\\n        }\\n    }\\n\\n    return {};\\n}\\n\\net::serde::RawValue MasterServer::complete_include(const PreambleCompletionContext& ctx,\\n                                                   llvm::StringRef path) {\\n    std::string directory;\\n    std::vector<std::string> arguments;\\n    if(!fill_compile_args(path, directory, arguments))\\n        return et::serde::RawValue{\\\"[]\\\"};\\n\\n    // Convert arguments to const char* array.\\n    std::vector<const char*> args_ptrs;\\n    args_ptrs.reserve(arguments.size());\\n    for(auto& arg: arguments) {\\n        args_ptrs.push_back(arg.c_str());\\n    }\\n\\n    auto config = extract_search_config(args_ptrs, directory);\\n    DirListingCache dir_cache;\\n    auto resolved = resolve_search_config(config, dir_cache);\\n\\n    // Determine search range based on context.\\n    unsigned start_idx = 0;\\n    if(ctx.kind == CompletionContext::IncludeAngled) {\\n        start_idx = resolved.angled_start_idx;\\n    }\\n\\n    // Split prefix into dir_prefix and file_prefix if it contains '/'.\\n    llvm::StringRef prefix_ref(ctx.prefix);\\n    llvm::StringRef dir_prefix;\\n    llvm::StringRef file_prefix = prefix_ref;\\n    auto slash_pos = prefix_ref.rfind('/');\\n    if(slash_pos != llvm::StringRef::npos) {\\n        dir_prefix = prefix_ref.slice(0, slash_pos);\\n        file_prefix = prefix_ref.slice(slash_pos + 1, llvm::StringRef::npos);\\n    }\\n\\n    std::vector<protocol::CompletionItem> items;\\n    llvm::StringSet<> seen;  // Deduplicate entries across search dirs.\\n\\n    for(unsigned i = start_idx; i < resolved.dirs.size(); ++i) {\\n        auto& search_dir = resolved.dirs[i];\\n\\n        // If there's a dir_prefix, resolve the subdirectory.\\n        const llvm::StringSet<>* entries = nullptr;\\n        if(!dir_prefix.empty()) {\\n            llvm::SmallString<256> sub_path(search_dir.path);\\n            llvm::sys::path::append(sub_path, dir_prefix);\\n            entries = resolve_dir(sub_path, dir_cache);\\n        } else {\\n            entries = search_dir.entries;\\n        }\\n\\n        if(!entries)\\n            continue;\\n\\n        for(auto& entry: *entries) {\\n            auto name = entry.getKey();\\n            if(!name.starts_with(file_prefix))\\n                continue;\\n            if(!seen.insert(name).second)\\n                continue;\\n\\n            // Check if this entry is a directory.\\n            llvm::SmallString<256> full_path(search_dir.path);\\n            if(!dir_prefix.empty()) {\\n                llvm::sys::path::append(full_path, dir_prefix);\\n            }\\n            llvm::sys::path::append(full_path, name);\\n\\n            bool is_dir = false;\\n            llvm::sys::fs::is_directory(llvm::Twine(full_path), is_dir);\\n\\n            protocol::CompletionItem item;\\n            if(is_dir) {\\n                item.label = (name + \\\"/\\\").str();\\n            } else {\\n                item.label = name.str();\\n            }\\n            item.kind = protocol::CompletionItemKind::File;\\n            items.push_back(std::move(item));\\n        }\\n    }\\n\\n    auto json = et::serde::json::to_json<et::ipc::lsp_config>(items);\\n    return et::serde::RawValue{json ? std::move(*json) : \\\"[]\\\"};\\n}\\n\\net::serde::RawValue MasterServer::complete_import(const PreambleCompletionContext& ctx) {\\n    std::vector<protocol::CompletionItem> items;\\n    llvm::StringRef prefix_ref(ctx.prefix);\\n\\n    for(auto& [path_id, module_name]: path_to_module) {\\n        llvm::StringRef name_ref(module_name);\\n        if(!name_ref.starts_with(prefix_ref))\\n            continue;\\n\\n        protocol::CompletionItem item;\\n        item.label = module_name;\\n        item.kind = protocol::CompletionItemKind::Module;\\n        item.insert_text = module_name + \\\";\\\";\\n        items.push_back(std::move(item));\\n    }\\n\\n    auto json = et::serde::json::to_json<et::ipc::lsp_config>(items);\\n    return et::serde::RawValue{json ? std::move(*json) : \\\"[]\\\"};\\n}\\n\\n// =========================================================================\\n// Forwarding helpers\\n// =========================================================================\\n\\nusing serde_raw = et::serde::RawValue;\\n\\ntemplate <typename WorkerParams>\\nMasterServer::RawResult MasterServer::forward_stateful(const std::string& uri) {\\n    auto path = uri_to_path(uri);\\n    auto path_id = path_pool.intern(path);\\n\\n    LOG_INFO(\\\"forward_stateful: request path={}\\\", path);\\n\\n    if(!co_await ensure_compiled(path_id)) {\\n        LOG_DEBUG(\\\"forward_stateful: ensure_compiled failed for {}\\\", path);\\n        co_return serde_raw{\\\"null\\\"};\\n    }\\n\\n    WorkerParams wp;\\n    wp.path = path;\\n\\n    LOG_INFO(\\\"forward_stateful: sending feature to worker path={}\\\", path);\\n    auto result = co_await pool.send_stateful(path_id, wp);\\n    LOG_INFO(\\\"forward_stateful: feature response path={} ok={}\\\", path, result.has_value());\\n    if(!result.has_value()) {\\n        co_return serde_raw{};\\n    }\\n    co_return std::move(result.value());\\n}\\n\\ntemplate <typename WorkerParams>\\nMasterServer::RawResult MasterServer::forward_stateful(const std::string& uri,\\n                                                       const protocol::Position& position) {\\n    auto path = uri_to_path(uri);\\n    auto path_id = path_pool.intern(path);\\n\\n    LOG_INFO(\\\"forward_stateful(pos): request path={} pos={}:{}\\\",\\n             path,\\n             position.line,\\n             position.character);\\n\\n    if(!co_await ensure_compiled(path_id)) {\\n        LOG_INFO(\\\"forward_stateful(pos): ensure_compiled failed for {}\\\", path);\\n        co_return serde_raw{\\\"null\\\"};\\n    }\\n\\n    WorkerParams wp;\\n    wp.path = path;\\n\\n    auto doc_it = documents.find(path_id);\\n    if(doc_it != documents.end()) {\\n        lsp::PositionMapper mapper(doc_it->second.text, lsp::PositionEncoding::UTF16);\\n        auto offset = mapper.to_offset(position);\\n        if(!offset)\\n            co_return serde_raw{\\\"null\\\"};\\n        wp.offset = *offset;\\n    }\\n\\n    LOG_INFO(\\\"forward_stateful(pos): sending feature to worker path={}\\\", path);\\n    auto result = co_await pool.send_stateful(path_id, wp);\\n    LOG_INFO(\\\"forward_stateful(pos): feature response path={} ok={}\\\", path, result.has_value());\\n    if(!result.has_value()) {\\n        co_return serde_raw{};\\n    }\\n    co_return std::move(result.value());\\n}\\n\\ntemplate <typename WorkerParams>\\nMasterServer::RawResult MasterServer::forward_stateless(const std::string& uri,\\n                                                        const protocol::Position& position) {\\n    auto path = uri_to_path(uri);\\n    auto path_id = path_pool.intern(path);\\n\\n    LOG_DEBUG(\\\"forward_stateless: {} path={} pos={}:{}\\\",\\n              \\\"request\\\",\\n              path,\\n              position.line,\\n              position.character);\\n\\n    auto doc_it = documents.find(path_id);\\n    if(doc_it == documents.end()) {\\n        LOG_DEBUG(\\\"forward_stateless: doc not found for {}\\\", path);\\n        co_return serde_raw{};\\n    }\\n\\n    auto& doc = doc_it->second;\\n\\n    WorkerParams wp;\\n    wp.path = path;\\n    wp.version = doc.version;\\n    wp.text = doc.text;\\n    if(!fill_compile_args(path, wp.directory, wp.arguments)) {\\n        LOG_DEBUG(\\\"forward_stateless: no CDB for {}\\\", path);\\n        co_return serde_raw{};\\n    }\\n\\n    // Ensure module deps, PCH, and PCM paths are ready for stateless compilation.\\n    if(!co_await ensure_deps(path_id, path, wp.text, wp.directory, wp.arguments, wp.pch, wp.pcms)) {\\n        LOG_DEBUG(\\\"forward_stateless: ensure_deps failed for {}\\\", path);\\n        co_return serde_raw{};\\n    }\\n\\n    lsp::PositionMapper mapper(wp.text, lsp::PositionEncoding::UTF16);\\n    auto offset = mapper.to_offset(position);\\n    if(!offset)\\n        co_return serde_raw{\\\"null\\\"};\\n    wp.offset = *offset;\\n\\n    auto result = co_await pool.send_stateless(wp);\\n    if(!result.has_value()) {\\n        LOG_DEBUG(\\\"forward_stateless: worker error for {}: {}\\\", path, result.error().message);\\n        co_return serde_raw{};\\n    }\\n    LOG_DEBUG(\\\"forward_stateless: done {}\\\", path);\\n    co_return std::move(result.value());\\n}\\n\\n// Serialize a value to a JSON RawValue using LSP config.\\ntemplate <typename T>\\nstatic serde_raw to_raw(const T& value) {\\n    auto json = et::serde::json::to_json<et::ipc::lsp_config>(value);\\n    return serde_raw{json ? std::move(*json) : \\\"null\\\"};\\n}\\n\\nMasterServer::RawResult MasterServer::query_index_relations(const std::string& uri,\\n                                                            const protocol::Position& position,\\n                                                            RelationKind kind) {\\n    auto path = uri_to_path(uri);\\n    auto server_path_id = path_pool.intern(path);\\n\\n    // Need document text for position-to-offset conversion.\\n    auto doc_it = documents.find(server_path_id);\\n    if(doc_it == documents.end())\\n        co_return serde_raw{\\\"null\\\"};\\n\\n    lsp::PositionMapper mapper(doc_it->second.text, lsp::PositionEncoding::UTF16);\\n    auto offset_opt = mapper.to_offset(position);\\n    if(!offset_opt)\\n        co_return serde_raw{\\\"null\\\"};\\n    auto offset = *offset_opt;\\n\\n    // Find the project-level path_id for this file.\\n    auto proj_cache_it = project_index.path_pool.find(path);\\n    if(proj_cache_it == project_index.path_pool.cache.end()) {\\n        LOG_DEBUG(\\\"query_index_relations: path '{}' not in project_index\\\", path);\\n        co_return serde_raw{\\\"null\\\"};\\n    }\\n    auto proj_path_id = proj_cache_it->second;\\n\\n    // Lookup occurrence at offset in this file's MergedIndex.\\n    auto merged_it = merged_indices.find(proj_path_id);\\n    if(merged_it == merged_indices.end()) {\\n        LOG_DEBUG(\\\"query_index_relations: no MergedIndex for proj_path_id={}\\\", proj_path_id);\\n        co_return serde_raw{\\\"null\\\"};\\n    }\\n\\n    index::SymbolHash symbol_hash = 0;\\n    merged_it->second.lookup(offset, [&](const index::Occurrence& o) {\\n        symbol_hash = o.target;\\n        return false;  // stop after first match\\n    });\\n\\n    if(symbol_hash == 0) {\\n        LOG_DEBUG(\\\"query_index_relations: no occurrence at offset {} in '{}'\\\", offset, path);\\n        co_return serde_raw{\\\"null\\\"};\\n    }\\n\\n    // Get reference files from ProjectIndex.\\n    auto sym_it = project_index.symbols.find(symbol_hash);\\n    if(sym_it == project_index.symbols.end()) {\\n        LOG_DEBUG(\\\"query_index_relations: symbol {} not in project_index\\\", symbol_hash);\\n        co_return serde_raw{\\\"null\\\"};\\n    }\\n\\n    // Query each referenced file's MergedIndex for relations of the requested kind.\\n    std::vector<protocol::Location> locations;\\n\\n    for(auto file_id: sym_it->second.reference_files) {\\n        auto file_merged_it = merged_indices.find(file_id);\\n        if(file_merged_it == merged_indices.end())\\n            continue;\\n\\n        auto file_path = project_index.path_pool.path(file_id);\\n        auto file_uri = lsp::URI::from_file_path(file_path);\\n        if(!file_uri)\\n            continue;\\n        auto file_uri_str = file_uri->str();\\n\\n        // Use stored content from MergedIndex for offset-to-position conversion.\\n        auto file_content = file_merged_it->second.content();\\n        if(file_content.empty())\\n            continue;\\n\\n        lsp::PositionMapper file_mapper(file_content, lsp::PositionEncoding::UTF16);\\n\\n        file_merged_it->second.lookup(symbol_hash, kind, [&](const index::Relation& r) {\\n            auto start = file_mapper.to_position(r.range.begin);\\n            auto end = file_mapper.to_position(r.range.end);\\n            if(start && end) {\\n                protocol::Location loc;\\n                loc.uri = file_uri_str;\\n                loc.range = protocol::Range{*start, *end};\\n                locations.push_back(std::move(loc));\\n            }\\n            return true;  // continue collecting\\n        });\\n    }\\n\\n    if(locations.empty())\\n        co_return serde_raw{\\\"null\\\"};\\n\\n    co_return to_raw(locations);\\n}\\n\\nprotocol::SymbolKind MasterServer::to_lsp_symbol_kind(SymbolKind kind) {\\n    switch(kind) {\\n        case SymbolKind::Namespace: return protocol::SymbolKind::Namespace;\\n        case SymbolKind::Class: return protocol::SymbolKind::Class;\\n        case SymbolKind::Struct: return protocol::SymbolKind::Struct;\\n        case SymbolKind::Union: return protocol::SymbolKind::Class;\\n        case SymbolKind::Enum: return protocol::SymbolKind::Enum;\\n        case SymbolKind::Type: return protocol::SymbolKind::TypeParameter;\\n        case SymbolKind::Field: return protocol::SymbolKind::Field;\\n        case SymbolKind::EnumMember: return protocol::SymbolKind::EnumMember;\\n        case SymbolKind::Function: return protocol::SymbolKind::Function;\\n        case SymbolKind::Method: return protocol::SymbolKind::Method;\\n        case SymbolKind::Variable: return protocol::SymbolKind::Variable;\\n        case SymbolKind::Parameter: return protocol::SymbolKind::Variable;\\n        case SymbolKind::Macro: return protocol::SymbolKind::Function;\\n        case SymbolKind::Concept: return protocol::SymbolKind::Interface;\\n        case SymbolKind::Module: return protocol::SymbolKind::Module;\\n        case SymbolKind::Operator: return protocol::SymbolKind::Operator;\\n        default: return protocol::SymbolKind::Variable;\\n    }\\n}\\n\\net::task<std::optional<SymbolInfo>>\\n    MasterServer::lookup_symbol_at_position(const std::string& uri,\\n                                            const protocol::Position& position) {\\n    auto path = uri_to_path(uri);\\n    auto server_path_id = path_pool.intern(path);\\n\\n    // Need document text for position-to-offset conversion.\\n    auto doc_it = documents.find(server_path_id);\\n    if(doc_it == documents.end())\\n        co_return std::nullopt;\\n\\n    lsp::PositionMapper mapper(doc_it->second.text, lsp::PositionEncoding::UTF16);\\n    auto offset_opt = mapper.to_offset(position);\\n    if(!offset_opt)\\n        co_return std::nullopt;\\n    auto offset = *offset_opt;\\n\\n    // Find the project-level path_id for this file.\\n    auto proj_cache_it = project_index.path_pool.find(path);\\n    if(proj_cache_it == project_index.path_pool.cache.end()) {\\n        LOG_WARN(\\\"lookup_symbol: path '{}' not in project_index (pool has {} entries)\\\",\\n                 path,\\n                 project_index.path_pool.paths.size());\\n        co_return std::nullopt;\\n    }\\n    auto proj_path_id = proj_cache_it->second;\\n\\n    // Lookup occurrence at offset in this file's MergedIndex.\\n    auto merged_it = merged_indices.find(proj_path_id);\\n    if(merged_it == merged_indices.end()) {\\n        LOG_WARN(\\\"lookup_symbol: no MergedIndex for proj_path_id={} (have {} shards)\\\",\\n                 proj_path_id,\\n                 merged_indices.size());\\n        co_return std::nullopt;\\n    }\\n\\n    index::SymbolHash symbol_hash = 0;\\n    index::Range occ_range{};\\n    merged_it->second.lookup(offset, [&](const index::Occurrence& o) {\\n        symbol_hash = o.target;\\n        occ_range = o.range;\\n        return false;  // stop after first match\\n    });\\n\\n    if(symbol_hash == 0) {\\n        LOG_WARN(\\\"lookup_symbol: no occurrence at offset {} in '{}'\\\", offset, path);\\n        co_return std::nullopt;\\n    }\\n\\n    // Get symbol info from ProjectIndex.\\n    auto sym_it = project_index.symbols.find(symbol_hash);\\n    if(sym_it == project_index.symbols.end())\\n        co_return std::nullopt;\\n\\n    // Convert occurrence range to LSP Range.\\n    auto start = mapper.to_position(occ_range.begin);\\n    auto end = mapper.to_position(occ_range.end);\\n    if(!start || !end)\\n        co_return std::nullopt;\\n\\n    SymbolInfo info;\\n    info.hash = symbol_hash;\\n    info.name = sym_it->second.name;\\n    info.kind = sym_it->second.kind;\\n    info.uri = uri;\\n    info.range = protocol::Range{*start, *end};\\n    co_return info;\\n}\\n\\nstd::optional<protocol::Location>\\n    MasterServer::find_symbol_definition_location(index::SymbolHash hash) {\\n    auto sym_it = project_index.symbols.find(hash);\\n    if(sym_it == project_index.symbols.end())\\n        return std::nullopt;\\n\\n    // Search reference files for a Definition relation.\\n    for(auto file_id: sym_it->second.reference_files) {\\n        auto file_merged_it = merged_indices.find(file_id);\\n        if(file_merged_it == merged_indices.end())\\n            continue;\\n\\n        auto file_path = project_index.path_pool.path(file_id);\\n        auto file_uri = lsp::URI::from_file_path(file_path);\\n        if(!file_uri)\\n            continue;\\n\\n        // Use stored content from MergedIndex for offset-to-position conversion.\\n        auto file_content = file_merged_it->second.content();\\n        if(file_content.empty())\\n            continue;\\n        lsp::PositionMapper file_mapper(file_content, lsp::PositionEncoding::UTF16);\\n\\n        std::optional<protocol::Location> result;\\n        file_merged_it->second.lookup(hash,\\n                                      RelationKind::Definition,\\n                                      [&](const index::Relation& r) {\\n                                          auto start = file_mapper.to_position(r.range.begin);\\n                                          auto end = file_mapper.to_position(r.range.end);\\n                                          if(start && end) {\\n                                              protocol::Location loc;\\n                                              loc.uri = file_uri->str();\\n                                              loc.range = protocol::Range{*start, *end};\\n                                              result = std::move(loc);\\n                                              return false;  // found it, stop\\n                                          }\\n                                          return true;\\n                                      });\\n\\n        if(result)\\n            return result;\\n    }\\n\\n    return std::nullopt;\\n}\\n\\nprotocol::CallHierarchyItem MasterServer::build_call_hierarchy_item(const SymbolInfo& info) {\\n    protocol::CallHierarchyItem item;\\n    item.name = info.name;\\n    item.kind = to_lsp_symbol_kind(info.kind);\\n    item.uri = info.uri;\\n    item.range = info.range;\\n    item.selection_range = info.range;\\n    // Store the symbol hash in data for later use in incoming/outgoing calls.\\n    item.data = protocol::LSPAny(static_cast<std::int64_t>(info.hash));\\n    return item;\\n}\\n\\nprotocol::TypeHierarchyItem MasterServer::build_type_hierarchy_item(const SymbolInfo& info) {\\n    protocol::TypeHierarchyItem item;\\n    item.name = info.name;\\n    item.kind = to_lsp_symbol_kind(info.kind);\\n    item.uri = info.uri;\\n    item.range = info.range;\\n    item.selection_range = info.range;\\n    item.data = protocol::LSPAny(static_cast<std::int64_t>(info.hash));\\n    return item;\\n}\\n\\net::task<std::optional<SymbolInfo>>\\n    MasterServer::resolve_hierarchy_item(const std::string& uri,\\n                                         const protocol::Range& range,\\n                                         const std::optional<protocol::LSPAny>& data) {\\n    // Try to extract symbol hash from the stored data field first.\\n    if(data) {\\n        if(auto* int_val = std::get_if<std::int64_t>(&*data)) {\\n            auto hash = static_cast<index::SymbolHash>(*int_val);\\n            auto sym_it = project_index.symbols.find(hash);\\n            if(sym_it != project_index.symbols.end()) {\\n                SymbolInfo info;\\n                info.hash = hash;\\n                info.name = sym_it->second.name;\\n                info.kind = sym_it->second.kind;\\n                info.uri = uri;\\n                info.range = range;\\n                co_return info;\\n            }\\n        }\\n    }\\n\\n    // Fallback: re-lookup from position (requires document to be open).\\n    co_return co_await lookup_symbol_at_position(uri, range.start);\\n}\\n\\nvoid MasterServer::register_handlers() {\\n    // === initialize ===\\n    peer.on_request([this](RequestContext& ctx, const protocol::InitializeParams& params)\\n                        -> RequestResult<protocol::InitializeParams> {\\n        if(lifecycle != ServerLifecycle::Uninitialized) {\\n            co_return et::outcome_error(protocol::Error{\\\"Server already initialized\\\"});\\n        }\\n\\n        // Extract workspace root\\n        auto& init = params.lsp__initialize_params;\\n        if(init.root_uri.has_value()) {\\n            workspace_root = uri_to_path(*init.root_uri);\\n        }\\n\\n        lifecycle = ServerLifecycle::Initialized;\\n\\n        LOG_INFO(\\\"Initialized with workspace: {}\\\", workspace_root);\\n\\n        // Build capabilities\\n        protocol::InitializeResult result;\\n\\n        // Text document sync: incremental\\n        protocol::TextDocumentSyncOptions sync_opts;\\n        sync_opts.open_close = true;\\n        sync_opts.change = protocol::TextDocumentSyncKind::Incremental;\\n        sync_opts.save = protocol::variant<protocol::boolean, protocol::SaveOptions>{true};\\n        result.capabilities.text_document_sync = std::move(sync_opts);\\n\\n        // Feature capabilities\\n        result.capabilities.hover_provider = true;\\n        result.capabilities.completion_provider = protocol::CompletionOptions{};\\n        result.capabilities.signature_help_provider = protocol::SignatureHelpOptions{};\\n        result.capabilities.definition_provider = true;\\n        result.capabilities.references_provider = true;\\n        result.capabilities.document_symbol_provider = true;\\n        result.capabilities.document_link_provider = protocol::DocumentLinkOptions{};\\n        result.capabilities.code_action_provider = true;\\n        result.capabilities.folding_range_provider = true;\\n        result.capabilities.inlay_hint_provider = true;\\n        result.capabilities.call_hierarchy_provider = true;\\n        result.capabilities.type_hierarchy_provider = true;\\n        result.capabilities.workspace_symbol_provider = true;\\n\\n        // Semantic tokens\\n        protocol::SemanticTokensOptions sem_opts;\\n        {\\n            auto lower_first = [](std::string_view name) -> std::string {\\n                std::string s(name);\\n                if(!s.empty()) {\\n                    s[0] = static_cast<char>(std::tolower(static_cast<unsigned char>(s[0])));\\n                }\\n                return s;\\n            };\\n\\n            auto to_names = [&](auto names) {\\n                return std::ranges::to<std::vector>(names | std::views::transform(lower_first));\\n            };\\n\\n            sem_opts.legend = protocol::SemanticTokensLegend{\\n                to_names(refl::reflection<SymbolKind::Kind>::member_names),\\n                to_names(refl::reflection<SymbolModifiers::Kind>::member_names),\\n            };\\n        }\\n        sem_opts.full = true;\\n        result.capabilities.semantic_tokens_provider = std::move(sem_opts);\\n\\n        // Server info\\n        protocol::ServerInfo info;\\n        info.name = \\\"clice\\\";\\n        info.version = \\\"0.1.0\\\";\\n        result.server_info = std::move(info);\\n\\n        co_return result;\\n    });\\n\\n    // === initialized ===\\n    peer.on_notification([this](const protocol::InitializedParams& params) {\\n        // Load configuration from workspace\\n        config = CliceConfig::load_from_workspace(workspace_root);\\n\\n        // Switch master to file logging under a session-timestamped directory\\n        if(!config.logging_dir.empty()) {\\n            auto now = std::chrono::system_clock::now();\\n            auto pid = llvm::sys::Process::getProcessId();\\n            auto session_dir =\\n                path::join(config.logging_dir, std::format(\\\"{:%Y-%m-%d_%H-%M-%S}_{}\\\", now, pid));\\n            logging::file_logger(\\\"master\\\", session_dir, logging::options);\\n            session_log_dir = session_dir;\\n        }\\n\\n        LOG_INFO(\\\"Server ready (stateful={}, stateless={}, idle={}ms)\\\",\\n                 config.stateful_worker_count,\\n                 config.stateless_worker_count,\\n                 config.idle_timeout_ms);\\n\\n        // Start worker pool\\n        WorkerPoolOptions pool_opts;\\n        pool_opts.self_path = self_path;\\n        pool_opts.stateful_count = config.stateful_worker_count;\\n        pool_opts.stateless_count = config.stateless_worker_count;\\n        pool_opts.worker_memory_limit = config.worker_memory_limit;\\n        pool_opts.log_dir = session_log_dir;\\n        if(!pool.start(pool_opts)) {\\n            LOG_ERROR(\\\"Failed to start worker pool\\\");\\n            return;\\n        }\\n\\n        lifecycle = ServerLifecycle::Ready;\\n\\n        // Load CDB in background\\n        loop.schedule(load_workspace());\\n    });\\n\\n    // === shutdown ===\\n    peer.on_request(\\n        [this](RequestContext& ctx,\\n               const protocol::ShutdownParams& params) -> RequestResult<protocol::ShutdownParams> {\\n            lifecycle = ServerLifecycle::ShuttingDown;\\n            LOG_INFO(\\\"Shutdown requested\\\");\\n            co_return nullptr;\\n        });\\n\\n    // === exit ===\\n    peer.on_notification([this](const protocol::ExitParams& params) {\\n        lifecycle = ServerLifecycle::Exited;\\n        LOG_INFO(\\\"Exit notification received\\\");\\n\\n        // Persist index and cache state before stopping.\\n        save_index();\\n        save_cache();\\n\\n        // Graceful shutdown: cancel compilations, stop workers, then stop loop\\n        loop.schedule([this]() -> et::task<> {\\n            co_await pool.stop();\\n            loop.stop();\\n        }());\\n    });\\n\\n    // === textDocument/didOpen ===\\n    peer.on_notification([this](const protocol::DidOpenTextDocumentParams& params) {\\n        if(lifecycle != ServerLifecycle::Ready)\\n            return;\\n\\n        auto& td = params.text_document;\\n        auto path = uri_to_path(td.uri);\\n        auto path_id = path_pool.intern(path);\\n\\n        auto& doc = documents[path_id];\\n        doc.version = td.version;\\n        doc.text = td.text;\\n        doc.generation++;\\n\\n        LOG_DEBUG(\\\"didOpen: {} (v{})\\\", path, td.version);\\n    });\\n\\n    // === textDocument/didChange ===\\n    peer.on_notification([this](const protocol::DidChangeTextDocumentParams& params) {\\n        if(lifecycle != ServerLifecycle::Ready)\\n            return;\\n\\n        auto path = uri_to_path(params.text_document.uri);\\n        auto path_id = path_pool.intern(path);\\n\\n        auto it = documents.find(path_id);\\n        if(it == documents.end())\\n            return;\\n\\n        auto& doc = it->second;\\n        doc.version = params.text_document.version;\\n\\n        // Apply content changes.\\n        for(auto& change: params.content_changes) {\\n            std::visit(\\n                [&](auto& c) {\\n                    using T = std::remove_cvref_t<decltype(c)>;\\n                    if constexpr(std::is_same_v<T,\\n                                                protocol::TextDocumentContentChangeWholeDocument>) {\\n                        doc.text = c.text;\\n                    } else {\\n                        // Incremental change: replace range\\n                        auto& range = c.range;\\n                        lsp::PositionMapper mapper(doc.text, lsp::PositionEncoding::UTF16);\\n                        auto start = mapper.to_offset(range.start);\\n                        auto end = mapper.to_offset(range.end);\\n                        if(start && end && *start <= *end) {\\n                            doc.text.replace(*start, *end - *start, c.text);\\n                        }\\n                    }\\n                },\\n                change);\\n        }\\n\\n        doc.generation++;\\n        doc.ast_dirty = true;\\n\\n        LOG_DEBUG(\\\"didChange: path={} version={} gen={}\\\", path, doc.version, doc.generation);\\n\\n        // Notify the owning stateful worker so it marks the document dirty\\n        worker::DocumentUpdateParams update;\\n        update.path = path;\\n        update.version = doc.version;\\n        pool.notify_stateful(path_id, update);\\n    });\\n\\n    // === textDocument/didClose ===\\n    peer.on_notification([this](const protocol::DidCloseTextDocumentParams& params) {\\n        if(lifecycle != ServerLifecycle::Ready)\\n            return;\\n\\n        auto path = uri_to_path(params.text_document.uri);\\n        auto path_id = path_pool.intern(path);\\n\\n        // Cancel in-flight module compilations for this file.\\n        if(compile_graph && compile_graph->has_unit(path_id)) {\\n            compile_graph->update(path_id);\\n        }\\n\\n        documents.erase(path_id);\\n        pch_states.erase(path_id);\\n        ast_deps.erase(path_id);\\n\\n        // Clear diagnostics for closed file\\n        clear_diagnostics(params.text_document.uri);\\n\\n        LOG_DEBUG(\\\"didClose: {}\\\", path);\\n    });\\n\\n    // === textDocument/didSave ===\\n    peer.on_notification([this](const protocol::DidSaveTextDocumentParams& params) {\\n        if(lifecycle != ServerLifecycle::Ready)\\n            return;\\n\\n        auto path = uri_to_path(params.text_document.uri);\\n        auto path_id = path_pool.intern(path);\\n\\n        // Invalidate this file and cascade to dependents in the compile graph.\\n        if(compile_graph) {\\n            auto dirtied = compile_graph->update(path_id);\\n            // Remove stale PCMs for all invalidated units.\\n            for(auto dirty_id: dirtied) {\\n                pcm_paths.erase(dirty_id);\\n                pcm_states.erase(dirty_id);\\n            }\\n            // Mark ast_dirty for open documents that depend on the saved file.\\n            for(auto dirty_id: dirtied) {\\n                auto doc_it = documents.find(dirty_id);\\n                if(doc_it != documents.end()) {\\n                    doc_it->second.ast_dirty = true;\\n                }\\n            }\\n        }\\n\\n        // Trigger background indexing after save.\\n        schedule_indexing();\\n\\n        LOG_DEBUG(\\\"didSave: {}\\\", params.text_document.uri);\\n    });\\n\\n    // =========================================================================\\n    // Feature requests routed to stateful workers (RawValue passthrough)\\n    // =========================================================================\\n\\n    // --- textDocument/hover ---\\n    peer.on_request([this](RequestContext& ctx, const protocol::HoverParams& params) -> RawResult {\\n        co_return co_await forward_stateful<worker::HoverParams>(\\n            params.text_document_position_params.text_document.uri,\\n            params.text_document_position_params.position);\\n    });\\n\\n    // --- textDocument/semanticTokens/full ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::SemanticTokensParams& params) -> RawResult {\\n        co_return co_await forward_stateful<worker::SemanticTokensParams>(params.text_document.uri);\\n    });\\n\\n    // --- textDocument/inlayHint ---\\n    peer.on_request(\\n        [this](RequestContext& ctx, const protocol::InlayHintParams& params) -> RawResult {\\n            co_return co_await forward_stateful<worker::InlayHintsParams>(params.text_document.uri);\\n        });\\n\\n    // --- textDocument/foldingRange ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::FoldingRangeParams& params) -> RawResult {\\n        co_return co_await forward_stateful<worker::FoldingRangeParams>(params.text_document.uri);\\n    });\\n\\n    // --- textDocument/documentSymbol ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::DocumentSymbolParams& params) -> RawResult {\\n        co_return co_await forward_stateful<worker::DocumentSymbolParams>(params.text_document.uri);\\n    });\\n\\n    // --- textDocument/documentLink ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::DocumentLinkParams& params) -> RawResult {\\n        co_return co_await forward_stateful<worker::DocumentLinkParams>(params.text_document.uri);\\n    });\\n\\n    // --- textDocument/codeAction ---\\n    peer.on_request(\\n        [this](RequestContext& ctx, const protocol::CodeActionParams& params) -> RawResult {\\n            co_return co_await forward_stateful<worker::CodeActionParams>(params.text_document.uri);\\n        });\\n\\n    // --- textDocument/definition ---\\n    peer.on_request(\\n        [this](RequestContext& ctx, const protocol::DefinitionParams& params) -> RawResult {\\n            auto& uri = params.text_document_position_params.text_document.uri;\\n            auto& pos = params.text_document_position_params.position;\\n\\n            // Try index-based lookup first.\\n            auto result = co_await query_index_relations(uri, pos, RelationKind::Definition);\\n            if(result.has_value() && !result.value().empty() && result.value().data != \\\"null\\\") {\\n                co_return std::move(result).value();\\n            }\\n\\n            // Fall back to stateful worker AST query.\\n            co_return co_await forward_stateful<worker::GoToDefinitionParams>(uri, pos);\\n        });\\n\\n    // --- textDocument/references ---\\n    peer.on_request(\\n        [this](RequestContext& ctx, const protocol::ReferenceParams& params) -> RawResult {\\n            auto& uri = params.text_document_position_params.text_document.uri;\\n            auto& pos = params.text_document_position_params.position;\\n\\n            auto refs = co_await query_index_relations(uri, pos, RelationKind::Reference);\\n\\n            if(params.context.include_declaration) {\\n                // Also include Definition locations when the client requests the declaration.\\n                auto defs = co_await query_index_relations(uri, pos, RelationKind::Definition);\\n                if(defs.has_value() && !defs.value().empty() && defs.value().data != \\\"null\\\") {\\n                    if(!refs.has_value() || refs.value().empty() || refs.value().data == \\\"null\\\") {\\n                        co_return std::move(defs).value();\\n                    }\\n                    // Merge: parse both JSON arrays and concatenate.\\n                    auto& ref_json = refs.value().data;\\n                    auto& def_json = defs.value().data;\\n                    // Both are JSON arrays like \\\"[...]\\\". Merge them.\\n                    if(ref_json.size() > 2 && def_json.size() > 2) {\\n                        // Remove trailing ']' from refs, add comma, add def content without leading\\n                        // '['\\n                        std::string merged = ref_json.substr(0, ref_json.size() - 1);\\n                        merged += ',';\\n                        merged += def_json.substr(1);\\n                        co_return serde_raw{std::move(merged)};\\n                    }\\n                }\\n            }\\n\\n            co_return refs;\\n        });\\n\\n    // =========================================================================\\n    // Feature requests routed to stateless workers\\n    // =========================================================================\\n\\n    // --- textDocument/completion ---\\n    peer.on_request(\\n        [this](RequestContext& ctx, const protocol::CompletionParams& params) -> RawResult {\\n            auto uri = params.text_document_position_params.text_document.uri;\\n            auto position = params.text_document_position_params.position;\\n\\n            // Check if cursor is on an #include or import line.\\n            auto path = uri_to_path(uri);\\n            auto path_id = path_pool.intern(path);\\n            auto doc_it = documents.find(path_id);\\n            if(doc_it != documents.end()) {\\n                lsp::PositionMapper mapper(doc_it->second.text, lsp::PositionEncoding::UTF16);\\n                auto offset = mapper.to_offset(position);\\n                if(offset) {\\n                    auto pctx = detect_completion_context(doc_it->second.text, *offset);\\n                    if(pctx.kind == CompletionContext::IncludeQuoted ||\\n                       pctx.kind == CompletionContext::IncludeAngled) {\\n                        co_return complete_include(pctx, path);\\n                    }\\n                    if(pctx.kind == CompletionContext::Import) {\\n                        co_return complete_import(pctx);\\n                    }\\n                }\\n            }\\n\\n            // Default: forward to stateless worker.\\n            co_return co_await forward_stateless<worker::CompletionParams>(uri, position);\\n        });\\n\\n    // --- textDocument/signatureHelp ---\\n    peer.on_request(\\n        [this](RequestContext& ctx, const protocol::SignatureHelpParams& params) -> RawResult {\\n            co_return co_await forward_stateless<worker::SignatureHelpParams>(\\n                params.text_document_position_params.text_document.uri,\\n                params.text_document_position_params.position);\\n        });\\n\\n    // =========================================================================\\n    // Hierarchy and workspace symbol handlers (index-based)\\n    // =========================================================================\\n\\n    // --- textDocument/prepareCallHierarchy ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::CallHierarchyPrepareParams& params) -> RawResult {\\n        auto info = co_await lookup_symbol_at_position(\\n            params.text_document_position_params.text_document.uri,\\n            params.text_document_position_params.position);\\n        if(!info)\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        // Only functions/methods are valid for call hierarchy.\\n        if(!(info->kind == SymbolKind::Function || info->kind == SymbolKind::Method))\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        std::vector<protocol::CallHierarchyItem> items;\\n        items.push_back(build_call_hierarchy_item(*info));\\n        co_return to_raw(items);\\n    });\\n\\n    // --- callHierarchy/incomingCalls ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::CallHierarchyIncomingCallsParams& params) -> RawResult {\\n        // Re-lookup the symbol from the item.\\n        auto info =\\n            co_await resolve_hierarchy_item(params.item.uri, params.item.range, params.item.data);\\n        if(!info)\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        auto sym_it = project_index.symbols.find(info->hash);\\n        if(sym_it == project_index.symbols.end())\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        // Collect all Caller relations across reference files.\\n        // Caller relation: on the callee symbol, target_symbol is the caller's hash.\\n        std::vector<protocol::CallHierarchyIncomingCall> results;\\n\\n        // Group call sites by caller symbol hash.\\n        llvm::DenseMap<index::SymbolHash, std::vector<protocol::Range>> caller_ranges;\\n\\n        for(auto file_id: sym_it->second.reference_files) {\\n            auto file_merged_it = merged_indices.find(file_id);\\n            if(file_merged_it == merged_indices.end())\\n                continue;\\n\\n            // Use stored content from MergedIndex for offset-to-position conversion.\\n            auto file_content = file_merged_it->second.content();\\n            if(file_content.empty())\\n                continue;\\n\\n            lsp::PositionMapper file_mapper(file_content, lsp::PositionEncoding::UTF16);\\n\\n            file_merged_it->second.lookup(info->hash,\\n                                          RelationKind::Caller,\\n                                          [&](const index::Relation& r) {\\n                                              auto start = file_mapper.to_position(r.range.begin);\\n                                              auto end = file_mapper.to_position(r.range.end);\\n                                              if(start && end) {\\n                                                  caller_ranges[r.target_symbol].push_back(\\n                                                      protocol::Range{*start, *end});\\n                                              }\\n                                              return true;\\n                                          });\\n        }\\n\\n        // Build incoming call items from grouped caller symbols.\\n        for(auto& [caller_hash, ranges]: caller_ranges) {\\n            auto def_loc = find_symbol_definition_location(caller_hash);\\n            auto caller_sym_it = project_index.symbols.find(caller_hash);\\n            if(caller_sym_it == project_index.symbols.end())\\n                continue;\\n\\n            if(!def_loc)\\n                continue;\\n\\n            protocol::CallHierarchyItem caller_item;\\n            caller_item.name = caller_sym_it->second.name;\\n            caller_item.kind = to_lsp_symbol_kind(caller_sym_it->second.kind);\\n            caller_item.uri = def_loc->uri;\\n            caller_item.range = def_loc->range;\\n            caller_item.selection_range = def_loc->range;\\n            caller_item.data = protocol::LSPAny(static_cast<std::int64_t>(caller_hash));\\n\\n            protocol::CallHierarchyIncomingCall call;\\n            call.from = std::move(caller_item);\\n            call.from_ranges = std::move(ranges);\\n            results.push_back(std::move(call));\\n        }\\n\\n        if(results.empty())\\n            co_return serde_raw{\\\"null\\\"};\\n        co_return to_raw(results);\\n    });\\n\\n    // --- callHierarchy/outgoingCalls ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::CallHierarchyOutgoingCallsParams& params) -> RawResult {\\n        auto info =\\n            co_await resolve_hierarchy_item(params.item.uri, params.item.range, params.item.data);\\n        if(!info)\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        auto sym_it = project_index.symbols.find(info->hash);\\n        if(sym_it == project_index.symbols.end())\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        // Collect Callee relations (outgoing calls from this function).\\n        // Group call sites by callee symbol hash.\\n        llvm::DenseMap<index::SymbolHash, std::vector<protocol::Range>> callee_ranges;\\n\\n        for(auto file_id: sym_it->second.reference_files) {\\n            auto file_merged_it = merged_indices.find(file_id);\\n            if(file_merged_it == merged_indices.end())\\n                continue;\\n\\n            // Use stored content from MergedIndex for offset-to-position conversion.\\n            auto file_content = file_merged_it->second.content();\\n            if(file_content.empty())\\n                continue;\\n\\n            lsp::PositionMapper file_mapper(file_content, lsp::PositionEncoding::UTF16);\\n\\n            file_merged_it->second.lookup(info->hash,\\n                                          RelationKind::Callee,\\n                                          [&](const index::Relation& r) {\\n                                              auto start = file_mapper.to_position(r.range.begin);\\n                                              auto end = file_mapper.to_position(r.range.end);\\n                                              if(start && end) {\\n                                                  callee_ranges[r.target_symbol].push_back(\\n                                                      protocol::Range{*start, *end});\\n                                              }\\n                                              return true;\\n                                          });\\n        }\\n\\n        std::vector<protocol::CallHierarchyOutgoingCall> results;\\n        for(auto& [callee_hash, ranges]: callee_ranges) {\\n            auto def_loc = find_symbol_definition_location(callee_hash);\\n            auto callee_sym_it = project_index.symbols.find(callee_hash);\\n            if(callee_sym_it == project_index.symbols.end())\\n                continue;\\n\\n            if(!def_loc)\\n                continue;\\n\\n            protocol::CallHierarchyItem callee_item;\\n            callee_item.name = callee_sym_it->second.name;\\n            callee_item.kind = to_lsp_symbol_kind(callee_sym_it->second.kind);\\n            callee_item.uri = def_loc->uri;\\n            callee_item.range = def_loc->range;\\n            callee_item.selection_range = def_loc->range;\\n            callee_item.data = protocol::LSPAny(static_cast<std::int64_t>(callee_hash));\\n\\n            protocol::CallHierarchyOutgoingCall call;\\n            call.to = std::move(callee_item);\\n            call.from_ranges = std::move(ranges);\\n            results.push_back(std::move(call));\\n        }\\n\\n        if(results.empty())\\n            co_return serde_raw{\\\"null\\\"};\\n        co_return to_raw(results);\\n    });\\n\\n    // --- textDocument/prepareTypeHierarchy ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::TypeHierarchyPrepareParams& params) -> RawResult {\\n        auto info = co_await lookup_symbol_at_position(\\n            params.text_document_position_params.text_document.uri,\\n            params.text_document_position_params.position);\\n        if(!info)\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        // Only class-like types are valid for type hierarchy.\\n        if(!(info->kind == SymbolKind::Class || info->kind == SymbolKind::Struct ||\\n             info->kind == SymbolKind::Enum || info->kind == SymbolKind::Union))\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        std::vector<protocol::TypeHierarchyItem> items;\\n        items.push_back(build_type_hierarchy_item(*info));\\n        co_return to_raw(items);\\n    });\\n\\n    // --- typeHierarchy/supertypes ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::TypeHierarchySupertypesParams& params) -> RawResult {\\n        auto info =\\n            co_await resolve_hierarchy_item(params.item.uri, params.item.range, params.item.data);\\n        if(!info)\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        auto sym_it = project_index.symbols.find(info->hash);\\n        if(sym_it == project_index.symbols.end())\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        // Find Base relations: supertypes of this class.\\n        std::vector<protocol::TypeHierarchyItem> results;\\n\\n        for(auto file_id: sym_it->second.reference_files) {\\n            auto file_merged_it = merged_indices.find(file_id);\\n            if(file_merged_it == merged_indices.end())\\n                continue;\\n\\n            file_merged_it->second.lookup(\\n                info->hash,\\n                RelationKind::Base,\\n                [&](const index::Relation& r) {\\n                    auto base_hash = r.target_symbol;\\n                    auto base_sym_it = project_index.symbols.find(base_hash);\\n                    if(base_sym_it == project_index.symbols.end())\\n                        return true;\\n\\n                    // Find the definition location of the base class.\\n                    auto def_loc = find_symbol_definition_location(base_hash);\\n                    if(!def_loc)\\n                        return true;\\n\\n                    protocol::TypeHierarchyItem item;\\n                    item.name = base_sym_it->second.name;\\n                    item.kind = to_lsp_symbol_kind(base_sym_it->second.kind);\\n                    item.uri = def_loc->uri;\\n                    item.range = def_loc->range;\\n                    item.selection_range = def_loc->range;\\n                    item.data = protocol::LSPAny(static_cast<std::int64_t>(base_hash));\\n                    results.push_back(std::move(item));\\n                    return true;\\n                });\\n        }\\n\\n        if(results.empty())\\n            co_return serde_raw{\\\"null\\\"};\\n        co_return to_raw(results);\\n    });\\n\\n    // --- typeHierarchy/subtypes ---\\n    peer.on_request([this](RequestContext& ctx,\\n                           const protocol::TypeHierarchySubtypesParams& params) -> RawResult {\\n        auto info =\\n            co_await resolve_hierarchy_item(params.item.uri, params.item.range, params.item.data);\\n        if(!info)\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        auto sym_it = project_index.symbols.find(info->hash);\\n        if(sym_it == project_index.symbols.end())\\n            co_return serde_raw{\\\"null\\\"};\\n\\n        // Find Derived relations across all reference files: subtypes of this class.\\n        std::vector<protocol::TypeHierarchyItem> results;\\n\\n        for(auto file_id: sym_it->second.reference_files) {\\n            auto file_merged_it = merged_indices.find(file_id);\\n            if(file_merged_it == merged_indices.end())\\n                continue;\\n\\n            file_merged_it->second.lookup(\\n                info->hash,\\n                RelationKind::Derived,\\n                [&](const index::Relation& r) {\\n                    auto derived_hash = r.target_symbol;\\n                    auto derived_sym_it = project_index.symbols.find(derived_hash);\\n                    if(derived_sym_it == project_index.symbols.end())\\n                        return true;\\n\\n                    auto def_loc = find_symbol_definition_location(derived_hash);\\n                    if(!def_loc)\\n                        return true;\\n\\n                    protocol::TypeHierarchyItem item;\\n                    item.name = derived_sym_it->second.name;\\n                    item.kind = to_lsp_symbol_kind(derived_sym_it->second.kind);\\n                    item.uri = def_loc->uri;\\n                    item.range = def_loc->range;\\n                    item.selection_range = def_loc->range;\\n                    item.data = protocol::LSPAny(static_cast<std::int64_t>(derived_hash));\\n                    results.push_back(std::move(item));\\n                    return true;\\n                });\\n        }\\n\\n        if(results.empty())\\n            co_return serde_raw{\\\"null\\\"};\\n        co_return to_raw(results);\\n    });\\n\\n    // --- workspace/symbol ---\\n    peer.on_request(\\n        [this](RequestContext& ctx, const protocol::WorkspaceSymbolParams& params) -> RawResult {\\n            auto query = llvm::StringRef(params.query);\\n            std::vector<protocol::SymbolInformation> results;\\n\\n            // Case-insensitive substring match on symbol names.\\n            std::string query_lower = query.lower();\\n\\n            for(auto& [hash, symbol]: project_index.symbols) {\\n                if(results.size() >= 100)\\n                    break;\\n\\n                // Skip non-indexable symbol kinds (punctuation, literals, etc.)\\n                // Skip non-indexable symbol kinds using a positive check instead.\\n                auto sk = symbol.kind;\\n                if(!(sk == SymbolKind::Namespace || sk == SymbolKind::Class ||\\n                     sk == SymbolKind::Struct || sk == SymbolKind::Union ||\\n                     sk == SymbolKind::Enum || sk == SymbolKind::Type || sk == SymbolKind::Field ||\\n                     sk == SymbolKind::EnumMember || sk == SymbolKind::Function ||\\n                     sk == SymbolKind::Method || sk == SymbolKind::Variable ||\\n                     sk == SymbolKind::Parameter || sk == SymbolKind::Macro ||\\n                     sk == SymbolKind::Concept || sk == SymbolKind::Module ||\\n                     sk == SymbolKind::Operator || sk == SymbolKind::MacroParameter ||\\n                     sk == SymbolKind::Label || sk == SymbolKind::Attribute))\\n                    continue;\\n\\n                if(symbol.name.empty())\\n                    continue;\\n\\n                // Check case-insensitive substring match.\\n                std::string name_lower = llvm::StringRef(symbol.name).lower();\\n                if(!query_lower.empty() && name_lower.find(query_lower) == std::string::npos)\\n                    continue;\\n\\n                auto def_loc = find_symbol_definition_location(hash);\\n                if(!def_loc)\\n                    continue;\\n\\n                protocol::SymbolInformation info;\\n                info.name = symbol.name;\\n                info.kind = to_lsp_symbol_kind(symbol.kind);\\n                info.location = std::move(*def_loc);\\n                results.push_back(std::move(info));\\n            }\\n\\n            if(results.empty())\\n                co_return serde_raw{\\\"null\\\"};\\n            co_return to_raw(results);\\n        });\\n}\\n\\n}  // namespace clice\\n\"}}}"}
{"ts":4744,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/src/server/master_server.h\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#pragma once\\n\\n#include <cstdint>\\n#include <memory>\\n#include <string>\\n\\n#include \\\"command/command.h\\\"\\n#include \\\"eventide/async/async.h\\\"\\n#include \\\"eventide/ipc/lsp/protocol.h\\\"\\n#include \\\"eventide/ipc/peer.h\\\"\\n#include \\\"eventide/serde/serde/raw_value.h\\\"\\n#include \\\"index/merged_index.h\\\"\\n#include \\\"index/project_index.h\\\"\\n#include \\\"semantic/relation_kind.h\\\"\\n#include \\\"server/compile_graph.h\\\"\\n#include \\\"server/config.h\\\"\\n#include \\\"server/worker_pool.h\\\"\\n#include \\\"support/path_pool.h\\\"\\n#include \\\"syntax/dependency_graph.h\\\"\\n\\n#include \\\"llvm/ADT/DenseMap.h\\\"\\n#include \\\"llvm/ADT/SmallVector.h\\\"\\n#include \\\"llvm/ADT/StringMap.h\\\"\\n#include \\\"llvm/ADT/StringRef.h\\\"\\n\\nnamespace clice {\\n\\nnamespace et = eventide;\\nnamespace protocol = et::ipc::protocol;\\n\\nenum class CompletionContext { None, IncludeQuoted, IncludeAngled, Import };\\n\\nstruct PreambleCompletionContext {\\n    CompletionContext kind = CompletionContext::None;\\n    std::string prefix;\\n};\\n\\nstruct DocumentState {\\n    int version = 0;\\n\\n    std::string text;\\n\\n    std::uint64_t generation = 0;\\n\\n    bool ast_dirty = true;\\n\\n    /// Non-null while a compile is in flight. Other ensure_compiled() calls\\n    /// wait on this instead of sending duplicate compile requests.\\n    std::shared_ptr<et::event> compiling;\\n};\\n\\n/// Two-layer staleness snapshot for compilation artifacts (PCH, AST, etc.).\\n///\\n/// Layer 1 (fast): compare each file's current mtime against build_at.\\n///   If all mtimes <= build_at, the artifact is fresh (zero I/O beyond stat).\\n///\\n/// Layer 2 (precise): for files whose mtime changed, re-hash their content\\n///   and compare against the stored hash.  If the hash matches, the file was\\n///   \\\"touched\\\" but not actually modified — skip the rebuild.\\n///\\n/// This avoids unnecessary recompilation from timestamp-only changes (e.g.\\n/// git checkout, touch, backup restore) while remaining cheap in the common\\n/// case where nothing changed.\\nstruct DepsSnapshot {\\n    /// File path IDs interned via PathPool.\\n    llvm::SmallVector<std::uint32_t> path_ids;\\n\\n    /// xxh3_64bits of file content at build time.\\n    llvm::SmallVector<std::uint64_t> hashes;\\n\\n    /// time_t when this snapshot was captured.\\n    std::int64_t build_at = 0;\\n};\\n\\n/// Cached PCH state for a single source file.\\nstruct PCHState {\\n    /// Built PCH file path.\\n    std::string path;\\n\\n    /// Preamble byte offset used when building.\\n    std::uint32_t bound = 0;\\n\\n    /// xxh3 hash of preamble content.\\n    std::uint64_t hash = 0;\\n\\n    /// Dependency snapshot for staleness detection.\\n    DepsSnapshot deps;\\n\\n    /// Non-null while a build is in flight.\\n    std::shared_ptr<et::event> building;\\n};\\n\\n/// Cached PCM state for a single module file.\\nstruct PCMState {\\n    /// Built PCM file path.\\n    std::string path;\\n\\n    /// Dependency snapshot for staleness detection.\\n    DepsSnapshot deps;\\n};\\n\\n/// Information about a symbol at a given position.\\nstruct SymbolInfo {\\n    /// Unique hash identifying this symbol across the project.\\n    index::SymbolHash hash = 0;\\n\\n    /// Human-readable symbol name.\\n    std::string name;\\n\\n    /// Symbol kind (function, class, variable, etc.).\\n    SymbolKind kind;\\n\\n    /// URI of the file containing this symbol.\\n    std::string uri;\\n\\n    /// Source range of the symbol's identifier.\\n    protocol::Range range;\\n};\\n\\nenum class ServerLifecycle : std::uint8_t {\\n    Uninitialized,\\n    Initialized,\\n    Ready,\\n    ShuttingDown,\\n    Exited,\\n};\\n\\nclass MasterServer {\\npublic:\\n    MasterServer(et::event_loop& loop, et::ipc::JsonPeer& peer, std::string self_path);\\n    ~MasterServer();\\n\\n    void register_handlers();\\n\\nprivate:\\n    /// Event loop for scheduling async tasks.\\n    et::event_loop& loop;\\n\\n    /// JSON-RPC peer for LSP communication.\\n    et::ipc::JsonPeer& peer;\\n\\n    /// Pool of stateful/stateless worker processes.\\n    WorkerPool pool;\\n\\n    /// Interning pool for file paths (path string -> uint32_t ID).\\n    PathPool path_pool;\\n\\n    /// Current server lifecycle state.\\n    ServerLifecycle lifecycle = ServerLifecycle::Uninitialized;\\n\\n    /// Path to the clice binary itself.\\n    std::string self_path;\\n\\n    /// Root directory of the opened workspace.\\n    std::string workspace_root;\\n\\n    /// User/project configuration.\\n    CliceConfig config;\\n\\n    /// Session-specific log directory (e.g. .clice/logs/2026-04-05_10-30-00/).\\n    std::string session_log_dir;\\n\\n    /// Compilation database (compile_commands.json).\\n    CompilationDatabase cdb;\\n\\n    /// Include/module dependency graph built from fast lexer scanning.\\n    DependencyGraph dependency_graph;\\n\\n    /// Module compilation graph (lazy dependency resolution).\\n    std::unique_ptr<CompileGraph> compile_graph;\\n\\n    /// path_id -> built PCM output path (set after successful module build).\\n    llvm::DenseMap<std::uint32_t, std::string> pcm_paths;\\n\\n    /// path_id -> module name (for files that provide a module interface).\\n    llvm::DenseMap<std::uint32_t, std::string> path_to_module;\\n\\n    /// path_id -> cached PCH state (path, preamble hash, deps, build event).\\n    llvm::DenseMap<std::uint32_t, PCHState> pch_states;\\n\\n    /// path_id -> cached PCM state (path, deps).\\n    llvm::DenseMap<std::uint32_t, PCMState> pcm_states;\\n\\n    // === Index state ===\\n\\n    /// Global symbol table and path mapping for the project.\\n    index::ProjectIndex project_index;\\n\\n    /// Per-file merged index shards (keyed by project-level path_id).\\n    llvm::DenseMap<std::uint32_t, index::MergedIndex> merged_indices;\\n\\n    /// Files queued for background indexing (server-level path_ids from CDB).\\n    std::vector<std::uint32_t> index_queue;\\n\\n    /// Index of next file to process in index_queue.\\n    std::size_t index_queue_pos = 0;\\n\\n    /// Whether background indexing is currently in progress.\\n    bool indexing_active = false;\\n\\n    /// Whether a background indexing coroutine has been scheduled (waiting on timer).\\n    bool indexing_scheduled = false;\\n\\n    /// Timer for idle-triggered background indexing.\\n    std::shared_ptr<et::timer> index_idle_timer;\\n\\n    /// path_id -> open document state (text, version, generation, dirty flag).\\n    llvm::DenseMap<std::uint32_t, DocumentState> documents;\\n\\n    /// Per-file dependency snapshots from last successful AST compilation.\\n    llvm::DenseMap<std::uint32_t, DepsSnapshot> ast_deps;\\n\\n    // === Helpers ===\\n\\n    /// Convert a file:// URI to a local file path.\\n    std::string uri_to_path(const std::string& uri);\\n\\n    /// Publish diagnostics to the LSP client.\\n    void publish_diagnostics(const std::string& uri,\\n                             int version,\\n                             const eventide::serde::RawValue& diagnostics_json);\\n\\n    /// Clear diagnostics for a file (publish empty array).\\n    void clear_diagnostics(const std::string& uri);\\n\\n    /// Pull-based compilation entry point. Ensures AST is up-to-date.\\n    et::task<bool> ensure_compiled(std::uint32_t path_id);\\n\\n    /// Load CDB and build initial include/module dependency graph.\\n    et::task<> load_workspace();\\n\\n    /// Fill compile arguments from CDB for a given file.\\n    bool fill_compile_args(llvm::StringRef path,\\n                           std::string& directory,\\n                           std::vector<std::string>& arguments);\\n\\n    /// Build or reuse PCH for a source file. Returns true if PCH is available.\\n    et::task<bool> ensure_pch(std::uint32_t path_id,\\n                              llvm::StringRef path,\\n                              const std::string& text,\\n                              const std::string& directory,\\n                              const std::vector<std::string>& arguments);\\n\\n    /// Compile module dependencies, build/reuse PCH, and fill PCM paths.\\n    /// Shared preparation step for ensure_compiled() and forward_stateless().\\n    et::task<bool> ensure_deps(std::uint32_t path_id,\\n                               llvm::StringRef path,\\n                               const std::string& text,\\n                               const std::string& directory,\\n                               const std::vector<std::string>& arguments,\\n                               std::pair<std::string, uint32_t>& pch,\\n                               std::unordered_map<std::string, std::string>& pcms);\\n\\n    /// Schedule background indexing when idle.\\n    void schedule_indexing();\\n\\n    /// Background indexing coroutine: picks files from queue and dispatches to workers.\\n    et::task<> run_background_indexing();\\n\\n    /// Merge a TUIndex result into ProjectIndex and MergedIndex shards.\\n    void merge_index_result(const void* tu_index_data, std::size_t size);\\n\\n    /// Persist index state to disk.\\n    void save_index();\\n\\n    /// Load index state from disk.\\n    void load_index();\\n\\n    /// Load PCH/PCM cache metadata from cache.json.\\n    void load_cache();\\n\\n    /// Save PCH/PCM cache metadata to cache.json.\\n    void save_cache();\\n\\n    /// Clean up stale cache files older than max_age_days.\\n    void cleanup_cache(int max_age_days = 7);\\n\\n    // === Include/import completion (handled in master) ===\\n\\n    /// Detect whether the cursor is on an #include or import line.\\n    PreambleCompletionContext detect_completion_context(const std::string& text, uint32_t offset);\\n\\n    /// Complete #include paths by enumerating search directories.\\n    et::serde::RawValue complete_include(const PreambleCompletionContext& ctx,\\n                                         llvm::StringRef path);\\n\\n    /// Complete import module names from the known module map.\\n    et::serde::RawValue complete_import(const PreambleCompletionContext& ctx);\\n\\n    // === Feature request forwarding ===\\n\\n    using RawResult = et::task<et::serde::RawValue, et::ipc::Error>;\\n\\n    /// Forward a simple stateful request (path-only worker params).\\n    template <typename WorkerParams>\\n    RawResult forward_stateful(const std::string& uri);\\n\\n    /// Forward a stateful request with position-to-offset conversion.\\n    template <typename WorkerParams>\\n    RawResult forward_stateful(const std::string& uri, const protocol::Position& position);\\n\\n    /// Forward a stateless request with document content and compile args.\\n    template <typename WorkerParams>\\n    RawResult forward_stateless(const std::string& uri, const protocol::Position& position);\\n\\n    // === Index query helpers ===\\n\\n    /// Query index for symbol relations (GoToDefinition, FindReferences, etc.).\\n    RawResult query_index_relations(const std::string& uri,\\n                                    const protocol::Position& position,\\n                                    RelationKind kind);\\n\\n    /// Look up a symbol at a position, returning its hash, name, kind, and range.\\n    et::task<std::optional<SymbolInfo>>\\n        lookup_symbol_at_position(const std::string& uri, const protocol::Position& position);\\n\\n    /// Find the definition location (uri + range) of a symbol by its hash.\\n    std::optional<protocol::Location> find_symbol_definition_location(index::SymbolHash hash);\\n\\n    /// Convert clice::SymbolKind to LSP protocol::SymbolKind.\\n    static protocol::SymbolKind to_lsp_symbol_kind(SymbolKind kind);\\n\\n    /// Build a CallHierarchyItem from a SymbolInfo.\\n    protocol::CallHierarchyItem build_call_hierarchy_item(const SymbolInfo& info);\\n\\n    /// Build a TypeHierarchyItem from a SymbolInfo.\\n    protocol::TypeHierarchyItem build_type_hierarchy_item(const SymbolInfo& info);\\n\\n    /// Resolve SymbolInfo from a hierarchy item's stored data (symbol hash).\\n    et::task<std::optional<SymbolInfo>>\\n        resolve_hierarchy_item(const std::string& uri,\\n                               const protocol::Range& range,\\n                               const std::optional<protocol::LSPAny>& data);\\n};\\n\\n}  // namespace clice\\n\"}}}"}
{"ts":4744,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/src/server/stateless_worker.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include \\\"server/stateless_worker.h\\\"\\n\\n#include <chrono>\\n\\n#include \\\"compile/compilation.h\\\"\\n#include \\\"eventide/async/async.h\\\"\\n#include \\\"eventide/ipc/json_codec.h\\\"\\n#include \\\"eventide/ipc/peer.h\\\"\\n#include \\\"eventide/ipc/transport.h\\\"\\n#include \\\"eventide/serde/json/serializer.h\\\"\\n#include \\\"eventide/serde/serde/raw_value.h\\\"\\n#include \\\"feature/feature.h\\\"\\n#include \\\"index/tu_index.h\\\"\\n#include \\\"server/protocol.h\\\"\\n#include \\\"support/logging.h\\\"\\n\\n#include \\\"llvm/Support/raw_ostream.h\\\"\\n\\nnamespace clice {\\n\\nnamespace et = eventide;\\nusing et::ipc::RequestResult;\\nusing RequestContext = et::ipc::BincodePeer::RequestContext;\\n\\nstruct ScopedTimer {\\n    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();\\n\\n    long long ms() const {\\n        return std::chrono::duration_cast<std::chrono::milliseconds>(\\n                   std::chrono::steady_clock::now() - start)\\n            .count();\\n    }\\n};\\n\\nstatic void fill_args(CompilationParams& cp,\\n                      const std::string& directory,\\n                      const std::vector<std::string>& arguments) {\\n    cp.directory = directory;\\n    for(auto& arg: arguments) {\\n        cp.arguments.push_back(arg.c_str());\\n    }\\n}\\n\\ntemplate <typename T>\\nstatic et::serde::RawValue to_raw(const T& value) {\\n    auto json = et::serde::json::to_json<et::ipc::lsp_config>(value);\\n    return et::serde::RawValue{json ? std::move(*json) : \\\"null\\\"};\\n}\\n\\nint run_stateless_worker_mode(const std::string& worker_name, const std::string& log_dir) {\\n    logging::stderr_logger(worker_name, logging::options);\\n    if(!log_dir.empty()) {\\n        logging::file_logger(worker_name, log_dir, logging::options);\\n    }\\n\\n    LOG_INFO(\\\"Starting stateless worker\\\");\\n\\n    et::event_loop loop;\\n\\n    auto transport_result = et::ipc::StreamTransport::open_stdio(loop);\\n    if(!transport_result) {\\n        LOG_ERROR(\\\"Failed to open stdio transport\\\");\\n        return 1;\\n    }\\n\\n    et::ipc::BincodePeer peer(loop, std::move(*transport_result));\\n\\n    // === BuildPCH ===\\n    peer.on_request(\\n        [&](RequestContext& ctx,\\n            const worker::BuildPCHParams& params) -> RequestResult<worker::BuildPCHParams> {\\n            LOG_INFO(\\\"BuildPCH request: file={}\\\", params.file);\\n\\n            auto result = co_await et::queue([&]() -> worker::BuildPCHResult {\\n                ScopedTimer timer;\\n\\n                CompilationParams cp;\\n                cp.kind = CompilationKind::Preamble;\\n                fill_args(cp, params.directory, params.arguments);\\n                cp.add_remapped_file(params.file, params.content, params.preamble_bound);\\n\\n                // When output_path is set, write to .tmp then rename — avoids\\n                // Windows file-lock failures when the target is held by another\\n                // process.  When empty, fall back to a temporary file.\\n                std::string tmp_path;\\n                bool has_output = !params.output_path.empty();\\n                if(has_output) {\\n                    tmp_path = params.output_path + \\\".tmp\\\";\\n                } else {\\n                    auto tmp = fs::createTemporaryFile(\\\"clice-pch\\\", \\\"pch\\\");\\n                    if(!tmp) {\\n                        LOG_ERROR(\\\"BuildPCH: failed to create temp file\\\");\\n                        return {false, \\\"Failed to create temporary PCH file\\\", \\\"\\\"};\\n                    }\\n                    tmp_path = *tmp;\\n                }\\n                cp.output_file = tmp_path;\\n\\n                PCHInfo pch_info;\\n                auto unit = compile(cp, pch_info);\\n                bool success = unit.completed();\\n\\n                // Extract error messages before destroying the unit.\\n                std::string errors;\\n                if(!success) {\\n                    for(auto& diag: unit.diagnostics()) {\\n                        if(diag.id.level >= DiagnosticLevel::Error) {\\n                            if(!errors.empty())\\n                                errors += \\\"; \\\";\\n                            errors += diag.message;\\n                        }\\n                    }\\n                }\\n\\n                // Destroy CompilationUnit to flush PCH file to disk\\n                // (EndSourceFile serializes the AST on destruction).\\n                unit = CompilationUnit(nullptr);\\n\\n                if(success) {\\n                    std::string final_path;\\n                    if(has_output) {\\n                        auto ec = fs::rename(tmp_path, params.output_path);\\n                        if(ec) {\\n                            final_path = params.output_path;\\n                        } else {\\n                            LOG_WARN(\\\"BuildPCH: rename {} -> {} failed: {}\\\",\\n                                     tmp_path,\\n                                     params.output_path,\\n                                     ec.error().message());\\n                            final_path = tmp_path;\\n                        }\\n                    } else {\\n                        final_path = tmp_path;\\n                    }\\n                    LOG_INFO(\\\"BuildPCH done: file={}, output={}, {}ms\\\",\\n                             params.file,\\n                             final_path,\\n                             timer.ms());\\n                    worker::BuildPCHResult pch_result{true, \\\"\\\", std::move(final_path)};\\n                    pch_result.deps = pch_info.deps;\\n                    return pch_result;\\n                } else {\\n                    LOG_WARN(\\\"BuildPCH failed: file={}, {}ms, errors=[{}]\\\",\\n                             params.file,\\n                             timer.ms(),\\n                             errors);\\n                    fs::remove(tmp_path);\\n                    return {false, errors.empty() ? \\\"PCH compilation failed\\\" : errors, \\\"\\\"};\\n                }\\n            });\\n            co_return result.value();\\n        });\\n\\n    // === BuildPCM ===\\n    peer.on_request(\\n        [&](RequestContext& ctx,\\n            const worker::BuildPCMParams& params) -> RequestResult<worker::BuildPCMParams> {\\n            LOG_INFO(\\\"BuildPCM request: file={}, module={}\\\", params.file, params.module_name);\\n\\n            auto result = co_await et::queue([&]() -> worker::BuildPCMResult {\\n                ScopedTimer timer;\\n\\n                CompilationParams cp;\\n                cp.kind = CompilationKind::ModuleInterface;\\n                fill_args(cp, params.directory, params.arguments);\\n                for(auto& [name, path]: params.pcms) {\\n                    cp.pcms.try_emplace(name, path);\\n                }\\n\\n                std::string tmp_path;\\n                bool has_output = !params.output_path.empty();\\n                if(has_output) {\\n                    tmp_path = params.output_path + \\\".tmp\\\";\\n                } else {\\n                    auto tmp = fs::createTemporaryFile(\\\"clice-pcm\\\", \\\"pcm\\\");\\n                    if(!tmp) {\\n                        LOG_ERROR(\\\"BuildPCM: failed to create temp file\\\");\\n                        return {false, \\\"Failed to create temporary PCM file\\\", \\\"\\\"};\\n                    }\\n                    tmp_path = *tmp;\\n                }\\n                cp.output_file = tmp_path;\\n\\n                PCMInfo pcm_info;\\n                auto unit = compile(cp, pcm_info);\\n                bool success = unit.completed();\\n\\n                std::string errors;\\n                if(!success) {\\n                    for(auto& diag: unit.diagnostics()) {\\n                        if(diag.id.level >= DiagnosticLevel::Error) {\\n                            if(!errors.empty())\\n                                errors += \\\"; \\\";\\n                            errors += diag.message;\\n                        }\\n                    }\\n                }\\n\\n                unit = CompilationUnit(nullptr);\\n\\n                if(success) {\\n                    std::string final_path = tmp_path;\\n                    if(has_output) {\\n                        auto ec = fs::rename(tmp_path, params.output_path);\\n                        if(ec) {\\n                            final_path = params.output_path;\\n                        } else {\\n                            LOG_WARN(\\\"BuildPCM: rename {} -> {} failed: {}\\\",\\n                                     tmp_path,\\n                                     params.output_path,\\n                                     ec.error().message());\\n                        }\\n                    }\\n                    LOG_INFO(\\\"BuildPCM done: module={}, {}ms\\\", params.module_name, timer.ms());\\n                    worker::BuildPCMResult pcm_result{true, \\\"\\\", std::move(final_path)};\\n                    pcm_result.deps = pcm_info.deps;\\n                    return pcm_result;\\n                } else {\\n                    LOG_WARN(\\\"BuildPCM failed: module={}, {}ms, errors=[{}]\\\",\\n                             params.module_name,\\n                             timer.ms(),\\n                             errors);\\n                    fs::remove(tmp_path);\\n                    return {false, errors.empty() ? \\\"PCM compilation failed\\\" : errors, \\\"\\\"};\\n                }\\n            });\\n            co_return result.value();\\n        });\\n\\n    // === Completion ===\\n    peer.on_request(\\n        [&](RequestContext& ctx,\\n            const worker::CompletionParams& params) -> RequestResult<worker::CompletionParams> {\\n            LOG_DEBUG(\\\"Completion request: path={}, offset={}\\\", params.path, params.offset);\\n\\n            auto result = co_await et::queue([&]() -> et::serde::RawValue {\\n                ScopedTimer timer;\\n\\n                CompilationParams cp;\\n                cp.kind = CompilationKind::Completion;\\n                fill_args(cp, params.directory, params.arguments);\\n                if(!params.pch.first.empty()) {\\n                    cp.pch = params.pch;\\n                }\\n                for(auto& [name, path]: params.pcms) {\\n                    cp.pcms.try_emplace(name, path);\\n                }\\n                cp.add_remapped_file(params.path, params.text);\\n                cp.completion = {params.path, params.offset};\\n\\n                auto items = feature::code_complete(cp);\\n                LOG_DEBUG(\\\"Completion done: {} items, {}ms\\\", items.size(), timer.ms());\\n                return to_raw(items);\\n            });\\n            co_return result.value();\\n        });\\n\\n    // === SignatureHelp ===\\n    peer.on_request([&](RequestContext& ctx, const worker::SignatureHelpParams& params)\\n                        -> RequestResult<worker::SignatureHelpParams> {\\n        LOG_DEBUG(\\\"SignatureHelp request: path={}, offset={}\\\", params.path, params.offset);\\n\\n        auto result = co_await et::queue([&]() -> et::serde::RawValue {\\n            ScopedTimer timer;\\n\\n            CompilationParams cp;\\n            cp.kind = CompilationKind::Completion;\\n            fill_args(cp, params.directory, params.arguments);\\n            if(!params.pch.first.empty()) {\\n                cp.pch = params.pch;\\n            }\\n            for(auto& [name, path]: params.pcms) {\\n                cp.pcms.try_emplace(name, path);\\n            }\\n            cp.add_remapped_file(params.path, params.text);\\n            cp.completion = {params.path, params.offset};\\n\\n            auto help = feature::signature_help(cp);\\n            LOG_DEBUG(\\\"SignatureHelp done: {}ms\\\", timer.ms());\\n            return to_raw(help);\\n        });\\n        co_return result.value();\\n    });\\n\\n    // === Index ===\\n    peer.on_request([&](RequestContext& ctx,\\n                        const worker::IndexParams& params) -> RequestResult<worker::IndexParams> {\\n        LOG_INFO(\\\"Index request: file={}\\\", params.file);\\n\\n        auto result = co_await et::queue([&]() -> worker::IndexResult {\\n            ScopedTimer timer;\\n\\n            CompilationParams cp;\\n            cp.kind = CompilationKind::Indexing;\\n            fill_args(cp, params.directory, params.arguments);\\n            for(auto& [name, path]: params.pcms) {\\n                cp.pcms.try_emplace(name, path);\\n            }\\n\\n            auto unit = compile(cp);\\n\\n            if(!unit.completed()) {\\n                LOG_WARN(\\\"Index failed: file={}, {}ms\\\", params.file, timer.ms());\\n                return {false, \\\"Index compilation failed\\\", \\\"\\\"};\\n            }\\n\\n            auto tu_index = index::TUIndex::build(unit);\\n\\n            std::string serialized;\\n            llvm::raw_string_ostream os(serialized);\\n            tu_index.serialize(os);\\n\\n            LOG_INFO(\\\"Index done: file={}, {} symbols, {}ms\\\",\\n                     params.file,\\n                     tu_index.symbols.size(),\\n                     timer.ms());\\n            return {true, \\\"\\\", std::move(serialized)};\\n        });\\n        co_return result.value();\\n    });\\n\\n    LOG_INFO(\\\"Stateless worker ready, waiting for requests\\\");\\n    loop.schedule(peer.run());\\n    auto ret = loop.run();\\n    LOG_INFO(\\\"Stateless worker exiting with code {}\\\", ret);\\n    return ret;\\n}\\n\\n}  // namespace clice\\n\"}}}"}
{"ts":4745,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/src/server/worker_pool.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include \\\"server/worker_pool.h\\\"\\n\\n#include <csignal>\\n#include <string>\\n\\n#include \\\"eventide/ipc/transport.h\\\"\\n#include \\\"support/logging.h\\\"\\n\\nnamespace clice {\\n\\nnamespace {\\n\\n/// Coroutine that drains a worker's stderr pipe.\\n/// Workers write their own log files, so this only captures unexpected output\\n/// (crash stacktraces, assertion failures, etc.) that bypasses spdlog.\\net::task<> drain_stderr(et::pipe stderr_pipe, std::string prefix) {\\n    std::string buffer;\\n    while(true) {\\n        auto result = co_await stderr_pipe.read();\\n        if(!result.has_value()) {\\n            break;\\n        }\\n        auto& chunk = result.value();\\n        if(chunk.empty())\\n            break;\\n\\n        buffer += chunk;\\n\\n        std::size_t pos = 0;\\n        while(true) {\\n            auto nl = buffer.find('\\\\n', pos);\\n            if(nl == std::string::npos)\\n                break;\\n            auto line = buffer.substr(pos, nl - pos);\\n            if(!line.empty()) {\\n                LOG_DEBUG(\\\"{} {}\\\", prefix, line);\\n            }\\n            pos = nl + 1;\\n        }\\n        buffer.erase(0, pos);\\n    }\\n\\n    if(!buffer.empty()) {\\n        LOG_DEBUG(\\\"{} {}\\\", prefix, buffer);\\n    }\\n}\\n\\n}  // namespace\\n\\nbool WorkerPool::spawn_worker(const std::string& self_path,\\n                              bool stateful,\\n                              std::uint64_t memory_limit) {\\n    auto& workers = stateful ? stateful_workers : stateless_workers;\\n    auto worker_index = workers.size();\\n    std::string worker_name = std::string(stateful ? \\\"SF-\\\" : \\\"SL-\\\") + std::to_string(worker_index);\\n\\n    et::process::options opts;\\n    opts.file = self_path;\\n    if(stateful) {\\n        opts.args = {self_path,\\n                     \\\"--mode\\\",\\n                     \\\"stateful-worker\\\",\\n                     \\\"--worker-memory-limit\\\",\\n                     std::to_string(memory_limit)};\\n    } else {\\n        opts.args = {self_path, \\\"--mode\\\", \\\"stateless-worker\\\"};\\n    }\\n\\n    opts.args.push_back(\\\"--worker-name\\\");\\n    opts.args.push_back(worker_name);\\n\\n    if(!log_dir_.empty()) {\\n        opts.args.push_back(\\\"--log-dir\\\");\\n        opts.args.push_back(log_dir_);\\n    }\\n\\n    opts.streams = {\\n        et::process::stdio::pipe(true, false),  // stdin: child reads\\n        et::process::stdio::pipe(false, true),  // stdout: child writes\\n        et::process::stdio::pipe(false, true),  // stderr: child writes\\n    };\\n\\n    auto result = et::process::spawn(opts, loop);\\n    if(!result) {\\n        LOG_ERROR(\\\"Failed to spawn {} worker: {}\\\",\\n                  stateful ? \\\"stateful\\\" : \\\"stateless\\\",\\n                  result.error().message());\\n        return false;\\n    }\\n\\n    auto& spawn = *result;\\n\\n    // StreamTransport: input = child's stdout (parent reads), output = child's stdin (parent\\n    // writes)\\n    auto transport = std::make_unique<et::ipc::StreamTransport>(std::move(spawn.stdout_pipe),\\n                                                                std::move(spawn.stdin_pipe));\\n    auto peer = std::make_unique<et::ipc::BincodePeer>(loop, std::move(transport));\\n\\n    // Schedule stderr log collection\\n    std::string prefix = \\\"[\\\" + worker_name + \\\"]\\\";\\n    loop.schedule(drain_stderr(std::move(spawn.stderr_pipe), prefix));\\n\\n    workers.push_back(WorkerProcess{\\n        .proc = std::move(spawn.proc),\\n        .peer = std::move(peer),\\n        .owned_documents = 0,\\n    });\\n\\n    auto& w = workers.back();\\n    w.peer->set_logger(\\n        [prefix](et::ipc::LogLevel, std::string_view msg) {\\n            LOG_INFO(\\\"{} ipc: {}\\\", prefix, msg);\\n        },\\n        et::ipc::LogLevel::debug);\\n    loop.schedule(w.peer->run());\\n\\n    return true;\\n}\\n\\nbool WorkerPool::start(const WorkerPoolOptions& options) {\\n    log_dir_ = options.log_dir;\\n\\n    for(std::uint32_t i = 0; i < options.stateless_count; ++i) {\\n        if(!spawn_worker(options.self_path, false, 0)) {\\n            return false;\\n        }\\n    }\\n\\n    for(std::uint32_t i = 0; i < options.stateful_count; ++i) {\\n        if(!spawn_worker(options.self_path, true, options.worker_memory_limit)) {\\n            return false;\\n        }\\n    }\\n\\n    // Register evicted notification handler for each stateful worker\\n    for(std::size_t i = 0; i < stateful_workers.size(); ++i) {\\n        stateful_workers[i].peer->on_notification([this](const worker::EvictedParams& params) {\\n            if(on_evicted) {\\n                on_evicted(params.path);\\n            }\\n        });\\n    }\\n\\n    LOG_INFO(\\\"WorkerPool started: {} stateless, {} stateful workers\\\",\\n             stateless_workers.size(),\\n             stateful_workers.size());\\n    return true;\\n}\\n\\net::task<> WorkerPool::stop() {\\n    LOG_INFO(\\\"WorkerPool stopping...\\\");\\n\\n    // Close output pipes to signal workers to exit gracefully\\n    for(auto& w: stateless_workers) {\\n        w.peer->close_output();\\n    }\\n    for(auto& w: stateful_workers) {\\n        w.peer->close_output();\\n    }\\n\\n    // Send SIGTERM to all workers\\n    for(auto& w: stateless_workers) {\\n        w.proc.kill(SIGTERM);\\n    }\\n    for(auto& w: stateful_workers) {\\n        w.proc.kill(SIGTERM);\\n    }\\n\\n    // Wait for all worker processes to exit\\n    for(auto& w: stateless_workers) {\\n        co_await w.proc.wait();\\n    }\\n    for(auto& w: stateful_workers) {\\n        co_await w.proc.wait();\\n    }\\n\\n    LOG_INFO(\\\"WorkerPool stopped\\\");\\n}\\n\\nstd::size_t WorkerPool::assign_worker(std::uint32_t path_id) {\\n    auto it = owner.find(path_id);\\n    if(it != owner.end()) {\\n        // Already assigned; touch LRU\\n        auto lru_it = owner_lru_index.find(path_id);\\n        if(lru_it != owner_lru_index.end()) {\\n            owner_lru.erase(lru_it->second);\\n        }\\n        owner_lru.push_front(path_id);\\n        owner_lru_index[path_id] = owner_lru.begin();\\n        return it->second;\\n    }\\n\\n    // New assignment: pick the least-loaded worker\\n    auto selected = pick_least_loaded();\\n    owner[path_id] = selected;\\n    stateful_workers[selected].owned_documents++;\\n    owner_lru.push_front(path_id);\\n    owner_lru_index[path_id] = owner_lru.begin();\\n    return selected;\\n}\\n\\nstd::size_t WorkerPool::pick_least_loaded() {\\n    std::size_t best = 0;\\n    for(std::size_t i = 1; i < stateful_workers.size(); ++i) {\\n        if(stateful_workers[i].owned_documents < stateful_workers[best].owned_documents) {\\n            best = i;\\n        }\\n    }\\n    return best;\\n}\\n\\nvoid WorkerPool::remove_owner(std::uint32_t path_id) {\\n    auto it = owner.find(path_id);\\n    if(it == owner.end())\\n        return;\\n\\n    auto worker_idx = it->second;\\n    stateful_workers[worker_idx].owned_documents--;\\n    owner.erase(it);\\n\\n    auto lru_it = owner_lru_index.find(path_id);\\n    if(lru_it != owner_lru_index.end()) {\\n        owner_lru.erase(lru_it->second);\\n        owner_lru_index.erase(lru_it);\\n    }\\n}\\n\\nvoid WorkerPool::clear_owner(std::size_t worker_index) {\\n    llvm::SmallVector<std::uint32_t> to_remove;\\n    for(auto& [pid, widx]: owner) {\\n        if(widx == worker_index) {\\n            to_remove.push_back(pid);\\n        }\\n    }\\n    for(auto pid: to_remove) {\\n        remove_owner(pid);\\n    }\\n}\\n\\n}  // namespace clice\\n\"}}}"}
{"ts":4745,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/src/syntax/scan.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include \\\"syntax/scan.h\\\"\\n\\n#include <deque>\\n\\n#include \\\"syntax/lexer.h\\\"\\n\\n#include \\\"llvm/ADT/StringSet.h\\\"\\n#include \\\"llvm/Support/MemoryBuffer.h\\\"\\n#include \\\"clang/Basic/DiagnosticOptions.h\\\"\\n#include \\\"clang/Basic/FileEntry.h\\\"\\n#include \\\"clang/Basic/FileManager.h\\\"\\n#include \\\"clang/Basic/SourceManager.h\\\"\\n#include \\\"clang/Frontend/CompilerInstance.h\\\"\\n#include \\\"clang/Frontend/FrontendActions.h\\\"\\n#include \\\"clang/Lex/PPCallbacks.h\\\"\\n#include \\\"clang/Lex/Preprocessor.h\\\"\\n#include \\\"clang/Lex/PreprocessorOptions.h\\\"\\n#include \\\"clang/Tooling/CompilationDatabase.h\\\"\\n\\nnamespace clice {\\n\\nScanResult scan(llvm::StringRef content) {\\n    namespace dds = clang::dependency_directives_scan;\\n\\n    ScanResult result;\\n\\n    llvm::SmallVector<dds::Token> tokens;\\n    llvm::SmallVector<dds::Directive> directives;\\n\\n    if(clang::scanSourceForDependencyDirectives(content, tokens, directives)) {\\n        return result;\\n    }\\n\\n    // Most source files have 10-30 includes; pre-allocate to avoid reallocs.\\n    result.includes.reserve(std::min<std::size_t>(directives.size(), 32));\\n\\n    int conditional_depth = 0;\\n\\n    for(auto& dir: directives) {\\n        switch(dir.Kind) {\\n            case dds::pp_if:\\n            case dds::pp_ifdef:\\n            case dds::pp_ifndef: {\\n                conditional_depth++;\\n                break;\\n            }\\n            case dds::pp_endif: {\\n                if(conditional_depth > 0) {\\n                    conditional_depth--;\\n                }\\n                break;\\n            }\\n            case dds::pp_elif:\\n            case dds::pp_elifdef:\\n            case dds::pp_elifndef:\\n            case dds::pp_else: {\\n                break;\\n            }\\n            case dds::pp_include:\\n            case dds::pp_include_next:\\n            case dds::pp___include_macros: {\\n                // Find the header token (string_literal or header_name).\\n                for(auto& tok: dir.Tokens) {\\n                    if(tok.is(clang::tok::header_name) || tok.is(clang::tok::string_literal)) {\\n                        auto name = content.substr(tok.Offset, tok.Length);\\n                        // Strip <> or \\\"\\\" delimiters.\\n                        if(name.size() >= 2) {\\n                            bool angled = name.front() == '<';\\n                            ScanResult::IncludeInfo info;\\n                            info.path = std::string(name.substr(1, name.size() - 2));\\n                            info.conditional = conditional_depth > 0;\\n                            info.is_angled = angled;\\n                            info.is_include_next = dir.Kind == dds::pp_include_next;\\n                            result.includes.push_back(std::move(info));\\n                        }\\n                        break;\\n                    }\\n                }\\n                break;\\n            }\\n            case dds::cxx_module_decl:\\n            case dds::cxx_export_module_decl: {\\n                if(conditional_depth > 0) {\\n                    result.need_preprocess = true;\\n                    return result;\\n                }\\n\\n                // Collect module name from tokens: skip keywords, then\\n                // collect identifiers, '.', ':'.\\n                std::string module_name;\\n                bool seen_module_keyword = false;\\n                for(auto& tok: dir.Tokens) {\\n                    if(!seen_module_keyword) {\\n                        if(tok.is(clang::tok::raw_identifier)) {\\n                            auto spelling = content.substr(tok.Offset, tok.Length);\\n                            if(spelling == \\\"module\\\") {\\n                                seen_module_keyword = true;\\n                            }\\n                        }\\n                        continue;\\n                    }\\n                    if(tok.is(clang::tok::raw_identifier)) {\\n                        module_name += content.substr(tok.Offset, tok.Length);\\n                    } else if(tok.is(clang::tok::period)) {\\n                        module_name += '.';\\n                    } else if(tok.is(clang::tok::colon)) {\\n                        module_name += ':';\\n                    }\\n                }\\n\\n                result.module_name = std::move(module_name);\\n                result.is_interface_unit = (dir.Kind == dds::cxx_export_module_decl);\\n                break;\\n            }\\n            default: {\\n                break;\\n            }\\n        }\\n    }\\n\\n    return result;\\n}\\n\\nnamespace {\\n\\nclass ScanDirectivesGetter : public clang::DependencyDirectivesGetter {\\npublic:\\n    ScanDirectivesGetter(SharedScanCache* cache, clang::FileManager& file_mgr) :\\n        cache(cache), file_mgr(&file_mgr) {}\\n\\n    std::unique_ptr<clang::DependencyDirectivesGetter>\\n        cloneFor(clang::FileManager& new_file_mgr) override {\\n        return std::make_unique<ScanDirectivesGetter>(cache, new_file_mgr);\\n    }\\n\\n    std::optional<llvm::ArrayRef<clang::dependency_directives_scan::Directive>>\\n        operator()(clang::FileEntryRef file) override {\\n        auto path = file.getFileEntry().tryGetRealPathName();\\n        if(path.empty()) {\\n            path = file.getName();\\n        }\\n\\n        // Check cache first.\\n        if(cache) {\\n            auto it = cache->entries.find(path);\\n            if(it != cache->entries.end()) {\\n                return llvm::ArrayRef(it->second.directives);\\n            }\\n        }\\n\\n        // Read the file content.\\n        auto buffer = file_mgr->getBufferForFile(file);\\n        if(!buffer) {\\n            return std::nullopt;\\n        }\\n\\n        auto source = (*buffer)->getBuffer().str();\\n\\n        // Create entry in its final location first, then scan into it.\\n        // Directive::Tokens are ArrayRefs pointing into the tokens SmallVector,\\n        // so the entry must not be moved after scanning.\\n        SharedScanCache::CachedEntry* entry_ptr;\\n        if(cache) {\\n            auto [it, _] = cache->entries.try_emplace(path);\\n            entry_ptr = &it->second;\\n        } else {\\n            local_entries.emplace_back();\\n            entry_ptr = &local_entries.back();\\n        }\\n\\n        entry_ptr->source = std::move(source);\\n\\n        if(clang::scanSourceForDependencyDirectives(entry_ptr->source,\\n                                                    entry_ptr->tokens,\\n                                                    entry_ptr->directives)) {\\n            // Scan failed — remove the entry.\\n            if(cache) {\\n                cache->entries.erase(path);\\n            } else {\\n                local_entries.pop_back();\\n            }\\n            return std::nullopt;\\n        }\\n\\n        return llvm::ArrayRef(entry_ptr->directives);\\n    }\\n\\nprivate:\\n    SharedScanCache* cache;\\n    clang::FileManager* file_mgr;\\n    std::deque<SharedScanCache::CachedEntry> local_entries;\\n};\\n\\n/// PPCallbacks for precise mode: single ScanResult with accurate\\n/// conditional tracking via preprocessor callbacks.\\nclass PreciseScanPPCallbacks : public clang::PPCallbacks {\\npublic:\\n    explicit PreciseScanPPCallbacks(ScanResult& result) : result(result) {}\\n\\n    void InclusionDirective(clang::SourceLocation,\\n                            const clang::Token& include_tok,\\n                            llvm::StringRef file_name,\\n                            bool is_angled,\\n                            clang::CharSourceRange,\\n                            clang::OptionalFileEntryRef file,\\n                            llvm::StringRef,\\n                            llvm::StringRef,\\n                            const clang::Module*,\\n                            bool,\\n                            clang::SrcMgr::CharacteristicKind) override {\\n        bool not_found = !file.has_value();\\n        std::string resolved_path;\\n        if(file) {\\n            resolved_path = file->getFileEntry().tryGetRealPathName().str();\\n        } else {\\n            resolved_path = file_name.str();\\n        }\\n\\n        ScanResult::IncludeInfo info;\\n        info.path = std::move(resolved_path);\\n        info.conditional = conditional_depth > 0;\\n        info.not_found = not_found;\\n        info.is_angled = is_angled;\\n        info.is_include_next =\\n            include_tok.getIdentifierInfo() &&\\n            include_tok.getIdentifierInfo()->getPPKeywordID() == clang::tok::pp_include_next;\\n        result.includes.push_back(std::move(info));\\n    }\\n\\n    void If(clang::SourceLocation, clang::SourceRange, ConditionValueKind) override {\\n        conditional_depth++;\\n    }\\n\\n    void Ifdef(clang::SourceLocation, const clang::Token&, const clang::MacroDefinition&) override {\\n        conditional_depth++;\\n    }\\n\\n    void Ifndef(clang::SourceLocation,\\n                const clang::Token&,\\n                const clang::MacroDefinition&) override {\\n        conditional_depth++;\\n    }\\n\\n    void Endif(clang::SourceLocation, clang::SourceLocation) override {\\n        if(conditional_depth > 0) {\\n            conditional_depth--;\\n        }\\n    }\\n\\n    void moduleImport(clang::SourceLocation,\\n                      clang::ModuleIdPath names,\\n                      const clang::Module*) override {\\n        std::string name;\\n        for(auto& part: names) {\\n            if(!name.empty()) {\\n                name += '.';\\n            }\\n            name += part.getIdentifierInfo()->getName();\\n        }\\n        result.modules.emplace_back(std::move(name));\\n    }\\n\\nprivate:\\n    ScanResult& result;\\n    int conditional_depth = 0;\\n};\\n\\n/// Create and configure a CompilerInstance for scanning.\\n/// If content is non-empty, it is used as remapped source for the main file.\\nstd::unique_ptr<clang::CompilerInstance>\\n    create_scan_instance(llvm::ArrayRef<const char*> arguments,\\n                         llvm::StringRef directory,\\n                         llvm::StringRef content,\\n                         llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {\\n    clang::DiagnosticOptions diag_opts;\\n    auto diag_engine = clang::CompilerInstance::createDiagnostics(*vfs,\\n                                                                  diag_opts,\\n                                                                  new clang::IgnoringDiagConsumer(),\\n                                                                  true);\\n\\n    std::unique_ptr<clang::CompilerInvocation> invocation;\\n\\n    bool is_cc1 = arguments.size() >= 2 && llvm::StringRef(arguments[1]) == \\\"-cc1\\\";\\n    if(is_cc1) {\\n        invocation = std::make_unique<clang::CompilerInvocation>();\\n        if(!clang::CompilerInvocation::CreateFromArgs(*invocation,\\n                                                      llvm::ArrayRef(arguments).drop_front(2),\\n                                                      *diag_engine,\\n                                                      arguments[0])) {\\n            return nullptr;\\n        }\\n    } else {\\n        clang::CreateInvocationOptions options = {\\n            .Diags = diag_engine,\\n            .VFS = vfs,\\n            .ProbePrecompiled = false,\\n        };\\n        invocation = clang::createInvocation(arguments, options);\\n        if(!invocation) {\\n            return nullptr;\\n        }\\n    }\\n\\n    invocation->getFrontendOpts().DisableFree = false;\\n    invocation->getFileSystemOpts().WorkingDir = directory.str();\\n\\n    if(!content.empty()) {\\n        auto& inputs = invocation->getFrontendOpts().Inputs;\\n        if(!inputs.empty()) {\\n            auto main_file = inputs[0].getFile();\\n            // Use an overlay VFS to inject the remapped content. This ensures\\n            // both the preprocessor and the DependencyDirectivesGetter see it.\\n            auto overlay = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(vfs);\\n            auto mem_fs = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();\\n            mem_fs->addFile(main_file, 0, llvm::MemoryBuffer::getMemBufferCopy(content, main_file));\\n            overlay->pushOverlay(std::move(mem_fs));\\n            vfs = std::move(overlay);\\n        }\\n    }\\n\\n    auto instance = std::make_unique<clang::CompilerInstance>(std::move(invocation));\\n    instance->createDiagnostics(*vfs, new clang::IgnoringDiagConsumer(), true);\\n    instance->getDiagnostics().setSuppressAllDiagnostics(true);\\n    instance->createFileManager(vfs);\\n\\n    return instance;\\n}\\n\\n}  // namespace\\n\\nScanResult scan_precise(llvm::ArrayRef<const char*> arguments,\\n                        llvm::StringRef directory,\\n                        llvm::StringRef content,\\n                        SharedScanCache* cache,\\n                        llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {\\n    ScanResult result;\\n\\n    if(!vfs) {\\n        vfs = llvm::vfs::createPhysicalFileSystem();\\n    }\\n\\n    auto instance = create_scan_instance(arguments, directory, content, vfs);\\n    if(!instance) {\\n        return result;\\n    }\\n\\n    auto getter = std::make_unique<ScanDirectivesGetter>(cache, instance->getFileManager());\\n    instance->setDependencyDirectivesGetter(std::move(getter));\\n\\n    if(!instance->createTarget()) {\\n        return result;\\n    }\\n\\n    auto action = std::make_unique<clang::PreprocessOnlyAction>();\\n\\n    if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) {\\n        return result;\\n    }\\n\\n    instance->getPreprocessor().addPPCallbacks(std::make_unique<PreciseScanPPCallbacks>(result));\\n\\n    if(auto error = action->Execute()) {\\n        llvm::consumeError(std::move(error));\\n    }\\n\\n    action->EndSourceFile();\\n\\n    // Get module name from preprocessor.\\n    auto& pp = instance->getPreprocessor();\\n    if(pp.isInNamedModule()) {\\n        result.module_name = pp.getNamedModuleName();\\n        result.is_interface_unit = pp.isInNamedInterfaceUnit();\\n    }\\n\\n    return result;\\n}\\n\\nScanResult scan_module_decl(llvm::ArrayRef<const char*> arguments,\\n                            llvm::StringRef directory,\\n                            llvm::StringRef content,\\n                            SharedScanCache* cache,\\n                            llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {\\n    ScanResult result;\\n\\n    if(!vfs) {\\n        vfs = llvm::vfs::createPhysicalFileSystem();\\n    }\\n\\n    auto instance = create_scan_instance(arguments, directory, content, vfs);\\n    if(!instance) {\\n        return result;\\n    }\\n\\n    auto getter = std::make_unique<ScanDirectivesGetter>(cache, instance->getFileManager());\\n    instance->setDependencyDirectivesGetter(std::move(getter));\\n\\n    if(!instance->createTarget()) {\\n        return result;\\n    }\\n\\n    auto action = std::make_unique<clang::PreprocessOnlyAction>();\\n\\n    if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) {\\n        return result;\\n    }\\n\\n    // Instead of action->Execute() which processes the entire file,\\n    // manually lex tokens and stop as soon as the module declaration is found.\\n    auto& pp = instance->getPreprocessor();\\n    pp.EnterMainSourceFile();\\n\\n    clang::Token tok;\\n    do {\\n        pp.Lex(tok);\\n        if(pp.isInNamedModule()) {\\n            result.module_name = pp.getNamedModuleName();\\n            result.is_interface_unit = pp.isInNamedInterfaceUnit();\\n            break;\\n        }\\n    } while(tok.isNot(clang::tok::eof));\\n\\n    action->EndSourceFile();\\n\\n    return result;\\n}\\n\\nstd::uint32_t compute_preamble_bound(llvm::StringRef content) {\\n    auto result = compute_preamble_bounds(content);\\n    if(result.empty()) {\\n        return 0;\\n    } else {\\n        return result.back();\\n    }\\n}\\n\\nstd::vector<std::uint32_t> compute_preamble_bounds(llvm::StringRef content) {\\n    std::vector<std::uint32_t> result;\\n\\n    Lexer lexer(content, true, nullptr, false);\\n\\n    while(true) {\\n        auto token = lexer.advance();\\n        if(token.is_eof()) {\\n            break;\\n        }\\n\\n        if(token.is_at_start_of_line) {\\n            if(token.kind == clang::tok::hash) {\\n                /// For preprocessor directive, consume the whole directive.\\n                lexer.advance_until(clang::tok::eod);\\n                auto last = lexer.last();\\n\\n                /// Append the token before the eod.\\n                result.push_back(last.range.end);\\n            } else if(token.is_identifier() && token.text(content) == \\\"module\\\") {\\n                /// If we encounter a module keyword at the start of a line, it may be\\n                /// a module declaration or global module fragment.\\n                auto next = lexer.next();\\n\\n                if(next.kind == clang::tok::semi) {\\n                    /// If next token is `;`, it is a global module fragment.\\n                    /// we just continue.\\n                    lexer.advance();\\n\\n                    /// Append it to bounds.\\n                    result.push_back(next.range.end);\\n                } else {\\n                    break;\\n                }\\n            } else {\\n                break;\\n            }\\n        }\\n    }\\n\\n    return result;\\n}\\n\\n/// Check if a preprocessor #include/#import directive line is complete.\\nstatic bool is_include_directive_complete(llvm::StringRef directive) {\\n    if(directive.contains('\\\"')) {\\n        auto after_keyword = directive.drop_front(directive.starts_with(\\\"import\\\") ? 6 : 7);\\n        return after_keyword.count('\\\"') >= 2;\\n    }\\n    if(directive.contains('<')) {\\n        return directive.contains('>');\\n    }\\n    // No \\\" or < — might be a macro (#include FOO) or just incomplete (#include ).\\n    auto after_keyword = directive.drop_front(directive.starts_with(\\\"import\\\") ? 6 : 7).ltrim();\\n    return !after_keyword.empty();\\n}\\n\\n/// Check if a C++20 module statement line (import/export module) is complete.\\n/// A complete statement must end with ';'.\\nstatic bool is_module_statement_complete(llvm::StringRef trimmed) {\\n    return trimmed.rtrim().ends_with(\\\";\\\");\\n}\\n\\nbool is_preamble_complete(llvm::StringRef content, std::uint32_t bound) {\\n    auto preamble = content.substr(0, bound);\\n\\n    while(!preamble.empty()) {\\n        auto [line, rest] = preamble.split('\\\\n');\\n        preamble = rest;\\n\\n        auto trimmed = line.ltrim();\\n\\n        // Preprocessor directive: #include or #import\\n        if(trimmed.starts_with(\\\"#\\\")) {\\n            auto directive = trimmed.drop_front(1).ltrim();\\n            if(directive.starts_with(\\\"include\\\") || directive.starts_with(\\\"import\\\")) {\\n                if(!is_include_directive_complete(directive)) {\\n                    return false;\\n                }\\n            }\\n            continue;\\n        }\\n\\n        // C++20 module statements: import, export module, export import\\n        // Check word boundary to avoid matching identifiers like \\\"important\\\".\\n        auto is_keyword = [](llvm::StringRef s, llvm::StringRef keyword) {\\n            return s.starts_with(keyword) &&\\n                   (s.size() == keyword.size() || !llvm::isAlnum(s[keyword.size()]));\\n        };\\n        if(is_keyword(trimmed, \\\"import\\\") || is_keyword(trimmed, \\\"export\\\")) {\\n            if(!is_module_statement_complete(trimmed)) {\\n                return false;\\n            }\\n        }\\n    }\\n\\n    return true;\\n}\\n\\n}  // namespace clice\\n\"}}}"}
{"ts":4745,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/src/syntax/scan.h\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#pragma once\\n\\n#include <cstdint>\\n#include <string>\\n#include <vector>\\n\\n#include \\\"llvm/ADT/ArrayRef.h\\\"\\n#include \\\"llvm/ADT/IntrusiveRefCntPtr.h\\\"\\n#include \\\"llvm/ADT/StringMap.h\\\"\\n#include \\\"llvm/ADT/StringRef.h\\\"\\n#include \\\"llvm/Support/VirtualFileSystem.h\\\"\\n#include \\\"clang/Lex/DependencyDirectivesScanner.h\\\"\\n\\nnamespace clice {\\n\\nstruct ScanResult {\\n    /// Module name (empty if not a module unit).\\n    std::string module_name;\\n\\n    /// Whether this is an interface unit (has `export module`).\\n    bool is_interface_unit = false;\\n\\n    /// Whether module declaration is inside conditional directive,\\n    /// signaling caller should fall back to preprocessor-based scan.\\n    bool need_preprocess = false;\\n\\n    struct IncludeInfo {\\n        /// Resolved file path (fuzzy/precise scan) or raw header name (lexer scan).\\n        std::string path;\\n\\n        /// Whether this include is inside a conditional directive context.\\n        bool conditional = false;\\n\\n        /// Whether the included file was not found during resolution.\\n        bool not_found = false;\\n\\n        /// Whether this is an angled include (<...>) vs quoted (\\\"...\\\").\\n        bool is_angled = false;\\n\\n        /// Whether this is an #include_next directive.\\n        bool is_include_next = false;\\n    };\\n\\n    /// Include file names.\\n    /// From lexer scan these are the raw header names;\\n    /// from preprocessor scan these are resolved file paths.\\n    std::vector<IncludeInfo> includes;\\n\\n    /// Dependent module names.\\n    std::vector<std::string> modules;\\n};\\n\\n/// Shared cache for dependency directives across multiple scan invocations.\\nstruct SharedScanCache {\\n    struct CachedEntry {\\n        /// The source content of the file (kept alive for token references).\\n        std::string source;\\n\\n        /// Scanned tokens.\\n        llvm::SmallVector<clang::dependency_directives_scan::Token> tokens;\\n\\n        /// Scanned directives (referencing tokens above).\\n        llvm::SmallVector<clang::dependency_directives_scan::Directive> directives;\\n    };\\n\\n    /// path -> cached scan result.\\n    llvm::StringMap<CachedEntry> entries;\\n};\\n\\n/// Quick lexer-based scan for module name and include file names.\\n/// If module declaration is inside #if/#ifdef, sets need_preprocess=true\\n/// and module_name will be empty.\\nScanResult scan(llvm::StringRef content);\\n\\n/// Precise preprocessing-based scan. Keeps all directives including #define\\n/// and conditionals. Used for lazy module dependency resolution.\\nScanResult scan_precise(llvm::ArrayRef<const char*> arguments,\\n                        llvm::StringRef directory,\\n                        llvm::StringRef content = {},\\n                        SharedScanCache* cache = nullptr,\\n                        llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = nullptr);\\n\\n/// Lightweight preprocessor-based fallback for resolving module declarations\\n/// inside conditional directives (#if/#ifdef). When the quick `scan()` detects\\n/// `need_preprocess=true`, this function runs clang's preprocessor to evaluate\\n/// the conditions and extract the actual module name.\\n///\\n/// Much cheaper than `scan_precise()`: stops lexing as soon as the module\\n/// declaration is found, so it only processes the file preamble (global module\\n/// fragment + conditionals around the module declaration). Only populates\\n/// `module_name` and `is_interface_unit` in the returned ScanResult.\\nScanResult scan_module_decl(llvm::ArrayRef<const char*> arguments,\\n                            llvm::StringRef directory,\\n                            llvm::StringRef content = {},\\n                            SharedScanCache* cache = nullptr,\\n                            llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = nullptr);\\n\\n/// Compute preamble bound (moved from compile/preamble).\\nstd::uint32_t compute_preamble_bound(llvm::StringRef content);\\n\\nstd::vector<std::uint32_t> compute_preamble_bounds(llvm::StringRef content);\\n\\n/// Check if the preamble region contains only syntactically complete directives.\\n/// Returns false if any #include/#import has an unclosed \\\"\\\" or <>, or any\\n/// C++20 module statement (import/export) is missing a trailing ';',\\n/// indicating the user is still typing and PCH/PCM rebuild should be deferred.\\nbool is_preamble_complete(llvm::StringRef content, std::uint32_t bound);\\n\\n}  // namespace clice\\n\"}}}"}
{"ts":4745,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/unit/syntax/scan_tests.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include \\\"test/test.h\\\"\\n#include \\\"syntax/scan.h\\\"\\n\\nnamespace clice::testing {\\nnamespace {\\n\\nTEST_SUITE(Scan) {\\n\\n// === scan() tests ===\\n\\nTEST_CASE(BasicIncludes) {\\n    auto result = scan(R\\\"(\\n#include <vector>\\n#include \\\"foo/bar.h\\\"\\nint x = 1;\\n)\\\");\\n\\n    ASSERT_EQ(result.includes.size(), 2u);\\n    EXPECT_EQ(result.includes[0].path, \\\"vector\\\");\\n    EXPECT_TRUE(result.includes[0].is_angled);\\n    EXPECT_FALSE(result.includes[0].conditional);\\n    EXPECT_EQ(result.includes[1].path, \\\"foo/bar.h\\\");\\n    EXPECT_FALSE(result.includes[1].is_angled);\\n    EXPECT_FALSE(result.includes[1].conditional);\\n    EXPECT_TRUE(result.module_name.empty());\\n}\\n\\nTEST_CASE(ConditionalIncludes) {\\n    auto result = scan(R\\\"(\\n#include <always.h>\\n#ifdef FOO\\n#include <conditional.h>\\n#endif\\n#include <after.h>\\n)\\\");\\n\\n    ASSERT_EQ(result.includes.size(), 3u);\\n    EXPECT_EQ(result.includes[0].path, \\\"always.h\\\");\\n    EXPECT_FALSE(result.includes[0].conditional);\\n    EXPECT_EQ(result.includes[1].path, \\\"conditional.h\\\");\\n    EXPECT_TRUE(result.includes[1].conditional);\\n    EXPECT_EQ(result.includes[2].path, \\\"after.h\\\");\\n    EXPECT_FALSE(result.includes[2].conditional);\\n}\\n\\nTEST_CASE(NestedConditionals) {\\n    auto result = scan(R\\\"(\\n#ifdef A\\n#ifdef B\\n#include <nested.h>\\n#endif\\n#include <outer.h>\\n#endif\\n#include <top.h>\\n)\\\");\\n\\n    ASSERT_EQ(result.includes.size(), 3u);\\n    EXPECT_EQ(result.includes[0].path, \\\"nested.h\\\");\\n    EXPECT_TRUE(result.includes[0].conditional);\\n    EXPECT_EQ(result.includes[1].path, \\\"outer.h\\\");\\n    EXPECT_TRUE(result.includes[1].conditional);\\n    EXPECT_EQ(result.includes[2].path, \\\"top.h\\\");\\n    EXPECT_FALSE(result.includes[2].conditional);\\n}\\n\\nTEST_CASE(ModuleDeclaration) {\\n    auto result = scan(R\\\"(\\nmodule;\\n#include <header.h>\\nexport module my.module;\\n)\\\");\\n\\n    EXPECT_EQ(result.module_name, \\\"my.module\\\");\\n    EXPECT_TRUE(result.is_interface_unit);\\n    EXPECT_FALSE(result.need_preprocess);\\n    ASSERT_EQ(result.includes.size(), 1u);\\n    EXPECT_EQ(result.includes[0].path, \\\"header.h\\\");\\n    EXPECT_TRUE(result.includes[0].is_angled);\\n}\\n\\nTEST_CASE(ModulePartition) {\\n    auto result = scan(R\\\"(\\nmodule my.module:part;\\n)\\\");\\n\\n    EXPECT_EQ(result.module_name, \\\"my.module:part\\\");\\n    EXPECT_FALSE(result.is_interface_unit);\\n}\\n\\nTEST_CASE(ModuleImplementation) {\\n    auto result = scan(R\\\"(\\nmodule my.module;\\n)\\\");\\n\\n    EXPECT_EQ(result.module_name, \\\"my.module\\\");\\n    EXPECT_FALSE(result.is_interface_unit);\\n}\\n\\nTEST_CASE(ConditionalModule) {\\n    auto result = scan(R\\\"(\\n#ifdef USE_MODULES\\nexport module foo;\\n#endif\\n)\\\");\\n\\n    EXPECT_TRUE(result.module_name.empty());\\n    EXPECT_TRUE(result.need_preprocess);\\n}\\n\\nTEST_CASE(GlobalModuleFragment) {\\n    auto result = scan(R\\\"(\\nmodule;\\nexport module test;\\n)\\\");\\n\\n    EXPECT_EQ(result.module_name, \\\"test\\\");\\n    EXPECT_TRUE(result.is_interface_unit);\\n}\\n\\nTEST_CASE(EmptyContent) {\\n    auto result = scan(\\\"\\\");\\n    EXPECT_TRUE(result.includes.empty());\\n    EXPECT_TRUE(result.module_name.empty());\\n    EXPECT_FALSE(result.need_preprocess);\\n}\\n\\nTEST_CASE(NoDirectives) {\\n    auto result = scan(R\\\"(\\nint main() {\\n    return 0;\\n}\\n)\\\");\\n\\n    EXPECT_TRUE(result.includes.empty());\\n    EXPECT_TRUE(result.module_name.empty());\\n    EXPECT_FALSE(result.is_interface_unit);\\n    EXPECT_FALSE(result.need_preprocess);\\n}\\n\\n// === scan_precise() tests ===\\n\\nTEST_CASE(PreciseBasic) {\\n    auto vfs = llvm::makeIntrusiveRefCnt<TestVFS>();\\n    auto main_path = TestVFS::path(\\\"main.cpp\\\");\\n    vfs->add(\\\"main.cpp\\\", R\\\"(\\n#include \\\"header.h\\\"\\nint main() {}\\n)\\\");\\n    vfs->add(\\\"header.h\\\", R\\\"(\\n#pragma once\\nint x = 1;\\n)\\\");\\n\\n    auto args = std::vector<const char*>{\\\"clang++\\\", \\\"-std=c++20\\\", main_path.c_str()};\\n    auto result = scan_precise(args, TestVFS::root(), {}, nullptr, vfs);\\n\\n    ASSERT_EQ(result.includes.size(), 1u);\\n    EXPECT_FALSE(result.includes[0].not_found);\\n    EXPECT_FALSE(result.includes[0].conditional);\\n}\\n\\nTEST_CASE(PreciseConditionalWithDefine) {\\n    auto vfs = llvm::makeIntrusiveRefCnt<TestVFS>();\\n    auto main_path = TestVFS::path(\\\"main.cpp\\\");\\n    vfs->add(\\\"main.cpp\\\", R\\\"(\\n#define USE_FOO\\n#ifdef USE_FOO\\n#include \\\"foo.h\\\"\\n#endif\\n#ifndef USE_FOO\\n#include \\\"bar.h\\\"\\n#endif\\n)\\\");\\n    vfs->add(\\\"foo.h\\\");\\n    vfs->add(\\\"bar.h\\\");\\n\\n    auto args = std::vector<const char*>{\\\"clang++\\\", \\\"-std=c++20\\\", main_path.c_str()};\\n    auto result = scan_precise(args, TestVFS::root(), {}, nullptr, vfs);\\n\\n    // Precise mode evaluates conditionals: only foo.h should be included.\\n    ASSERT_EQ(result.includes.size(), 1u);\\n    EXPECT_TRUE(result.includes[0].conditional);\\n    EXPECT_TRUE(result.includes[0].path.find(\\\"foo.h\\\") != std::string::npos);\\n}\\n\\nTEST_CASE(PreciseWithContent) {\\n    auto vfs = llvm::makeIntrusiveRefCnt<TestVFS>();\\n    auto main_path = TestVFS::path(\\\"main.cpp\\\");\\n    vfs->add(\\\"main.cpp\\\");\\n    vfs->add(\\\"header.h\\\");\\n\\n    auto args = std::vector<const char*>{\\\"clang++\\\", \\\"-std=c++20\\\", main_path.c_str()};\\n    auto result = scan_precise(args, TestVFS::root(), R\\\"(#include \\\"header.h\\\")\\\", nullptr, vfs);\\n\\n    ASSERT_EQ(result.includes.size(), 1u);\\n    EXPECT_FALSE(result.includes[0].not_found);\\n}\\n\\n};  // TEST_SUITE(Scan)\\n\\nTEST_SUITE(PreambleBound) {\\n\\nTEST_CASE(Empty) {\\n    EXPECT_EQ(compute_preamble_bound(\\\"\\\"), 0u);\\n}\\n\\nTEST_CASE(NoDirectives) {\\n    EXPECT_EQ(compute_preamble_bound(\\\"int x = 1;\\\"), 0u);\\n}\\n\\nTEST_CASE(SingleInclude) {\\n    llvm::StringRef src = R\\\"(\\n#include <vector>\\nint x;\\n)\\\";\\n    auto bound = compute_preamble_bound(src);\\n    EXPECT_TRUE(bound > 0u);\\n    EXPECT_TRUE(bound <= src.find(\\\"int\\\"));\\n}\\n\\nTEST_CASE(MultipleDirectives) {\\n    llvm::StringRef src = R\\\"(\\n#include <vector>\\n#include <string>\\n#define FOO 1\\nint x;\\n)\\\";\\n    auto bound = compute_preamble_bound(src);\\n    EXPECT_TRUE(bound > src.find(\\\"#define\\\"));\\n}\\n\\nTEST_CASE(GlobalModuleFragment) {\\n    llvm::StringRef src = R\\\"(\\nmodule;\\n#include <vector>\\nexport module foo;\\n)\\\";\\n    auto bound = compute_preamble_bound(src);\\n    EXPECT_TRUE(bound > 0u);\\n    EXPECT_TRUE(bound < src.size());\\n}\\n\\nTEST_CASE(BoundsVector) {\\n    llvm::StringRef src = R\\\"(\\n#include <a>\\n#include <b>\\nint x;\\n)\\\";\\n    auto bounds = compute_preamble_bounds(src);\\n    ASSERT_EQ(bounds.size(), 2u);\\n    EXPECT_TRUE(bounds[0] < bounds[1]);\\n}\\n\\nTEST_CASE(BoundsWithModuleFragment) {\\n    llvm::StringRef src = R\\\"(\\nmodule;\\n#include <a>\\n#include <b>\\nexport module foo;\\n)\\\";\\n    auto bounds = compute_preamble_bounds(src);\\n    // module; + two #include = 3 bounds.\\n    ASSERT_EQ(bounds.size(), 3u);\\n    EXPECT_TRUE(bounds[0] < bounds[1]);\\n    EXPECT_TRUE(bounds[1] < bounds[2]);\\n}\\n\\nTEST_CASE(StopsAtCode) {\\n    llvm::StringRef src = R\\\"(\\n#include <a>\\nint x;\\n#include <b>\\n)\\\";\\n    auto bounds = compute_preamble_bounds(src);\\n    ASSERT_EQ(bounds.size(), 1u);\\n}\\n\\nTEST_CASE(ConditionalDirectives) {\\n    llvm::StringRef src = R\\\"(\\n#ifndef GUARD\\n#define GUARD\\n#include <a>\\n#endif\\nint x;\\n)\\\";\\n    auto bound = compute_preamble_bound(src);\\n    EXPECT_TRUE(bound > src.find(\\\"#endif\\\"));\\n}\\n\\n};  // TEST_SUITE(PreambleBound)\\n\\nTEST_SUITE(PreambleComplete) {\\n\\n// --- #include completeness ---\\n\\nTEST_CASE(CompleteQuotedInclude) {\\n    llvm::StringRef content = \\\"#include \\\\\\\"foo.h\\\\\\\"\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_TRUE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(CompleteAngledInclude) {\\n    llvm::StringRef content = \\\"#include <vector>\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_TRUE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(IncompleteQuotedInclude) {\\n    llvm::StringRef content = \\\"#include \\\\\\\"foo\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_FALSE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(IncompleteAngledInclude) {\\n    llvm::StringRef content = \\\"#include <sys/\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_FALSE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(IncludeWithNoPath) {\\n    llvm::StringRef content = \\\"#include \\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_FALSE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(IncludeMacroUsage) {\\n    llvm::StringRef content = \\\"#include FOO\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_TRUE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(MultipleIncludesAllComplete) {\\n    llvm::StringRef content = \\\"#include <vector>\\\\n#include \\\\\\\"foo.h\\\\\\\"\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_TRUE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(MultipleIncludesLastIncomplete) {\\n    llvm::StringRef content = \\\"#include <vector>\\\\n#include \\\\\\\"foo\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_FALSE(is_preamble_complete(content, bound));\\n}\\n\\n// --- C++20 module statements ---\\n// Note: compute_preamble_bound does not include import/export lines in its\\n// bound, so we pass manual bounds covering the relevant lines.\\n\\nTEST_CASE(CompleteImport) {\\n    llvm::StringRef content = \\\"import std;\\\\nint x;\\\";\\n    // Bound covers \\\"import std;\\\\n\\\".\\n    EXPECT_TRUE(is_preamble_complete(content, 12));\\n}\\n\\nTEST_CASE(ImportMissingSemicolon) {\\n    llvm::StringRef content = \\\"import std\\\\nint x;\\\";\\n    // Bound covers \\\"import std\\\\n\\\".\\n    EXPECT_FALSE(is_preamble_complete(content, 11));\\n}\\n\\nTEST_CASE(ImportWithNothing) {\\n    llvm::StringRef content = \\\"import \\\\nint x;\\\";\\n    // Bound covers \\\"import \\\\n\\\".\\n    EXPECT_FALSE(is_preamble_complete(content, 8));\\n}\\n\\nTEST_CASE(CompleteExportModule) {\\n    llvm::StringRef content = \\\"export module foo;\\\\nint x;\\\";\\n    // Bound covers \\\"export module foo;\\\\n\\\".\\n    EXPECT_TRUE(is_preamble_complete(content, 19));\\n}\\n\\nTEST_CASE(ExportModuleMissingSemicolon) {\\n    llvm::StringRef content = \\\"export module foo\\\\nint x;\\\";\\n    // Bound covers \\\"export module foo\\\\n\\\".\\n    EXPECT_FALSE(is_preamble_complete(content, 18));\\n}\\n\\nTEST_CASE(CompleteExportImport) {\\n    llvm::StringRef content = \\\"export import std;\\\\nint x;\\\";\\n    // Bound covers \\\"export import std;\\\\n\\\".\\n    EXPECT_TRUE(is_preamble_complete(content, 19));\\n}\\n\\n// --- Edge cases ---\\n\\nTEST_CASE(EmptyPreamble) {\\n    llvm::StringRef content = \\\"int x;\\\";\\n    EXPECT_TRUE(is_preamble_complete(content, 0));\\n}\\n\\nTEST_CASE(NonImportIncludeLinesIgnored) {\\n    llvm::StringRef content = \\\"#define FOO 1\\\\n#ifdef BAR\\\\n#endif\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_TRUE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(ImportantDoesNotMatchImport) {\\n    // \\\"important\\\" starts with \\\"import\\\" but should NOT be treated as an import.\\n    llvm::StringRef content = \\\"#include <vector>\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    // Manually test with content that has \\\"important\\\" within the preamble region.\\n    // Since compute_preamble_bound won't include non-directive lines, we test\\n    // is_preamble_complete directly with a crafted bound.\\n    llvm::StringRef crafted = \\\"important = 1;\\\\n\\\";\\n    EXPECT_TRUE(is_preamble_complete(crafted, crafted.size()));\\n}\\n\\nTEST_CASE(PreprocessorDirectivesIgnored) {\\n    llvm::StringRef content = \\\"#ifdef FOO\\\\n#define BAR 1\\\\n#endif\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_TRUE(is_preamble_complete(content, bound));\\n}\\n\\nTEST_CASE(MixedIncludeAndImportAllComplete) {\\n    llvm::StringRef content = \\\"#include <vector>\\\\nimport std;\\\\nint x;\\\";\\n    auto bound = compute_preamble_bound(content);\\n    EXPECT_TRUE(is_preamble_complete(content, bound));\\n}\\n\\n};  // TEST_SUITE(PreambleComplete)\\n\\n}  // namespace\\n}  // namespace clice::testing\\n\"}}}"}
{"ts":4746,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/include_completion/main.cpp\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#include \\\"\\n\"}}}"}
{"ts":4746,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/include_completion/myheader.h\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#pragma once\\nint my_func();\\n\"}}}"}
{"ts":4746,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didOpen\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/include_completion/subdir/nested.h\",\"languageId\":\"cpp\",\"version\":1,\"text\":\"#pragma once\\nint nested_func();\\n\"}}}"}
{"ts":4886,"msg":"{\"jsonrpc\":\"2.0\",\"id\":16,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":9,\"character\":27},\"end\":{\"line\":9,\"character\":27}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":4978,"msg":"{\"jsonrpc\":\"2.0\",\"id\":17,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":12,\"character\":0}}}}"}
{"ts":5179,"msg":"{\"jsonrpc\":\"2.0\",\"id\":18,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":12,\"character\":0}}}}"}
{"ts":5181,"msg":"{\"jsonrpc\":\"2.0\",\"id\":19,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":9,\"character\":27},\"end\":{\"line\":9,\"character\":27}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":5278,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":2},\"contentChanges\":[{\"range\":{\"start\":{\"line\":9,\"character\":27},\"end\":{\"line\":9,\"character\":27}},\"rangeLength\":0,\"text\":\"\\n    \"}]}}"}
{"ts":5278,"msg":"{\"jsonrpc\":\"2.0\",\"id\":20,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":5517,"msg":"{\"jsonrpc\":\"2.0\",\"id\":21,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":5606,"msg":"{\"jsonrpc\":\"2.0\",\"id\":22,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":5666,"msg":"{\"jsonrpc\":\"2.0\",\"id\":23,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":5666,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":3},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":4},\"end\":{\"line\":10,\"character\":4}},\"rangeLength\":0,\"text\":\"s\"}]}}"}
{"ts":5666,"msg":"{\"jsonrpc\":\"2.0\",\"id\":24,\"method\":\"textDocument/completion\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":10,\"character\":5},\"context\":{\"triggerKind\":1}}}"}
{"ts":5666,"msg":"{\"jsonrpc\":\"2.0\",\"id\":25,\"method\":\"textDocument/semanticTokens/full\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":5680,"msg":"{\"jsonrpc\":\"2.0\",\"id\":26,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":5775,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":4},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":5},\"end\":{\"line\":10,\"character\":5}},\"rangeLength\":0,\"text\":\"t\"}]}}"}
{"ts":5818,"msg":"{\"jsonrpc\":\"2.0\",\"id\":27,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":5954,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":5},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":6},\"end\":{\"line\":10,\"character\":6}},\"rangeLength\":0,\"text\":\"d\"}]}}"}
{"ts":5964,"msg":"{\"jsonrpc\":\"2.0\",\"id\":28,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":6202,"msg":"{\"jsonrpc\":\"2.0\",\"id\":29,\"method\":\"textDocument/semanticTokens/full\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":6202,"msg":"{\"jsonrpc\":\"2.0\",\"id\":30,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":6293,"msg":"{\"jsonrpc\":\"2.0\",\"id\":31,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":6294,"msg":"{\"jsonrpc\":\"2.0\",\"id\":32,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"context\":{\"diagnostics\":[{\"range\":{\"start\":{\"line\":10,\"character\":4},\"end\":{\"line\":10,\"character\":7}},\"message\":\"unexpected namespace name 'std': expected expression\",\"code\":\"err_unexpected_namespace\",\"severity\":1,\"source\":\"clang\"},{\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"message\":\"expected ';' after expression\",\"code\":\"err_expected_semi_after_expr\",\"severity\":1,\"source\":\"clang\"}],\"triggerKind\":2}}}"}
{"ts":6294,"msg":"{\"jsonrpc\":\"2.0\",\"id\":33,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":6473,"msg":"{\"jsonrpc\":\"2.0\",\"id\":34,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"context\":{\"diagnostics\":[{\"range\":{\"start\":{\"line\":10,\"character\":4},\"end\":{\"line\":10,\"character\":7}},\"message\":\"unexpected namespace name 'std': expected expression\",\"code\":\"err_unexpected_namespace\",\"severity\":1,\"source\":\"clang\"},{\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"message\":\"expected ';' after expression\",\"code\":\"err_expected_semi_after_expr\",\"severity\":1,\"source\":\"clang\"}],\"triggerKind\":2}}}"}
{"ts":6473,"msg":"{\"jsonrpc\":\"2.0\",\"id\":35,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"context\":{\"diagnostics\":[{\"range\":{\"start\":{\"line\":10,\"character\":4},\"end\":{\"line\":10,\"character\":7}},\"message\":\"unexpected namespace name 'std': expected expression\",\"code\":\"err_unexpected_namespace\",\"severity\":1,\"source\":\"clang\"},{\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"message\":\"expected ';' after expression\",\"code\":\"err_expected_semi_after_expr\",\"severity\":1,\"source\":\"clang\"}],\"triggerKind\":2}}}"}
{"ts":6474,"msg":"{\"jsonrpc\":\"2.0\",\"id\":36,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"context\":{\"diagnostics\":[{\"range\":{\"start\":{\"line\":10,\"character\":4},\"end\":{\"line\":10,\"character\":7}},\"message\":\"unexpected namespace name 'std': expected expression\",\"code\":\"err_unexpected_namespace\",\"severity\":1,\"source\":\"clang\"},{\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"message\":\"expected ';' after expression\",\"code\":\"err_expected_semi_after_expr\",\"severity\":1,\"source\":\"clang\"}],\"triggerKind\":2}}}"}
{"ts":6480,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":34}}"}
{"ts":6480,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":35}}"}
{"ts":6522,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":36}}"}
{"ts":6522,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":6},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":7},\"end\":{\"line\":10,\"character\":7}},\"rangeLength\":0,\"text\":\":\"}]}}"}
{"ts":6533,"msg":"{\"jsonrpc\":\"2.0\",\"id\":37,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":6649,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":7},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":8},\"end\":{\"line\":10,\"character\":8}},\"rangeLength\":0,\"text\":\":\"}]}}"}
{"ts":6677,"msg":"{\"jsonrpc\":\"2.0\",\"id\":38,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":6802,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":8},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":9},\"end\":{\"line\":10,\"character\":9}},\"rangeLength\":0,\"text\":\"c\"}]}}"}
{"ts":6811,"msg":"{\"jsonrpc\":\"2.0\",\"id\":39,\"method\":\"textDocument/completion\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":10,\"character\":10},\"context\":{\"triggerKind\":1}}}"}
{"ts":6841,"msg":"{\"jsonrpc\":\"2.0\",\"id\":40,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":6966,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":9},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":10},\"end\":{\"line\":10,\"character\":10}},\"rangeLength\":0,\"text\":\"o\"}]}}"}
{"ts":7013,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":40}}"}
{"ts":7013,"msg":"{\"jsonrpc\":\"2.0\",\"id\":41,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":7014,"msg":"{\"jsonrpc\":\"2.0\",\"id\":42,\"method\":\"textDocument/semanticTokens/full\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":7095,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":10},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":11},\"end\":{\"line\":10,\"character\":11}},\"rangeLength\":0,\"text\":\"u\"}]}}"}
{"ts":7138,"msg":"{\"jsonrpc\":\"2.0\",\"id\":43,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":7240,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":11},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":12},\"end\":{\"line\":10,\"character\":12}},\"rangeLength\":0,\"text\":\"t\"}]}}"}
{"ts":7282,"msg":"{\"jsonrpc\":\"2.0\",\"id\":44,\"method\":\"textDocument/completion\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":10,\"character\":13},\"context\":{\"triggerKind\":1}}}"}
{"ts":7282,"msg":"{\"jsonrpc\":\"2.0\",\"id\":45,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":7282,"msg":"{\"jsonrpc\":\"2.0\",\"id\":46,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":13},\"end\":{\"line\":10,\"character\":13}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":7282,"msg":"{\"jsonrpc\":\"2.0\",\"id\":47,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":7598,"msg":"{\"jsonrpc\":\"2.0\",\"id\":48,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":7598,"msg":"{\"jsonrpc\":\"2.0\",\"id\":49,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":7598,"msg":"{\"jsonrpc\":\"2.0\",\"id\":50,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":13},\"end\":{\"line\":10,\"character\":13}},\"context\":{\"diagnostics\":[{\"range\":{\"start\":{\"line\":10,\"character\":13},\"end\":{\"line\":10,\"character\":13}},\"message\":\"expected ';' after expression\",\"code\":\"err_expected_semi_after_expr\",\"severity\":1,\"source\":\"clang\"}],\"triggerKind\":2}}}"}
{"ts":7670,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":12},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":13},\"end\":{\"line\":10,\"character\":13}},\"rangeLength\":0,\"text\":\" \"}]}}"}
{"ts":7677,"msg":"{\"jsonrpc\":\"2.0\",\"id\":51,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":7710,"msg":"{\"jsonrpc\":\"2.0\",\"id\":52,\"method\":\"textDocument/semanticTokens/full\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":7966,"msg":"{\"jsonrpc\":\"2.0\",\"id\":53,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":8014,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":53}}"}
{"ts":8014,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":13},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":14},\"end\":{\"line\":10,\"character\":14}},\"rangeLength\":0,\"text\":\"<\"}]}}"}
{"ts":8014,"msg":"{\"jsonrpc\":\"2.0\",\"id\":54,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":8014,"msg":"{\"jsonrpc\":\"2.0\",\"id\":55,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":8014,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":54}}"}
{"ts":8182,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":14},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":15},\"end\":{\"line\":10,\"character\":15}},\"rangeLength\":0,\"text\":\"<\"}]}}"}
{"ts":8184,"msg":"{\"jsonrpc\":\"2.0\",\"id\":56,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":8411,"msg":"{\"jsonrpc\":\"2.0\",\"id\":57,\"method\":\"textDocument/semanticTokens/full\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":8478,"msg":"{\"jsonrpc\":\"2.0\",\"id\":58,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":8526,"msg":"{\"jsonrpc\":\"2.0\",\"id\":59,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":8526,"msg":"{\"jsonrpc\":\"2.0\",\"id\":60,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":16},\"end\":{\"line\":10,\"character\":16}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":8630,"msg":"{\"jsonrpc\":\"2.0\",\"id\":61,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":8838,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":15},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":16},\"end\":{\"line\":10,\"character\":16}},\"rangeLength\":0,\"text\":\" \"}]}}"}
{"ts":8838,"msg":"{\"jsonrpc\":\"2.0\",\"id\":62,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":9101,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":16},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":17},\"end\":{\"line\":10,\"character\":17}},\"rangeLength\":0,\"text\":\"\\\"\\\"\"}]}}"}
{"ts":9158,"msg":"{\"jsonrpc\":\"2.0\",\"id\":63,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":9258,"msg":"{\"jsonrpc\":\"2.0\",\"id\":64,\"method\":\"textDocument/semanticTokens/full\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":9434,"msg":"{\"jsonrpc\":\"2.0\",\"id\":65,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":9482,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":65}}"}
{"ts":9482,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":17},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":18},\"end\":{\"line\":10,\"character\":18}},\"rangeLength\":0,\"text\":\"H\"}]}}"}
{"ts":9482,"msg":"{\"jsonrpc\":\"2.0\",\"id\":66,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":9482,"msg":"{\"jsonrpc\":\"2.0\",\"id\":67,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":9482,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":66}}"}
{"ts":9778,"msg":"{\"jsonrpc\":\"2.0\",\"id\":68,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":9830,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":68}}"}
{"ts":9830,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":18},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":19},\"end\":{\"line\":10,\"character\":19}},\"rangeLength\":0,\"text\":\"e\"}]}}"}
{"ts":9830,"msg":"{\"jsonrpc\":\"2.0\",\"id\":69,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":9837,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":69}}"}
{"ts":9838,"msg":"{\"jsonrpc\":\"2.0\",\"id\":70,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":9910,"msg":"{\"jsonrpc\":\"2.0\",\"id\":71,\"method\":\"textDocument/semanticTokens/full\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":10016,"msg":"{\"jsonrpc\":\"2.0\",\"id\":72,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":20},\"end\":{\"line\":10,\"character\":20}},\"context\":{\"diagnostics\":[{\"range\":{\"start\":{\"line\":10,\"character\":20},\"end\":{\"line\":10,\"character\":20}},\"message\":\"expected ';' after expression\",\"code\":\"err_expected_semi_after_expr\",\"severity\":1,\"source\":\"clang\"}],\"triggerKind\":2}}}"}
{"ts":10083,"msg":"{\"jsonrpc\":\"2.0\",\"id\":73,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":10131,"msg":"{\"jsonrpc\":\"2.0\",\"id\":74,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":10153,"msg":"{\"jsonrpc\":\"2.0\",\"id\":75,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":10428,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":72}}"}
{"ts":10429,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":73}}"}
{"ts":10429,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":19},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":20},\"end\":{\"line\":10,\"character\":20}},\"rangeLength\":0,\"text\":\"l\"}]}}"}
{"ts":10476,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":70}}"}
{"ts":10476,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":74}}"}
{"ts":10476,"msg":"{\"jsonrpc\":\"2.0\",\"id\":76,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":10603,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":20},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":21},\"end\":{\"line\":10,\"character\":21}},\"rangeLength\":0,\"text\":\"l\"}]}}"}
{"ts":10640,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":76}}"}
{"ts":10640,"msg":"{\"jsonrpc\":\"2.0\",\"id\":77,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":10890,"msg":"{\"jsonrpc\":\"2.0\",\"id\":78,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":22},\"end\":{\"line\":10,\"character\":22}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":10938,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":78}}"}
{"ts":10938,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":21},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":22},\"end\":{\"line\":10,\"character\":22}},\"rangeLength\":0,\"text\":\"o\"}]}}"}
{"ts":10986,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":77}}"}
{"ts":10986,"msg":"{\"jsonrpc\":\"2.0\",\"id\":79,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":10986,"msg":"{\"jsonrpc\":\"2.0\",\"id\":80,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":10986,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":80}}"}
{"ts":11158,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":22},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":23},\"end\":{\"line\":10,\"character\":23}},\"rangeLength\":0,\"text\":\" \"}]}}"}
{"ts":11158,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":79}}"}
{"ts":11158,"msg":"{\"jsonrpc\":\"2.0\",\"id\":81,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":11330,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":23},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":24},\"end\":{\"line\":10,\"character\":24}},\"rangeLength\":0,\"text\":\"w\"}]}}"}
{"ts":11345,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":81}}"}
{"ts":11345,"msg":"{\"jsonrpc\":\"2.0\",\"id\":82,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":11542,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":24},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":25},\"end\":{\"line\":10,\"character\":25}},\"rangeLength\":0,\"text\":\"o\"}]}}"}
{"ts":11542,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":82}}"}
{"ts":11542,"msg":"{\"jsonrpc\":\"2.0\",\"id\":83,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":11646,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":25},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":26},\"end\":{\"line\":10,\"character\":26}},\"rangeLength\":0,\"text\":\"r\"}]}}"}
{"ts":11649,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":83}}"}
{"ts":11649,"msg":"{\"jsonrpc\":\"2.0\",\"id\":84,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":11715,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":26},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":27},\"end\":{\"line\":10,\"character\":27}},\"rangeLength\":0,\"text\":\"l\"}]}}"}
{"ts":11759,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":84}}"}
{"ts":11759,"msg":"{\"jsonrpc\":\"2.0\",\"id\":85,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":11901,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":27},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":28},\"end\":{\"line\":10,\"character\":28}},\"rangeLength\":0,\"text\":\"d\"}]}}"}
{"ts":11902,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":85}}"}
{"ts":11902,"msg":"{\"jsonrpc\":\"2.0\",\"id\":86,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":12158,"msg":"{\"jsonrpc\":\"2.0\",\"id\":87,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":29},\"end\":{\"line\":10,\"character\":29}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":12158,"msg":"{\"jsonrpc\":\"2.0\",\"id\":88,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":12222,"msg":"{\"jsonrpc\":\"2.0\",\"id\":89,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":12267,"msg":"{\"jsonrpc\":\"2.0\",\"id\":90,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":12466,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":87}}"}
{"ts":12466,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":88}}"}
{"ts":12466,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":28},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":30},\"end\":{\"line\":10,\"character\":30}},\"rangeLength\":0,\"text\":\" \"}]}}"}
{"ts":12558,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":86}}"}
{"ts":12558,"msg":"{\"jsonrpc\":\"2.0\",\"id\":91,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":12558,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":89}}"}
{"ts":12814,"msg":"{\"jsonrpc\":\"2.0\",\"id\":92,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":12845,"msg":"{\"jsonrpc\":\"2.0\",\"id\":93,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":12852,"msg":"{\"jsonrpc\":\"2.0\",\"id\":94,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":12931,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":92}}"}
{"ts":12931,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":29},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":31},\"end\":{\"line\":10,\"character\":31}},\"rangeLength\":0,\"text\":\"<\"}]}}"}
{"ts":13022,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":91}}"}
{"ts":13022,"msg":"{\"jsonrpc\":\"2.0\",\"id\":95,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":13022,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":93}}"}
{"ts":13130,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":30},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":32},\"end\":{\"line\":10,\"character\":32}},\"rangeLength\":0,\"text\":\"<\"}]}}"}
{"ts":13130,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":95}}"}
{"ts":13130,"msg":"{\"jsonrpc\":\"2.0\",\"id\":96,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":13450,"msg":"{\"jsonrpc\":\"2.0\",\"id\":97,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":33},\"end\":{\"line\":10,\"character\":33}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":13525,"msg":"{\"jsonrpc\":\"2.0\",\"id\":98,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":13559,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":97}}"}
{"ts":13560,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":98}}"}
{"ts":13560,"msg":"{\"jsonrpc\":\"2.0\",\"id\":99,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":13560,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":31},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":33},\"end\":{\"line\":10,\"character\":33}},\"rangeLength\":0,\"text\":\" \"}]}}"}
{"ts":13614,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":96}}"}
{"ts":13614,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":99}}"}
{"ts":13614,"msg":"{\"jsonrpc\":\"2.0\",\"id\":100,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":13614,"msg":"{\"jsonrpc\":\"2.0\",\"id\":101,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":13710,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":32},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":34},\"end\":{\"line\":10,\"character\":34}},\"rangeLength\":0,\"text\":\"s\"}]}}"}
{"ts":13710,"msg":"{\"jsonrpc\":\"2.0\",\"id\":102,\"method\":\"textDocument/completion\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":10,\"character\":35},\"context\":{\"triggerKind\":1}}}"}
{"ts":13710,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":100}}"}
{"ts":13710,"msg":"{\"jsonrpc\":\"2.0\",\"id\":103,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":13846,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":33},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":35},\"end\":{\"line\":10,\"character\":35}},\"rangeLength\":0,\"text\":\"t\"}]}}"}
{"ts":13846,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":103}}"}
{"ts":13846,"msg":"{\"jsonrpc\":\"2.0\",\"id\":104,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":13998,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":34},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":36},\"end\":{\"line\":10,\"character\":36}},\"rangeLength\":0,\"text\":\"d\"}]}}"}
{"ts":13998,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":104}}"}
{"ts":13998,"msg":"{\"jsonrpc\":\"2.0\",\"id\":105,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":14242,"msg":"{\"jsonrpc\":\"2.0\",\"id\":106,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":37},\"end\":{\"line\":10,\"character\":37}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":14262,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":106}}"}
{"ts":14262,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":35},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":37},\"end\":{\"line\":10,\"character\":37}},\"rangeLength\":0,\"text\":\":\"}]}}"}
{"ts":14305,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":105}}"}
{"ts":14306,"msg":"{\"jsonrpc\":\"2.0\",\"id\":107,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":14446,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":36},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":38},\"end\":{\"line\":10,\"character\":38}},\"rangeLength\":0,\"text\":\":\"}]}}"}
{"ts":14446,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":107}}"}
{"ts":14446,"msg":"{\"jsonrpc\":\"2.0\",\"id\":108,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":14598,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":37},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":39},\"end\":{\"line\":10,\"character\":39}},\"rangeLength\":0,\"text\":\"e\"}]}}"}
{"ts":14598,"msg":"{\"jsonrpc\":\"2.0\",\"id\":109,\"method\":\"textDocument/completion\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":10,\"character\":40},\"context\":{\"triggerKind\":1}}}"}
{"ts":14598,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":108}}"}
{"ts":14598,"msg":"{\"jsonrpc\":\"2.0\",\"id\":110,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":14718,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":38},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":40},\"end\":{\"line\":10,\"character\":40}},\"rangeLength\":0,\"text\":\"n\"}]}}"}
{"ts":14718,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":110}}"}
{"ts":14718,"msg":"{\"jsonrpc\":\"2.0\",\"id\":111,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":14858,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":39},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":41},\"end\":{\"line\":10,\"character\":41}},\"rangeLength\":0,\"text\":\"d\"}]}}"}
{"ts":14858,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":111}}"}
{"ts":14858,"msg":"{\"jsonrpc\":\"2.0\",\"id\":112,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":14989,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":40},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":42},\"end\":{\"line\":10,\"character\":42}},\"rangeLength\":0,\"text\":\"l\"}]}}"}
{"ts":14990,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":112}}"}
{"ts":14991,"msg":"{\"jsonrpc\":\"2.0\",\"id\":113,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":15174,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/didChange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\",\"version\":41},\"contentChanges\":[{\"range\":{\"start\":{\"line\":10,\"character\":43},\"end\":{\"line\":10,\"character\":43}},\"rangeLength\":0,\"text\":\";\"}]}}"}
{"ts":15195,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":113}}"}
{"ts":15195,"msg":"{\"jsonrpc\":\"2.0\",\"id\":114,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":15454,"msg":"{\"jsonrpc\":\"2.0\",\"id\":115,\"method\":\"textDocument/codeAction\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":10,\"character\":44},\"end\":{\"line\":10,\"character\":44}},\"context\":{\"diagnostics\":[],\"triggerKind\":2}}}"}
{"ts":15466,"msg":"{\"jsonrpc\":\"2.0\",\"id\":116,\"method\":\"textDocument/foldingRange\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":15515,"msg":"{\"jsonrpc\":\"2.0\",\"id\":117,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":15566,"msg":"{\"jsonrpc\":\"2.0\",\"id\":118,\"method\":\"textDocument/documentSymbol\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":15966,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":114}}"}
{"ts":15966,"msg":"{\"jsonrpc\":\"2.0\",\"id\":119,\"method\":\"textDocument/inlayHint\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"range\":{\"start\":{\"line\":0,\"character\":0},\"end\":{\"line\":13,\"character\":0}}}}"}
{"ts":16677,"msg":"{\"jsonrpc\":\"2.0\",\"id\":120,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":11,\"character\":12}}}"}
{"ts":16677,"msg":"{\"jsonrpc\":\"2.0\",\"id\":121,\"method\":\"textDocument/documentLink\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"}}}"}
{"ts":18279,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":120}}"}
{"ts":18510,"msg":"{\"jsonrpc\":\"2.0\",\"id\":122,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":7,\"character\":9}}}"}
{"ts":19831,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":122}}"}
{"ts":20030,"msg":"{\"jsonrpc\":\"2.0\",\"id\":123,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":12}}}"}
{"ts":21092,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":123}}"}
{"ts":21285,"msg":"{\"jsonrpc\":\"2.0\",\"id\":124,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":20}}}"}
{"ts":21404,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":124}}"}
{"ts":21662,"msg":"{\"jsonrpc\":\"2.0\",\"id\":125,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":7,\"character\":39}}}"}
{"ts":23066,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":125}}"}
{"ts":23422,"msg":"{\"jsonrpc\":\"2.0\",\"id\":126,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":18}}}"}
{"ts":24724,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":126}}"}
{"ts":24922,"msg":"{\"jsonrpc\":\"2.0\",\"id\":127,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":10}}}"}
{"ts":26768,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":127}}"}
{"ts":26966,"msg":"{\"jsonrpc\":\"2.0\",\"id\":128,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":7,\"character\":9}}}"}
{"ts":28269,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":128}}"}
{"ts":28606,"msg":"{\"jsonrpc\":\"2.0\",\"id\":129,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":7,\"character\":41}}}"}
{"ts":30260,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":129}}"}
{"ts":30462,"msg":"{\"jsonrpc\":\"2.0\",\"id\":130,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":13}}}"}
{"ts":30565,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":130}}"}
{"ts":30758,"msg":"{\"jsonrpc\":\"2.0\",\"id\":131,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":9,\"character\":12}}}"}
{"ts":30866,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":131}}"}
{"ts":31066,"msg":"{\"jsonrpc\":\"2.0\",\"id\":132,\"method\":\"textDocument/hover\",\"params\":{\"textDocument\":{\"uri\":\"file:///home/ykiko/C%2B%2B/clice/tests/data/hello_world/main.cpp\"},\"position\":{\"line\":11,\"character\":14}}}"}
{"ts":31251,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"$/cancelRequest\",\"params\":{\"id\":132}}"}
{"ts":33730,"msg":"{\"jsonrpc\":\"2.0\",\"id\":133,\"method\":\"shutdown\"}"}
{"ts":33774,"msg":"{\"jsonrpc\":\"2.0\",\"method\":\"exit\"}"}
