From 7741991b72308e38c176feb623f8c144725a91b7 Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Sun, 23 Apr 2017 13:19:09 -0700 Subject: [PATCH] Simplify threading model a bit. --- src/command_line.cc | 924 ++++++++++++++++++++---------------------- src/ipc.cc | 6 - src/ipc.h | 19 - src/platform_linux.cc | 2 - src/platform_win.cc | 1 - 5 files changed, 447 insertions(+), 505 deletions(-) diff --git a/src/command_line.cc b/src/command_line.cc index 453064cc..ed366ae8 100644 --- a/src/command_line.cc +++ b/src/command_line.cc @@ -1102,8 +1102,6 @@ void IndexMain( // better icache behavior. We need to have some threads spinning on both though // otherwise memory usage will get bad. - int count = 0; - // We need to make sure to run both IndexMain_DoIndex and // IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any // work. Running both also lets the user query the partially constructed @@ -1112,10 +1110,9 @@ void IndexMain( bool did_create_update = IndexMain_DoCreateIndexUpdate(queue_on_id_mapped, queue_on_indexed); if (!did_index && !did_create_update) { - //if (count++ > 2) { - // count = 0; - IndexJoinIndexUpdates(queue_on_indexed); - //} + // Nothing to index and no index updates to create, so join some already + // created index updates to reduce work on querydb thread. + IndexJoinIndexUpdates(queue_on_indexed); // TODO: use CV to wakeup? std::this_thread::sleep_for(std::chrono::milliseconds(25)); @@ -1170,7 +1167,6 @@ void IndexMain( - void QueryDbMainLoop( @@ -1181,6 +1177,7 @@ void QueryDbMainLoop( Index_OnIdMappedQueue* queue_on_id_mapped, Index_OnIndexedQueue* queue_on_indexed, Project* project, + FileConsumer::SharedState* file_consumer_shared, WorkingFiles* working_files, CompletionManager* completion_manager) { IpcManager* ipc = IpcManager::instance(); @@ -1190,486 +1187,539 @@ void QueryDbMainLoop( std::cerr << "[querydb] Processing message " << IpcIdToString(message->method_id) << std::endl; switch (message->method_id) { - case IpcId::Quit: { - std::cerr << "[querydb] Got quit message (exiting)" << std::endl; - exit(0); - break; - } + case IpcId::Initialize: { + auto request = static_cast(message.get()); + if (request->params.rootUri) { + std::string project_path = request->params.rootUri->GetPath(); + std::cerr << "[stdin] Initialize in directory " << project_path + << " with uri " << request->params.rootUri->raw_uri + << std::endl; + + if (!request->params.initializationOptions) { + std::cerr << "Initialization parameters (particularily cacheDirectory) are required" << std::endl; + exit(1); + } - case IpcId::IsAlive: { - std::cerr << "[querydb] Sending IsAlive response to client" << std::endl; - ipc->SendMessage(IpcManager::Destination::Client, MakeUnique()); - break; - } + *config = *request->params.initializationOptions; - case IpcId::OpenProject: { - Ipc_OpenProject* msg = static_cast(message.get()); - std::string path = msg->project_path; + // Make sure cache directory is valid. + if (config->cacheDirectory.empty()) { + std::cerr << "No cache directory" << std::endl; + exit(1); + } + config->cacheDirectory = NormalizePath(config->cacheDirectory); + if (config->cacheDirectory[config->cacheDirectory.size() - 1] != '/') + config->cacheDirectory += '/'; + MakeDirectoryRecursive(config->cacheDirectory); + + // Start indexer threads. + int indexer_count = std::max(std::thread::hardware_concurrency(), 2) - 1; + if (config->indexerCount > 0) + indexer_count = config->indexerCount; + std::cerr << "[querydb] Starting " << indexer_count << " indexers" << std::endl; + for (int i = 0; i < indexer_count; ++i) { + new std::thread([&]() { + IndexMain(config, file_consumer_shared, project, queue_do_index, queue_do_id_map, queue_on_id_mapped, queue_on_indexed); + }); + } - project->Load(path); - std::cerr << "Loaded compilation entries (" << project->entries.size() << " files)" << std::endl; + // Open up / load the project. + project->Load(project_path); + std::cerr << "Loaded compilation entries (" << project->entries.size() << " files)" << std::endl; - project->ForAllFilteredFiles(config, [&](int i, const Project::Entry& entry) { - std::cerr << "[" << i << "/" << (project->entries.size() - 1) - << "] Dispatching index request for file " << entry.filename - << std::endl; + project->ForAllFilteredFiles(config, [&](int i, const Project::Entry& entry) { + std::cerr << "[" << i << "/" << (project->entries.size() - 1) + << "] Dispatching index request for file " << entry.filename + << std::endl; - queue_do_index->Enqueue(Index_DoIndex(Index_DoIndex::Type::ImportAndUpdate, entry.filename, entry.args)); - }); + queue_do_index->Enqueue(Index_DoIndex(Index_DoIndex::Type::ImportAndUpdate, entry.filename, entry.args)); + }); + } - break; - } + // TODO: query request->params.capabilities.textDocument and support only things + // the client supports. - case IpcId::CqueryFreshenIndex: { - std::cerr << "Freshening " << project->entries.size() << " files" << std::endl; - project->ForAllFilteredFiles(config, [&](int i, const Project::Entry& entry) { - std::cerr << "[" << i << "/" << (project->entries.size() - 1) - << "] Dispatching index request for file " << entry.filename - << std::endl; - queue_do_index->Enqueue(Index_DoIndex(Index_DoIndex::Type::Freshen, entry.filename, entry.args)); - }); - break; - } + auto response = Out_InitializeResponse(); + response.id = request->id; - case IpcId::TextDocumentDidOpen: { - // NOTE: This function blocks code lens. If it starts taking a long time - // we will need to find a way to unblock the code lens request. + //response.result.capabilities.textDocumentSync = lsTextDocumentSyncOptions(); + //response.result.capabilities.textDocumentSync->openClose = true; + //response.result.capabilities.textDocumentSync->change = lsTextDocumentSyncKind::Full; + //response.result.capabilities.textDocumentSync->willSave = true; + //response.result.capabilities.textDocumentSync->willSaveWaitUntil = true; + response.result.capabilities.textDocumentSync = lsTextDocumentSyncKind::Incremental; - Timer time; - auto msg = static_cast(message.get()); - WorkingFile* working_file = working_files->OnOpen(msg->params); - optional cached_file_contents = LoadCachedFileContents(config, msg->params.textDocument.uri.GetPath()); - if (cached_file_contents) - working_file->SetIndexContent(*cached_file_contents); - else - working_file->SetIndexContent(working_file->buffer_content); - time.ResetAndPrint("[querydb] Loading cached index file for DidOpen"); + response.result.capabilities.renameProvider = true; - break; - } - case IpcId::TextDocumentDidChange: { - auto msg = static_cast(message.get()); - working_files->OnChange(msg->params); - break; - } - case IpcId::TextDocumentDidClose: { - auto msg = static_cast(message.get()); - working_files->OnClose(msg->params); - break; - } + response.result.capabilities.completionProvider = lsCompletionOptions(); + response.result.capabilities.completionProvider->resolveProvider = false; + response.result.capabilities.completionProvider->triggerCharacters = { ".", "::", "->" }; - case IpcId::TextDocumentDidSave: { - auto msg = static_cast(message.get()); + response.result.capabilities.codeLensProvider = lsCodeLensOptions(); + response.result.capabilities.codeLensProvider->resolveProvider = false; - std::string path = msg->params.textDocument.uri.GetPath(); - // Send an index update request. - queue_do_index->Enqueue(Index_DoIndex(Index_DoIndex::Type::Parse, path, project->FindArgsForFile(path))); + response.result.capabilities.definitionProvider = true; + response.result.capabilities.documentHighlightProvider = true; + response.result.capabilities.hoverProvider = true; + response.result.capabilities.referencesProvider = true; - // Copy current buffer content so it can be applied when index request is done. - WorkingFile* working_file = working_files->GetFileByFilename(path); - if (working_file) - working_file->pending_new_index_content = working_file->buffer_content; + response.result.capabilities.documentSymbolProvider = true; + response.result.capabilities.workspaceSymbolProvider = true; - break; - } - - case IpcId::TextDocumentRename: { - auto msg = static_cast(message.get()); + ipc->SendOutMessageToClient(IpcId::Initialize, response); + break; + } - QueryFileId file_id; - QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath(), &file_id); - if (!file) { - std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + case IpcId::CqueryFreshenIndex: { + std::cerr << "Freshening " << project->entries.size() << " files" << std::endl; + project->ForAllFilteredFiles(config, [&](int i, const Project::Entry& entry) { + std::cerr << "[" << i << "/" << (project->entries.size() - 1) + << "] Dispatching index request for file " << entry.filename + << std::endl; + queue_do_index->Enqueue(Index_DoIndex(Index_DoIndex::Type::Freshen, entry.filename, entry.args)); + }); break; } - WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - Out_TextDocumentRename response; - response.id = msg->id; + case IpcId::TextDocumentDidOpen: { + // NOTE: This function blocks code lens. If it starts taking a long time + // we will need to find a way to unblock the code lens request. + + Timer time; + auto msg = static_cast(message.get()); + WorkingFile* working_file = working_files->OnOpen(msg->params); + optional cached_file_contents = LoadCachedFileContents(config, msg->params.textDocument.uri.GetPath()); + if (cached_file_contents) + working_file->SetIndexContent(*cached_file_contents); + else + working_file->SetIndexContent(working_file->buffer_content); + time.ResetAndPrint("[querydb] Loading cached index file for DidOpen"); - for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { - // Found symbol. Return references to rename. - std::vector uses = GetUsesOfSymbol(db, ref.idx); - response.result = BuildWorkspaceEdit(db, working_files, uses, msg->params.newName); + break; + } + case IpcId::TextDocumentDidChange: { + auto msg = static_cast(message.get()); + working_files->OnChange(msg->params); + break; + } + case IpcId::TextDocumentDidClose: { + auto msg = static_cast(message.get()); + working_files->OnClose(msg->params); break; } - response.Write(std::cerr); - ipc->SendOutMessageToClient(IpcId::TextDocumentRename, response); - break; - } - - case IpcId::TextDocumentCompletion: { - auto msg = static_cast(message.get()); - lsTextDocumentPositionParams params = msg->params; + case IpcId::TextDocumentDidSave: { + auto msg = static_cast(message.get()); - CompletionManager::OnComplete callback = std::bind([](BaseIpcMessage* message, const NonElidedVector& results) { - auto msg = static_cast(message); - auto ipc = IpcManager::instance(); + std::string path = msg->params.textDocument.uri.GetPath(); + // Send an index update request. + queue_do_index->Enqueue(Index_DoIndex(Index_DoIndex::Type::Parse, path, project->FindArgsForFile(path))); - Out_TextDocumentComplete response; - response.id = msg->id; - response.result.isIncomplete = false; - response.result.items = results; + // Copy current buffer content so it can be applied when index request is done. + WorkingFile* working_file = working_files->GetFileByFilename(path); + if (working_file) + working_file->pending_new_index_content = working_file->buffer_content; - Timer timer; - ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, response); - timer.ResetAndPrint("Writing completion results"); + break; + } - delete message; - }, message.release(), std::placeholders::_1); + case IpcId::TextDocumentRename: { + auto msg = static_cast(message.get()); - completion_manager->CodeComplete(params, std::move(callback)); + QueryFileId file_id; + QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath(), &file_id); + if (!file) { + std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + break; + } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - break; - } + Out_TextDocumentRename response; + response.id = msg->id; - case IpcId::TextDocumentDefinition: { - auto msg = static_cast(message.get()); + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { + // Found symbol. Return references to rename. + std::vector uses = GetUsesOfSymbol(db, ref.idx); + response.result = BuildWorkspaceEdit(db, working_files, uses, msg->params.newName); + break; + } - QueryFileId file_id; - QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath(), &file_id); - if (!file) { - std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + response.Write(std::cerr); + ipc->SendOutMessageToClient(IpcId::TextDocumentRename, response); break; } - WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - - Out_TextDocumentDefinition response; - response.id = msg->id; - - int target_line = msg->params.position.line + 1; - int target_column = msg->params.position.character + 1; - - for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { - // Found symbol. Return definition. - - // Special cases which are handled: - // - symbol has declaration but no definition (ie, pure virtual) - // - start at spelling but end at extent for better mouse tooltip - // - goto declaration while in definition of recursive type - - optional def_loc = GetDefinitionSpellingOfSymbol(db, ref.idx); - - // We use spelling start and extent end because this causes vscode to - // highlight the entire definition when previewing / hoving with the - // mouse. - optional def_extent = GetDefinitionExtentOfSymbol(db, ref.idx); - if (def_loc && def_extent) - def_loc->range.end = def_extent->range.end; - - // If the cursor is currently at or in the definition we should goto - // the declaration if possible. We also want to use declarations if - // we're pointing to, ie, a pure virtual function which has no - // definition. - if (!def_loc || (def_loc->path == file_id && - def_loc->range.Contains(target_line, target_column))) { - // Goto declaration. - - std::vector declarations = GetDeclarationsOfSymbolForGotoDefinition(db, ref.idx); - for (auto declaration : declarations) { - optional ls_declaration = GetLsLocation(db, working_files, declaration); - if (ls_declaration) - response.result.push_back(*ls_declaration); - } - // We found some declarations. Break so we don't add the definition location. - if (!response.result.empty()) - break; - } - if (def_loc) - PushBack(&response.result, GetLsLocation(db, working_files, *def_loc)); + case IpcId::TextDocumentCompletion: { + auto msg = static_cast(message.get()); + lsTextDocumentPositionParams params = msg->params; - if (!response.result.empty()) - break; - } + CompletionManager::OnComplete callback = std::bind([](BaseIpcMessage* message, const NonElidedVector& results) { + auto msg = static_cast(message); + auto ipc = IpcManager::instance(); - ipc->SendOutMessageToClient(IpcId::TextDocumentDefinition, response); - break; - } + Out_TextDocumentComplete response; + response.id = msg->id; + response.result.isIncomplete = false; + response.result.items = results; - case IpcId::TextDocumentDocumentHighlight: { - auto msg = static_cast(message.get()); + Timer timer; + ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, response); + timer.ResetAndPrint("Writing completion results"); + + delete message; + }, message.release(), std::placeholders::_1); + + completion_manager->CodeComplete(params, std::move(callback)); - QueryFileId file_id; - QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath(), &file_id); - if (!file) { - std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; break; } - WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - Out_TextDocumentDocumentHighlight response; - response.id = msg->id; + case IpcId::TextDocumentDefinition: { + auto msg = static_cast(message.get()); - for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { - // Found symbol. Return references to highlight. - std::vector uses = GetUsesOfSymbol(db, ref.idx); - response.result.reserve(uses.size()); - for (const QueryLocation& use : uses) { - if (use.path != file_id) - continue; + QueryFileId file_id; + QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath(), &file_id); + if (!file) { + std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + break; + } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - optional ls_location = GetLsLocation(db, working_files, use); - if (!ls_location) - continue; + Out_TextDocumentDefinition response; + response.id = msg->id; - lsDocumentHighlight highlight; - highlight.kind = lsDocumentHighlightKind::Text; - highlight.range = ls_location->range; - response.result.push_back(highlight); - } - break; - } + int target_line = msg->params.position.line + 1; + int target_column = msg->params.position.character + 1; + + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { + // Found symbol. Return definition. + + // Special cases which are handled: + // - symbol has declaration but no definition (ie, pure virtual) + // - start at spelling but end at extent for better mouse tooltip + // - goto declaration while in definition of recursive type + + optional def_loc = GetDefinitionSpellingOfSymbol(db, ref.idx); + + // We use spelling start and extent end because this causes vscode to + // highlight the entire definition when previewing / hoving with the + // mouse. + optional def_extent = GetDefinitionExtentOfSymbol(db, ref.idx); + if (def_loc && def_extent) + def_loc->range.end = def_extent->range.end; + + // If the cursor is currently at or in the definition we should goto + // the declaration if possible. We also want to use declarations if + // we're pointing to, ie, a pure virtual function which has no + // definition. + if (!def_loc || (def_loc->path == file_id && + def_loc->range.Contains(target_line, target_column))) { + // Goto declaration. + + std::vector declarations = GetDeclarationsOfSymbolForGotoDefinition(db, ref.idx); + for (auto declaration : declarations) { + optional ls_declaration = GetLsLocation(db, working_files, declaration); + if (ls_declaration) + response.result.push_back(*ls_declaration); + } + // We found some declarations. Break so we don't add the definition location. + if (!response.result.empty()) + break; + } - ipc->SendOutMessageToClient(IpcId::TextDocumentDocumentHighlight, response); - break; - } + if (def_loc) + PushBack(&response.result, GetLsLocation(db, working_files, *def_loc)); - case IpcId::TextDocumentHover: { - auto msg = static_cast(message.get()); + if (!response.result.empty()) + break; + } - QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath()); - if (!file) { - std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + ipc->SendOutMessageToClient(IpcId::TextDocumentDefinition, response); break; } - WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - Out_TextDocumentHover response; - response.id = msg->id; + case IpcId::TextDocumentDocumentHighlight: { + auto msg = static_cast(message.get()); - for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { - // Found symbol. Return hover. - optional ls_range = GetLsRange(working_files->GetFileByFilename(file->def.path), ref.loc.range); - if (!ls_range) - continue; + QueryFileId file_id; + QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath(), &file_id); + if (!file) { + std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + break; + } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - response.result.contents = GetHoverForSymbol(db, ref.idx); - response.result.range = *ls_range; - break; - } + Out_TextDocumentDocumentHighlight response; + response.id = msg->id; - ipc->SendOutMessageToClient(IpcId::TextDocumentHover, response); - break; - } + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { + // Found symbol. Return references to highlight. + std::vector uses = GetUsesOfSymbol(db, ref.idx); + response.result.reserve(uses.size()); + for (const QueryLocation& use : uses) { + if (use.path != file_id) + continue; + + optional ls_location = GetLsLocation(db, working_files, use); + if (!ls_location) + continue; - case IpcId::TextDocumentReferences: { - auto msg = static_cast(message.get()); + lsDocumentHighlight highlight; + highlight.kind = lsDocumentHighlightKind::Text; + highlight.range = ls_location->range; + response.result.push_back(highlight); + } + break; + } - QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath()); - if (!file) { - std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + ipc->SendOutMessageToClient(IpcId::TextDocumentDocumentHighlight, response); break; } - WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - Out_TextDocumentReferences response; - response.id = msg->id; + case IpcId::TextDocumentHover: { + auto msg = static_cast(message.get()); - for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { - optional excluded_declaration; - if (!msg->params.context.includeDeclaration) { - std::cerr << "Excluding declaration in references" << std::endl; - excluded_declaration = GetDefinitionSpellingOfSymbol(db, ref.idx); + QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath()); + if (!file) { + std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + break; } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); + + Out_TextDocumentHover response; + response.id = msg->id; - // Found symbol. Return references. - std::vector uses = GetUsesOfSymbol(db, ref.idx); - response.result.reserve(uses.size()); - for (const QueryLocation& use : uses) { - if (excluded_declaration.has_value() && use == *excluded_declaration) + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { + // Found symbol. Return hover. + optional ls_range = GetLsRange(working_files->GetFileByFilename(file->def.path), ref.loc.range); + if (!ls_range) continue; - optional ls_location = GetLsLocation(db, working_files, use); - if (ls_location) - response.result.push_back(*ls_location); + response.result.contents = GetHoverForSymbol(db, ref.idx); + response.result.range = *ls_range; + break; } + + ipc->SendOutMessageToClient(IpcId::TextDocumentHover, response); break; } - ipc->SendOutMessageToClient(IpcId::TextDocumentReferences, response); - break; - } + case IpcId::TextDocumentReferences: { + auto msg = static_cast(message.get()); + + QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath()); + if (!file) { + std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + break; + } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); - case IpcId::TextDocumentDocumentSymbol: { - auto msg = static_cast(message.get()); + Out_TextDocumentReferences response; + response.id = msg->id; - Out_TextDocumentDocumentSymbol response; - response.id = msg->id; + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { + optional excluded_declaration; + if (!msg->params.context.includeDeclaration) { + std::cerr << "Excluding declaration in references" << std::endl; + excluded_declaration = GetDefinitionSpellingOfSymbol(db, ref.idx); + } - QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath()); - if (!file) { - std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + // Found symbol. Return references. + std::vector uses = GetUsesOfSymbol(db, ref.idx); + response.result.reserve(uses.size()); + for (const QueryLocation& use : uses) { + if (excluded_declaration.has_value() && use == *excluded_declaration) + continue; + + optional ls_location = GetLsLocation(db, working_files, use); + if (ls_location) + response.result.push_back(*ls_location); + } + break; + } + + ipc->SendOutMessageToClient(IpcId::TextDocumentReferences, response); break; } - std::cerr << "File outline size is " << file->def.outline.size() << std::endl; - for (SymbolRef ref : file->def.outline) { - optional info = GetSymbolInfo(db, working_files, ref.idx); - if (!info) - continue; - - optional location = GetLsLocation(db, working_files, ref.loc); - if (!location) - continue; - info->location = *location; - response.result.push_back(*info); - } + case IpcId::TextDocumentDocumentSymbol: { + auto msg = static_cast(message.get()); - ipc->SendOutMessageToClient(IpcId::TextDocumentDocumentSymbol, response); - break; - } + Out_TextDocumentDocumentSymbol response; + response.id = msg->id; - case IpcId::TextDocumentCodeLens: { - auto msg = static_cast(message.get()); + QueryFile* file = FindFile(db, msg->params.textDocument.uri.GetPath()); + if (!file) { + std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + break; + } - Out_TextDocumentCodeLens response; - response.id = msg->id; + std::cerr << "File outline size is " << file->def.outline.size() << std::endl; + for (SymbolRef ref : file->def.outline) { + optional info = GetSymbolInfo(db, working_files, ref.idx); + if (!info) + continue; - lsDocumentUri file_as_uri = msg->params.textDocument.uri; + optional location = GetLsLocation(db, working_files, ref.loc); + if (!location) + continue; + info->location = *location; + response.result.push_back(*info); + } - QueryFile* file = FindFile(db, file_as_uri.GetPath()); - if (!file) { - std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; + ipc->SendOutMessageToClient(IpcId::TextDocumentDocumentSymbol, response); break; } - CommonCodeLensParams common; - common.result = &response.result; - common.db = db; - common.working_files = working_files; - common.working_file = working_files->GetFileByFilename(file->def.path); - - Timer time; - - for (SymbolRef ref : file->def.outline) { - // NOTE: We OffsetColumn so that the code lens always show up in a - // predictable order. Otherwise, the client may randomize it. - - SymbolIdx symbol = ref.idx; - switch (symbol.kind) { - case SymbolKind::Type: { - optional& type = db->types[symbol.idx]; - if (!type) - continue; - AddCodeLens(&common, ref.loc.OffsetStartColumn(0), type->uses, "ref", "refs"); - AddCodeLens(&common, ref.loc.OffsetStartColumn(1), ToQueryLocation(db, type->derived), "derived", "derived"); - AddCodeLens(&common, ref.loc.OffsetStartColumn(2), ToQueryLocation(db, type->instances), "var", "vars"); + + case IpcId::TextDocumentCodeLens: { + auto msg = static_cast(message.get()); + + Out_TextDocumentCodeLens response; + response.id = msg->id; + + lsDocumentUri file_as_uri = msg->params.textDocument.uri; + + QueryFile* file = FindFile(db, file_as_uri.GetPath()); + if (!file) { + std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; break; } - case SymbolKind::Func: { - optional& func = db->funcs[symbol.idx]; - if (!func) - continue; + CommonCodeLensParams common; + common.result = &response.result; + common.db = db; + common.working_files = working_files; + common.working_file = working_files->GetFileByFilename(file->def.path); + + Timer time; + + for (SymbolRef ref : file->def.outline) { + // NOTE: We OffsetColumn so that the code lens always show up in a + // predictable order. Otherwise, the client may randomize it. + + SymbolIdx symbol = ref.idx; + switch (symbol.kind) { + case SymbolKind::Type: { + optional& type = db->types[symbol.idx]; + if (!type) + continue; + AddCodeLens(&common, ref.loc.OffsetStartColumn(0), type->uses, "ref", "refs"); + AddCodeLens(&common, ref.loc.OffsetStartColumn(1), ToQueryLocation(db, type->derived), "derived", "derived"); + AddCodeLens(&common, ref.loc.OffsetStartColumn(2), ToQueryLocation(db, type->instances), "var", "vars"); + break; + } + case SymbolKind::Func: { + optional& func = db->funcs[symbol.idx]; + if (!func) + continue; - int offset = 0; + int offset = 0; - std::vector base_callers = GetCallersForAllBaseFunctions(db, *func); - std::vector derived_callers = GetCallersForAllDerivedFunctions(db, *func); - if (base_callers.empty() && derived_callers.empty()) { - // set exclude_loc to true to force the code lens to show up - AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, func->callers), "call", "calls", true /*exclude_loc*/); - } - else { - AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, func->callers), "direct call", "direct calls"); - if (!base_callers.empty()) - AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, base_callers), "base call", "base calls"); - if (!derived_callers.empty()) - AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, derived_callers), "derived call", "derived calls"); - } + std::vector base_callers = GetCallersForAllBaseFunctions(db, *func); + std::vector derived_callers = GetCallersForAllDerivedFunctions(db, *func); + if (base_callers.empty() && derived_callers.empty()) { + // set exclude_loc to true to force the code lens to show up + AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, func->callers), "call", "calls", true /*exclude_loc*/); + } + else { + AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, func->callers), "direct call", "direct calls"); + if (!base_callers.empty()) + AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, base_callers), "base call", "base calls"); + if (!derived_callers.empty()) + AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, derived_callers), "derived call", "derived calls"); + } - AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, func->derived), "derived", "derived"); - - // "Base" - optional base_loc = GetBaseDefinitionOrDeclarationSpelling(db, *func); - if (base_loc) { - optional ls_base = GetLsLocation(db, working_files, *base_loc); - if (ls_base) { - optional range = GetLsRange(common.working_file, ref.loc.range); - if (range) { - TCodeLens code_lens; - code_lens.range = *range; - code_lens.range.start.character += offset++; - code_lens.command = lsCommand(); - code_lens.command->title = "Base"; - code_lens.command->command = "superindex.goto"; - code_lens.command->arguments.uri = ls_base->uri; - code_lens.command->arguments.position = ls_base->range.start; - response.result.push_back(code_lens); + AddCodeLens(&common, ref.loc.OffsetStartColumn(offset++), ToQueryLocation(db, func->derived), "derived", "derived"); + + // "Base" + optional base_loc = GetBaseDefinitionOrDeclarationSpelling(db, *func); + if (base_loc) { + optional ls_base = GetLsLocation(db, working_files, *base_loc); + if (ls_base) { + optional range = GetLsRange(common.working_file, ref.loc.range); + if (range) { + TCodeLens code_lens; + code_lens.range = *range; + code_lens.range.start.character += offset++; + code_lens.command = lsCommand(); + code_lens.command->title = "Base"; + code_lens.command->command = "superindex.goto"; + code_lens.command->arguments.uri = ls_base->uri; + code_lens.command->arguments.position = ls_base->range.start; + response.result.push_back(code_lens); + } } } + + break; } + case SymbolKind::Var: { + optional& var = db->vars[symbol.idx]; + if (!var) + continue; - break; + AddCodeLens(&common, ref.loc.OffsetStartColumn(0), var->uses, "ref", "refs", true /*exclude_loc*/); + break; + } + case SymbolKind::File: + case SymbolKind::Invalid: { + assert(false && "unexpected"); + break; + } + }; } - case SymbolKind::Var: { - optional& var = db->vars[symbol.idx]; - if (!var) - continue; - AddCodeLens(&common, ref.loc.OffsetStartColumn(0), var->uses, "ref", "refs", true /*exclude_loc*/); - break; - } - case SymbolKind::File: - case SymbolKind::Invalid: { - assert(false && "unexpected"); - break; - } - }; + time.ResetAndPrint("[querydb] Building code lens for " + file->def.path); + ipc->SendOutMessageToClient(IpcId::TextDocumentCodeLens, response); + break; } - time.ResetAndPrint("[querydb] Building code lens for " + file->def.path); - ipc->SendOutMessageToClient(IpcId::TextDocumentCodeLens, response); - break; - } + case IpcId::WorkspaceSymbol: { + auto msg = static_cast(message.get()); - case IpcId::WorkspaceSymbol: { - auto msg = static_cast(message.get()); + Out_WorkspaceSymbol response; + response.id = msg->id; - Out_WorkspaceSymbol response; - response.id = msg->id; + std::cerr << "[querydb] Considering " << db->detailed_names.size() + << " candidates for query " << msg->params.query << std::endl; - std::cerr << "[querydb] Considering " << db->detailed_names.size() - << " candidates for query " << msg->params.query << std::endl; + std::string query = msg->params.query; + for (int i = 0; i < db->detailed_names.size(); ++i) { + if (response.result.size() >= config->maxWorkspaceSearchResults) { + std::cerr << "[querydb] - Query exceeded maximum number of responses (" << config->maxWorkspaceSearchResults << "), output may not contain all results" << std::endl; + break; + } - std::string query = msg->params.query; - for (int i = 0; i < db->detailed_names.size(); ++i) { - if (response.result.size() >= config->maxWorkspaceSearchResults) { - std::cerr << "[querydb] - Query exceeded maximum number of responses (" << config->maxWorkspaceSearchResults << "), output may not contain all results" << std::endl; - break; - } + if (db->detailed_names[i].find(query) != std::string::npos) { + optional info = GetSymbolInfo(db, working_files, db->symbols[i]); + if (!info) + continue; - if (db->detailed_names[i].find(query) != std::string::npos) { - optional info = GetSymbolInfo(db, working_files, db->symbols[i]); - if (!info) - continue; + optional location = GetDefinitionExtentOfSymbol(db, db->symbols[i]); + if (!location) { + auto decls = GetDeclarationsOfSymbolForGotoDefinition(db, db->symbols[i]); + if (decls.empty()) + continue; + location = decls[0]; + } - optional location = GetDefinitionExtentOfSymbol(db, db->symbols[i]); - if (!location) { - auto decls = GetDeclarationsOfSymbolForGotoDefinition(db, db->symbols[i]); - if (decls.empty()) + optional ls_location = GetLsLocation(db, working_files, *location); + if (!ls_location) continue; - location = decls[0]; + info->location = *ls_location; + response.result.push_back(*info); } - - optional ls_location = GetLsLocation(db, working_files, *location); - if (!ls_location) - continue; - info->location = *ls_location; - response.result.push_back(*info); } - } - std::cerr << "[querydb] - Found " << response.result.size() << " results for query " << query << std::endl; - ipc->SendOutMessageToClient(IpcId::WorkspaceSymbol, response); - break; - } + std::cerr << "[querydb] - Found " << response.result.size() << " results for query " << query << std::endl; + ipc->SendOutMessageToClient(IpcId::WorkspaceSymbol, response); + break; + } - default: { - std::cerr << "[querydb] Unhandled IPC message " << IpcIdToString(message->method_id) << std::endl; - exit(1); - } + default: { + std::cerr << "[querydb] Unhandled IPC message " << IpcIdToString(message->method_id) << std::endl; + exit(1); + } } } @@ -1747,22 +1797,11 @@ void QueryDbMain(IndexerConfig* config) { CompletionManager completion_manager(config, &project, &working_files); FileConsumer::SharedState file_consumer_shared; - // Start indexer threads. - int indexer_count = std::max(std::thread::hardware_concurrency(), 2) - 1; - if (config->indexerCount > 0) - indexer_count = config->indexerCount; - std::cerr << "[querydb] Starting " << indexer_count << " indexers" << std::endl; - for (int i = 0; i < indexer_count; ++i) { - new std::thread([&]() { - IndexMain(config, &file_consumer_shared, &project, &queue_do_index, &queue_do_id_map, &queue_on_id_mapped, &queue_on_indexed); - }); - } - // Run query db main loop. SetCurrentThreadName("querydb"); QueryDatabase db; while (true) { - QueryDbMainLoop(config, &db, &queue_do_index, &queue_do_id_map, &queue_on_id_mapped, &queue_on_indexed, &project, &working_files, &completion_manager); + QueryDbMainLoop(config, &db, &queue_do_index, &queue_do_id_map, &queue_on_id_mapped, &queue_on_indexed, &project, &file_consumer_shared, &working_files, &completion_manager); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } @@ -1852,78 +1891,6 @@ void LanguageServerStdinLoop(IndexerConfig* config, std::unordered_mapmethod_id) << '"' << std::endl; switch (message->method_id) { - // TODO: For simplicitly lets just proxy the initialize request like - // all other requests so that stdin loop thread becomes super simple. - case IpcId::Initialize: { - auto request = static_cast(message.get()); - if (request->params.rootUri) { - std::string project_path = request->params.rootUri->GetPath(); - std::cerr << "[stdin] Initialize in directory " << project_path - << " with uri " << request->params.rootUri->raw_uri - << std::endl; - auto open_project = MakeUnique(); - open_project->project_path = project_path; - - if (!request->params.initializationOptions) { - std::cerr << "Initialization parameters (particularily cacheDirectory) are required" << std::endl; - exit(1); - } - - *config = *request->params.initializationOptions; - - // Make sure cache directory is valid. - if (config->cacheDirectory.empty()) { - std::cerr << "No cache directory" << std::endl; - exit(1); - } - config->cacheDirectory = NormalizePath(config->cacheDirectory); - if (config->cacheDirectory[config->cacheDirectory.size() - 1] != '/') - config->cacheDirectory += '/'; - MakeDirectoryRecursive(config->cacheDirectory); - - // Startup querydb now that we have initialization state. - new std::thread([&config]() { - QueryDbMain(config); - }); - - ipc->SendMessage(IpcManager::Destination::Server, std::move(open_project)); - } - - // TODO: query request->params.capabilities.textDocument and support only things - // the client supports. - - auto response = Out_InitializeResponse(); - response.id = request->id; - - //response.result.capabilities.textDocumentSync = lsTextDocumentSyncOptions(); - //response.result.capabilities.textDocumentSync->openClose = true; - //response.result.capabilities.textDocumentSync->change = lsTextDocumentSyncKind::Full; - //response.result.capabilities.textDocumentSync->willSave = true; - //response.result.capabilities.textDocumentSync->willSaveWaitUntil = true; - response.result.capabilities.textDocumentSync = lsTextDocumentSyncKind::Incremental; - - response.result.capabilities.renameProvider = true; - - response.result.capabilities.completionProvider = lsCompletionOptions(); - response.result.capabilities.completionProvider->resolveProvider = false; - response.result.capabilities.completionProvider->triggerCharacters = { ".", "::", "->" }; - - response.result.capabilities.codeLensProvider = lsCodeLensOptions(); - response.result.capabilities.codeLensProvider->resolveProvider = false; - - response.result.capabilities.definitionProvider = true; - response.result.capabilities.documentHighlightProvider = true; - response.result.capabilities.hoverProvider = true; - response.result.capabilities.referencesProvider = true; - - response.result.capabilities.documentSymbolProvider = true; - response.result.capabilities.workspaceSymbolProvider = true; - - //response.Write(std::cerr); - response.Write(std::cout); - break; - } - case IpcId::Initialized: { // TODO: don't send output until we get this notification break; @@ -1934,6 +1901,7 @@ void LanguageServerStdinLoop(IndexerConfig* config, std::unordered_map* request_times) { std::vector> messages = ipc->GetMessages(IpcManager::Destination::Client); for (auto& message : messages) { - std::cerr << "[server] Processing message " << IpcIdToString(message->method_id) << std::endl; + std::cerr << "[stdout] Processing message " << IpcIdToString(message->method_id) << std::endl; switch (message->method_id) { - case IpcId::Quit: { - std::cerr << "[server] Got quit message (exiting)" << std::endl; - exit(0); - break; - } - case IpcId::Cout: { auto msg = static_cast(message.get()); @@ -2032,7 +1994,7 @@ void LanguageServerMainLoop(std::unordered_map* request_times) { } default: { - std::cerr << "[server] Unhandled IPC message " << IpcIdToString(message->method_id) << std::endl; + std::cerr << "[stdout] Unhandled IPC message " << IpcIdToString(message->method_id) << std::endl; exit(1); } } @@ -2042,12 +2004,20 @@ void LanguageServerMainLoop(std::unordered_map* request_times) { void LanguageServerMain(IndexerConfig* config) { std::unordered_map request_times; - // Run language client. + // Start stdin reader. Reading from stdin is a blocking operation so this + // needs a dedicated thread. new std::thread([&]() { LanguageServerStdinLoop(config, &request_times); }); - SetCurrentThreadName("server"); + // Start querydb thread. querydb will start indexer threads as needed. + new std::thread([&config]() { + QueryDbMain(config); + }); + + // We run a dedicated thread for writing to stdout because there can be an + // unknown number of delays when output information. + SetCurrentThreadName("stdout"); while (true) { LanguageServerMainLoop(&request_times); std::this_thread::sleep_for(std::chrono::milliseconds(2)); diff --git a/src/ipc.cc b/src/ipc.cc index 339da10c..0148317c 100644 --- a/src/ipc.cc +++ b/src/ipc.cc @@ -43,12 +43,6 @@ const char* IpcIdToString(IpcId id) { return "$cquery/freshenIndex"; } - case IpcId::Quit: - return "$quit"; - case IpcId::IsAlive: - return "$isAlive"; - case IpcId::OpenProject: - return "$openProject"; case IpcId::Cout: return "$cout"; default: diff --git a/src/ipc.h b/src/ipc.h index 04191e42..7a20fd93 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -29,9 +29,6 @@ enum class IpcId : int { CqueryFreshenIndex, // Internal implementation detail. - Quit, - IsAlive, - OpenProject, Cout }; MAKE_ENUM_HASHABLE(IpcId) @@ -48,22 +45,6 @@ struct IpcMessage : public BaseIpcMessage { IpcMessage() : BaseIpcMessage(T::kIpcId) {} }; -struct Ipc_Quit : public IpcMessage { - static constexpr IpcId kIpcId = IpcId::Quit; -}; -MAKE_REFLECT_EMPTY_STRUCT(Ipc_Quit); - -struct Ipc_IsAlive : public IpcMessage { - static constexpr IpcId kIpcId = IpcId::IsAlive; -}; -MAKE_REFLECT_EMPTY_STRUCT(Ipc_IsAlive); - -struct Ipc_OpenProject : public IpcMessage { - static constexpr IpcId kIpcId = IpcId::OpenProject; - std::string project_path; -}; -MAKE_REFLECT_STRUCT(Ipc_OpenProject, project_path); - struct Ipc_Cout : public IpcMessage { static constexpr IpcId kIpcId = IpcId::Cout; std::string content; diff --git a/src/platform_linux.cc b/src/platform_linux.cc index 1191b772..5798b262 100644 --- a/src/platform_linux.cc +++ b/src/platform_linux.cc @@ -149,8 +149,6 @@ std::string NormalizePath(const std::string& path) { } bool TryMakeDirectory(const std::string& absolute_path) { - std::cerr << "!! TryMakeDirectory " << absolute_path << std::endl; - const mode_t kMode = 0777; // UNIX style permissions if (mkdir(absolute_path.c_str(), kMode) == -1) { // Success if the directory exists. diff --git a/src/platform_win.cc b/src/platform_win.cc index b2669076..ec6d3754 100644 --- a/src/platform_win.cc +++ b/src/platform_win.cc @@ -150,7 +150,6 @@ std::string NormalizePath(const std::string& path) { } bool TryMakeDirectory(const std::string& absolute_path) { - std::cerr << "!! TryMakeDirectory " << absolute_path << std::endl; if (_mkdir(absolute_path.c_str()) == -1) { // Success if the directory exists. return errno == EEXIST;