From 89d45fb48a5f8f044d4ee0710218655c37661f7a Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 11 Mar 2018 15:12:26 -0700 Subject: [PATCH] Reload .cquery compile_commands.json upon workspace/didChangeConfiguration. fix #378 And backport https://github.com/waf-project/waf/pull/2127 --- src/messages/cquery_freshen_index.cc | 23 ++--- src/messages/initialize.cc | 29 ++----- .../workspace_did_change_configuration.cc | 11 +++ src/project.cc | 85 +++++++++++-------- src/project.h | 31 ++++--- 5 files changed, 94 insertions(+), 85 deletions(-) diff --git a/src/messages/cquery_freshen_index.cc b/src/messages/cquery_freshen_index.cc index d731256f..bb08b2a1 100644 --- a/src/messages/cquery_freshen_index.cc +++ b/src/messages/cquery_freshen_index.cc @@ -4,6 +4,7 @@ #include "platform.h" #include "project.h" #include "queue_manager.h" +#include "timer.h" #include "timestamp_manager.h" #include "working_files.h" @@ -82,25 +83,11 @@ struct CqueryFreshenIndexHandler : BaseMessageHandler { } } - auto* queue = QueueManager::instance(); - + Timer time; // Send index requests for every file. - project->ForAllFilteredFiles( - config, [&](int i, const Project::Entry& entry) { - if (!need_index.count(entry.filename)) - return; - optional content = ReadContent(entry.filename); - if (!content) { - LOG_S(ERROR) << "When freshening index, cannot read file " - << entry.filename; - return; - } - bool is_interactive = - working_files->GetFileByFilename(entry.filename) != nullptr; - queue->index_request.PushBack( - Index_Request(entry.filename, entry.args, is_interactive, - *content, ICacheManager::Make(config))); - }); + project->Index(config, QueueManager::instance(), working_files, + std::monostate()); + time.ResetAndPrint("[perf] Dispatched $cquery/freshenIndex index requests"); } }; REGISTER_MESSAGE_HANDLER(CqueryFreshenIndexHandler); diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index e7b822f8..e0872449 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -487,7 +487,8 @@ struct InitializeHandler : BaseMessageHandler { LOG_S(INFO) << "Init parameters: " << output.GetString(); if (request->params.rootUri) { - std::string project_path = request->params.rootUri->GetPath(); + std::string project_path = + NormalizePath(request->params.rootUri->GetPath()); LOG_S(INFO) << "[querydb] Initialize in directory " << project_path << " with uri " << request->params.rootUri->raw_uri; @@ -575,8 +576,8 @@ struct InitializeHandler : BaseMessageHandler { QueueManager::WriteStdout(IpcId::Initialize, out); // Set project root. - config->projectRoot = NormalizePath(request->params.rootUri->GetPath()); - EnsureEndsInSlash(config->projectRoot); + EnsureEndsInSlash(project_path); + config->projectRoot = project_path; // Create two cache directories for files inside and outside of the // project. MakeDirectoryRecursive(config->cacheDirectory + @@ -589,9 +590,7 @@ struct InitializeHandler : BaseMessageHandler { semantic_cache->Init(config); // Open up / load the project. - project->Load(config, config->extraClangArguments, - config->compilationDatabaseDirectory, project_path, - config->resourceDirectory); + project->Load(config, project_path); time.ResetAndPrint("[perf] Loaded compilation entries (" + std::to_string(project->entries.size()) + " files)"); @@ -620,23 +619,9 @@ struct InitializeHandler : BaseMessageHandler { // files, because that takes a long time. include_complete->Rescan(); - auto* queue = QueueManager::instance(); time.Reset(); - project->ForAllFilteredFiles( - config, [&](int i, const Project::Entry& entry) { - optional content = ReadContent(entry.filename); - if (!content) { - LOG_S(ERROR) << "When loading project, canont read file " - << entry.filename; - return; - } - bool is_interactive = - working_files->GetFileByFilename(entry.filename) != nullptr; - queue->index_request.PushBack(Index_Request( - entry.filename, entry.args, is_interactive, *content, - ICacheManager::Make(config), request->id)); - }); - + project->Index(config, QueueManager::instance(), working_files, + request->id); // We need to support multiple concurrent index processes. time.ResetAndPrint("[perf] Dispatched initial index requests"); } diff --git a/src/messages/workspace_did_change_configuration.cc b/src/messages/workspace_did_change_configuration.cc index 2e672996..29b5f57d 100644 --- a/src/messages/workspace_did_change_configuration.cc +++ b/src/messages/workspace_did_change_configuration.cc @@ -2,6 +2,7 @@ #include "message_handler.h" #include "project.h" #include "queue_manager.h" +#include "timer.h" #include "working_files.h" namespace { @@ -21,6 +22,16 @@ REGISTER_IPC_MESSAGE(Ipc_WorkspaceDidChangeConfiguration); struct WorkspaceDidChangeConfigurationHandler : BaseMessageHandler { void Run(Ipc_WorkspaceDidChangeConfiguration* request) override { + Timer time; + project->Load(config, config->projectRoot); + time.ResetAndPrint("[perf] Loaded compilation entries (" + + std::to_string(project->entries.size()) + " files)"); + + time.Reset(); + project->Index(config, QueueManager::instance(), working_files, + std::monostate()); + time.ResetAndPrint( + "[perf] Dispatched workspace/didChangeConfiguration index requests"); } }; REGISTER_MESSAGE_HANDLER(WorkspaceDidChangeConfigurationHandler); diff --git a/src/project.cc b/src/project.cc index 0e6442cf..35d86aab 100644 --- a/src/project.cc +++ b/src/project.cc @@ -1,12 +1,15 @@ #include "project.h" +#include "cache_manager.h" #include "clang_utils.h" #include "language.h" #include "match.h" #include "platform.h" +#include "queue_manager.h" #include "serializers/json.h" #include "timer.h" #include "utils.h" +#include "working_files.h" #include #include @@ -364,22 +367,22 @@ std::vector LoadFromDirectoryListing(Config* init_opts, } std::vector LoadCompilationEntriesFromDirectory( - Config* init_opts, - ProjectConfig* config, + Config* config, + ProjectConfig* project, const std::string& opt_compilation_db_dir) { // If there is a .cquery file always load using directory listing. - if (FileExists(config->project_dir + "/.cquery")) - return LoadFromDirectoryListing(init_opts, config); + if (FileExists(project->project_dir + ".cquery")) + return LoadFromDirectoryListing(config, project); // If |compilationDatabaseCommand| is specified, execute it to get the compdb. std::string comp_db_dir; - if (init_opts->compilationDatabaseCommand.empty()) { - config->mode = ProjectMode::CompileCommandsJson; + if (config->compilationDatabaseCommand.empty()) { + project->mode = ProjectMode::CompileCommandsJson; // Try to load compile_commands.json, but fallback to a project listing. - comp_db_dir = opt_compilation_db_dir.empty() ? config->project_dir + comp_db_dir = opt_compilation_db_dir.empty() ? project->project_dir : opt_compilation_db_dir; } else { - config->mode = ProjectMode::ExternalCommand; + project->mode = ProjectMode::ExternalCommand; #ifdef _WIN32 // TODO #else @@ -390,10 +393,10 @@ std::vector LoadCompilationEntriesFromDirectory( rapidjson::StringBuffer input; rapidjson::Writer writer(input); JsonWriter json_writer(&writer); - Reflect(json_writer, *init_opts); + Reflect(json_writer, *config); std::string contents = GetExternalCommandOutput( - std::vector{init_opts->compilationDatabaseCommand, - config->project_dir}, + std::vector{config->compilationDatabaseCommand, + project->project_dir}, input.GetString()); std::ofstream(comp_db_dir + "/compile_commands.json") << contents; #endif @@ -403,7 +406,7 @@ std::vector LoadCompilationEntriesFromDirectory( CXCompilationDatabase_Error cx_db_load_error; CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory( comp_db_dir.c_str(), &cx_db_load_error); - if (!init_opts->compilationDatabaseCommand.empty()) { + if (!config->compilationDatabaseCommand.empty()) { #ifdef _WIN32 // TODO #else @@ -415,7 +418,7 @@ std::vector LoadCompilationEntriesFromDirectory( if (cx_db_load_error == CXCompilationDatabase_CanNotLoadDatabase) { LOG_S(INFO) << "Unable to load compile_commands.json located at \"" << comp_db_dir << "\"; using directory listing instead."; - return LoadFromDirectoryListing(init_opts, config); + return LoadFromDirectoryListing(config, project); } Timer clang_time; @@ -461,7 +464,7 @@ std::vector LoadCompilationEntriesFromDirectory( entry.file = NormalizePathWithTestOptOut(absolute_filename); result.push_back( - GetCompilationEntryFromCompileCommandEntry(init_opts, config, entry)); + GetCompilationEntryFromCompileCommandEntry(config, project, entry)); our_time.Pause(); } @@ -515,24 +518,20 @@ int ComputeGuessScore(const std::string& a, const std::string& b) { } // namespace -void Project::Load(Config* init_opts, - const std::vector& extra_flags, - const std::string& opt_compilation_db_dir, - const std::string& root_directory, - const std::string& resource_directory) { +void Project::Load(Config* config, const std::string& root_directory) { // Load data. - ProjectConfig config; - config.extra_flags = extra_flags; - config.project_dir = root_directory; - config.resource_dir = resource_directory; - entries = LoadCompilationEntriesFromDirectory(init_opts, &config, - opt_compilation_db_dir); + ProjectConfig project; + project.extra_flags = config->extraClangArguments; + project.project_dir = root_directory; + project.resource_dir = config->resourceDirectory; + entries = LoadCompilationEntriesFromDirectory( + config, &project, config->compilationDatabaseDirectory); // Cleanup / postprocess include directories. - quote_include_directories.assign(config.quote_dirs.begin(), - config.quote_dirs.end()); - angle_include_directories.assign(config.angle_dirs.begin(), - config.angle_dirs.end()); + quote_include_directories.assign(project.quote_dirs.begin(), + project.quote_dirs.end()); + angle_include_directories.assign(project.angle_dirs.begin(), + project.angle_dirs.end()); for (std::string& path : quote_include_directories) { EnsureEndsInSlash(path); LOG_S(INFO) << "quote_include_dir: " << path; @@ -607,23 +606,41 @@ void Project::ForAllFilteredFiles( } } +void Project::Index(Config* config, + QueueManager* queue, + WorkingFiles* wfiles, + lsRequestId id) { + ForAllFilteredFiles(config, [&](int i, const Project::Entry& entry) { + optional content = ReadContent(entry.filename); + if (!content) { + LOG_S(ERROR) << "When loading project, canont read file " + << entry.filename; + return; + } + bool is_interactive = wfiles->GetFileByFilename(entry.filename) != nullptr; + queue->index_request.PushBack( + Index_Request(entry.filename, entry.args, is_interactive, *content, + ICacheManager::Make(config), id)); + }); +} + TEST_SUITE("Project") { void CheckFlags(const std::string& directory, const std::string& file, std::vector raw, std::vector expected) { g_disable_normalize_path_for_test = true; - Config init_opts; - ProjectConfig config; - config.project_dir = "/w/c/s/"; - config.resource_dir = "/w/resource_dir/"; + Config config; + ProjectConfig project; + project.project_dir = "/w/c/s/"; + project.resource_dir = "/w/resource_dir/"; CompileCommandsEntry entry; entry.directory = directory; entry.args = raw; entry.file = file; Project::Entry result = - GetCompilationEntryFromCompileCommandEntry(&init_opts, &config, entry); + GetCompilationEntryFromCompileCommandEntry(&config, &project, entry); if (result.args != expected) { std::cout << "Raw: " << StringJoin(raw) << std::endl; diff --git a/src/project.h b/src/project.h index 42ba8033..17e41c59 100644 --- a/src/project.h +++ b/src/project.h @@ -4,12 +4,18 @@ #include #include +#include #include #include #include #include +// FIXME ipc.h +using lsRequestId = std::variant; +class QueueManager; +struct WorkingFiles; + struct Project { struct Entry { std::string filename; @@ -28,17 +34,15 @@ struct Project { // Loads a project for the given |directory|. // - // If |opt_compilation_db_dir| is not empty, the compile_commands.json - // file in it will be used to discover all files and args. If it's empty and - // |root_directory| contains a compile_commands.json file, that one will be - // used instead. Otherwise, a recursive directory listing of all *.cpp, *.cc, - // *.h, and *.hpp files will be used. clang arguments can be specified in a - // .cquery file located inside of |root_directory|. - void Load(Config* init_opts, - const std::vector& extra_flags, - const std::string& opt_compilation_db_dir, - const std::string& root_directory, - const std::string& resource_directory); + // If |config->compilationDatabaseDirectory| is not empty, look for .cquery or + // compile_commands.json in it, otherwise they are retrieved in + // |root_directory|. + // For .cquery, recursive directory listing is used and files with known + // suffixes are indexed. .cquery files can exist in subdirectories and they will affect + // flags in their subtrees (relative paths are relative to the project root, + // not subdirectories). + // For compile_commands.json, its entries are indexed. + void Load(Config* config, const std::string& root_directory); // Lookup the CompilationEntry for |filename|. If no entry was found this // will infer one based on existing project structure. @@ -48,4 +52,9 @@ struct Project { void ForAllFilteredFiles( Config* config, std::function action); + + void Index(Config* config, + QueueManager* queue, + WorkingFiles* wfiles, + lsRequestId id); };