diff --git a/CMakeLists.txt b/CMakeLists.txt index 97694a29..ae4500b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,6 +216,7 @@ target_sources(ccls PRIVATE src/import_pipeline.cc src/include_complete.cc src/method.cc + src/language.cc src/lex_utils.cc src/lsp.cc src/match.cc diff --git a/README.md b/README.md index d6495551..35553bfc 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ccls is a fork of cquery (originally written by Jacob Dufault), a C/C++/Objective-C language server. * code completion (with both signature help and snippets) - * finding [definition](src/messages/text_document_definition.cc)/[references](src/messages/text_document_references.cc) + * [definition](src/messages/text_document_definition.cc)/[references](src/messages/text_document_references.cc), and other cross references * [call (caller/callee) hierarchy](src/messages/ccls_call_hierarchy.cc), [inheritance (base/derived) hierarchy](src/messages/ccls_inheritance_hierarchy.cc), [member hierarchy](src/messages/ccls_member_hierarchy.cc) * [symbol rename](src/messages/text_document_rename.cc) * [document symbols](src/messages/text_document_document_symbol.cc) and approximate search of [workspace symbol](src/messages/workspace_symbol.cc) @@ -12,13 +12,12 @@ a C/C++/Objective-C language server. * diagnostics * code actions (clang FixIts) * preprocessor skipped regions - * #include auto-complete, undefined type include insertion, include quick-jump - (goto definition, document links) - * auto-implement functions without a definition * semantic highlighting, including support for [rainbow semantic highlighting](https://medium.com/@evnbr/coding-in-color-3a6db2743a1e) -# >>> [Getting started](https://github.com/MaskRay/ccls/wiki/Getting-started) (CLICK HERE) <<< +It makes use of C++17 features, has less third-party dependencies and slimmed-down code base. Cross reference features are strenghened, (see [wiki/FAQ]). It currently uses libclang to index C++ code but will switch to Clang C++ API. Refactoring and formatting are non-goals as they can be provided by clang-format, clang-include-fixer and other Clang based tools. -# License +# >>> [Getting started](../../wiki/Getting-started) (CLICK HERE) <<< -MIT +* [Build](../../wiki/Build) +* [Emacs](../../wiki/Emacs) +* [FAQ](../../wiki/FAQ) diff --git a/src/clang_indexer.cc b/src/clang_indexer.cc index 327d7fc2..a5df8596 100644 --- a/src/clang_indexer.cc +++ b/src/clang_indexer.cc @@ -718,27 +718,20 @@ std::string IndexFile::ToString() { return Serialize(SerializeFormat::Json, *this); } -void Uniquify(std::vector& ids) { +void Uniquify(std::vector& usrs) { std::unordered_set seen; size_t n = 0; - for (size_t i = 0; i < ids.size(); i++) - if (seen.insert(ids[i]).second) - ids[n++] = ids[i]; - ids.resize(n); + for (size_t i = 0; i < usrs.size(); i++) + if (seen.insert(usrs[i]).second) + usrs[n++] = usrs[i]; + usrs.resize(n); } void Uniquify(std::vector& uses) { - union U { - Range range = {}; - uint64_t u64; - }; - static_assert(sizeof(Range) == 8); - std::unordered_set seen; + std::unordered_set seen; size_t n = 0; for (size_t i = 0; i < uses.size(); i++) { - U u; - u.range = uses[i].range; - if (seen.insert(u.u64).second) + if (seen.insert(uses[i].range).second) uses[n++] = uses[i]; } uses.resize(n); diff --git a/src/import_pipeline.cc b/src/import_pipeline.cc index a299f38c..210dc72b 100644 --- a/src/import_pipeline.cc +++ b/src/import_pipeline.cc @@ -37,43 +37,6 @@ MAKE_REFLECT_STRUCT(Out_Progress::Params, activeThreads); MAKE_REFLECT_STRUCT(Out_Progress, jsonrpc, method, params); -// Instead of processing messages forever, we only process upto -// |kIterationSize| messages of a type at one time. While the import time -// likely stays the same, this should reduce overall queue lengths which means -// the user gets a usable index faster. -struct IterationLoop { - const int kIterationSize = 100; - int count = 0; - - bool Next() { - return count++ < kIterationSize; - } - void Reset() { - count = 0; - } -}; - -struct IModificationTimestampFetcher { - virtual ~IModificationTimestampFetcher() = default; - virtual std::optional LastWriteTime(const std::string& path) = 0; -}; -struct RealModificationTimestampFetcher : IModificationTimestampFetcher { - // IModificationTimestamp: - std::optional LastWriteTime(const std::string& path) override { - return ::LastWriteTime(path); - } -}; -struct FakeModificationTimestampFetcher : IModificationTimestampFetcher { - std::unordered_map> entries; - - // IModificationTimestamp: - std::optional LastWriteTime(const std::string& path) override { - auto it = entries.find(path); - assert(it != entries.end()); - return it->second; - } -}; - long long GetCurrentTimeInMilliseconds() { auto time_since_epoch = Timer::Clock::now().time_since_epoch(); long long elapsed_milliseconds = @@ -138,7 +101,6 @@ enum class ShouldParse { Yes, No, NoSuchFile }; ShouldParse FileNeedsParse( bool is_interactive, TimestampManager* timestamp_manager, - IModificationTimestampFetcher* modification_timestamp_fetcher, const std::shared_ptr& cache_manager, IndexFile* opt_previous_index, const std::string& path, @@ -150,8 +112,7 @@ ShouldParse FileNeedsParse( return ""; }; - std::optional modification_timestamp = - modification_timestamp_fetcher->LastWriteTime(path); + std::optional modification_timestamp = LastWriteTime(path); // Cannot find file. if (!modification_timestamp) @@ -193,7 +154,6 @@ enum CacheLoadResult { Parse, DoNotParse }; CacheLoadResult TryLoadFromCache( FileConsumerSharedState* file_consumer_shared, TimestampManager* timestamp_manager, - IModificationTimestampFetcher* modification_timestamp_fetcher, const std::shared_ptr& cache_manager, bool is_interactive, const Project::Entry& entry, @@ -209,10 +169,9 @@ CacheLoadResult TryLoadFromCache( // from cache. // Check timestamps and update |file_consumer_shared|. - ShouldParse path_state = FileNeedsParse( - is_interactive, timestamp_manager, modification_timestamp_fetcher, - cache_manager, previous_index, path_to_index, entry.args, - std::nullopt); + ShouldParse path_state = + FileNeedsParse(is_interactive, timestamp_manager, cache_manager, + previous_index, path_to_index, entry.args, std::nullopt); if (path_state == ShouldParse::Yes) file_consumer_shared->Reset(path_to_index); @@ -228,8 +187,7 @@ CacheLoadResult TryLoadFromCache( for (const std::string& dependency : previous_index->dependencies) { assert(!dependency.empty()); - if (FileNeedsParse(is_interactive, timestamp_manager, - modification_timestamp_fetcher, cache_manager, + if (FileNeedsParse(is_interactive, timestamp_manager, cache_manager, previous_index, dependency, entry.args, previous_index->path) == ShouldParse::Yes) { needs_reparse = true; @@ -245,7 +203,7 @@ CacheLoadResult TryLoadFromCache( return CacheLoadResult::Parse; // No timestamps changed - load directly from cache. - LOG_S(INFO) << "Skipping parse; no timestamp change for " << path_to_index; + LOG_S(INFO) << "load index for " << path_to_index; // TODO/FIXME: real perf PerformanceImportFile perf; @@ -333,7 +291,6 @@ void ParseFile(DiagnosticsEngine* diag_engine, WorkingFiles* working_files, FileConsumerSharedState* file_consumer_shared, TimestampManager* timestamp_manager, - IModificationTimestampFetcher* modification_timestamp_fetcher, IIndexer* indexer, const Index_Request& request, const Project::Entry& entry) { @@ -349,11 +306,9 @@ void ParseFile(DiagnosticsEngine* diag_engine, // Try to load the file from cache. if (TryLoadFromCache(file_consumer_shared, timestamp_manager, - modification_timestamp_fetcher, request.cache_manager, - request.is_interactive, entry, - path_to_index) == CacheLoadResult::DoNotParse) { + request.cache_manager, request.is_interactive, entry, + path_to_index) == CacheLoadResult::DoNotParse) return; - } LOG_S(INFO) << "parse " << path_to_index; std::vector file_contents = PreloadFileContents( @@ -404,7 +359,6 @@ bool IndexMain_DoParse( WorkingFiles* working_files, FileConsumerSharedState* file_consumer_shared, TimestampManager* timestamp_manager, - IModificationTimestampFetcher* modification_timestamp_fetcher, IIndexer* indexer) { auto* queue = QueueManager::instance(); std::optional request = queue->index_request.TryPopFront(); @@ -414,8 +368,7 @@ bool IndexMain_DoParse( Project::Entry entry; entry.filename = request->path; entry.args = request->args; - ParseFile(diag_engine, working_files, file_consumer_shared, - timestamp_manager, modification_timestamp_fetcher, + ParseFile(diag_engine, working_files, file_consumer_shared, timestamp_manager, indexer, request.value(), entry); return true; } @@ -424,8 +377,7 @@ bool IndexMain_DoCreateIndexUpdate(TimestampManager* timestamp_manager) { auto* queue = QueueManager::instance(); bool did_work = false; - IterationLoop loop; - while (loop.Next()) { + for (int i = 100; i--; ) { std::optional response = queue->on_id_mapped.TryPopFront(); if (!response) return did_work; @@ -434,24 +386,24 @@ bool IndexMain_DoCreateIndexUpdate(TimestampManager* timestamp_manager) { Timer time; - // Build delta update. - IndexUpdate update = IndexUpdate::CreateDelta(response->previous.get(), - response->current.get()); - response->perf.index_make_delta = time.ElapsedMicrosecondsAndReset(); - LOG_S(INFO) << "built index for " << response->current->path - << " (is_delta=" << !!response->previous << ")"; - // Write current index to disk if requested. + std::string path = response->current->path; if (response->write_to_disk) { - LOG_S(INFO) << "store index for " << response->current->path; + LOG_S(INFO) << "store index for " << path; time.Reset(); response->cache_manager->WriteToCache(*response->current); response->perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset(); timestamp_manager->UpdateCachedModificationTime( - response->current->path, - response->current->last_modification_time); + path, response->current->last_modification_time); } + // Build delta update. + IndexUpdate update = IndexUpdate::CreateDelta(response->previous.get(), + response->current.get()); + response->perf.index_make_delta = time.ElapsedMicrosecondsAndReset(); + LOG_S(INFO) << "built index for " << path + << " (is_delta=" << !!response->previous << ")"; + Index_OnIndexed reply(std::move(update), response->perf); queue->on_indexed.PushBack(std::move(reply), response->is_interactive); } @@ -533,7 +485,6 @@ void Indexer_Main(DiagnosticsEngine* diag_engine, Project* project, WorkingFiles* working_files, MultiQueueWaiter* waiter) { - RealModificationTimestampFetcher modification_timestamp_fetcher; auto* queue = QueueManager::instance(); // Build one index per-indexer, as building the index acquires a global lock. auto indexer = std::make_unique(); @@ -555,7 +506,6 @@ void Indexer_Main(DiagnosticsEngine* diag_engine, // index. did_work = IndexMain_DoParse(diag_engine, working_files, file_consumer_shared, timestamp_manager, - &modification_timestamp_fetcher, indexer.get()) || did_work; @@ -614,8 +564,7 @@ bool QueryDb_ImportMain(QueryDatabase* db, bool did_work = false; - IterationLoop loop; - while (loop.Next()) { + for (int i = 80; i--; ) { std::optional response = queue->on_indexed.TryPopFront(); if (!response) break; @@ -626,153 +575,3 @@ bool QueryDb_ImportMain(QueryDatabase* db, return did_work; } - -TEST_SUITE("ImportPipeline") { - struct Fixture { - Fixture() { - g_config = std::make_unique(); - QueueManager::Init(&querydb_waiter, &indexer_waiter, &stdout_waiter); - - queue = QueueManager::instance(); - cache_manager = ICacheManager::MakeFake({}); - indexer = IIndexer::MakeTestIndexer({}); - diag_engine.Init(); - } - - bool PumpOnce() { - return IndexMain_DoParse(&diag_engine, &working_files, - &file_consumer_shared, ×tamp_manager, - &modification_timestamp_fetcher, indexer.get()); - } - - void MakeRequest(const std::string& path, - const std::vector& args = {}, - bool is_interactive = false, - const std::string& contents = "void foo();") { - queue->index_request.PushBack( - Index_Request(path, args, is_interactive, contents, cache_manager)); - } - - MultiQueueWaiter querydb_waiter; - MultiQueueWaiter indexer_waiter; - MultiQueueWaiter stdout_waiter; - - QueueManager* queue = nullptr; - DiagnosticsEngine diag_engine; - WorkingFiles working_files; - FileConsumerSharedState file_consumer_shared; - TimestampManager timestamp_manager; - FakeModificationTimestampFetcher modification_timestamp_fetcher; - std::shared_ptr cache_manager; - std::unique_ptr indexer; - }; - - TEST_CASE_FIXTURE(Fixture, "FileNeedsParse") { - auto check = [&](const std::string& file, bool is_dependency = false, - bool is_interactive = false, - const std::vector& old_args = {}, - const std::vector& new_args = {}) { - std::unique_ptr opt_previous_index; - if (!old_args.empty()) { - opt_previous_index = std::make_unique("---.cc", ""); - opt_previous_index->args = old_args; - } - std::optional from; - if (is_dependency) - from = std::string("---.cc"); - return FileNeedsParse(is_interactive /*is_interactive*/, - ×tamp_manager, &modification_timestamp_fetcher, - cache_manager, opt_previous_index.get(), file, - new_args, from); - }; - - // A file with no timestamp is not imported, since this implies the file no - // longer exists on disk. - modification_timestamp_fetcher.entries["bar.h"] = std::nullopt; - REQUIRE(check("bar.h", false /*is_dependency*/) == ShouldParse::NoSuchFile); - - // A dependency is only imported once. - modification_timestamp_fetcher.entries["foo.h"] = 5; - REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::Yes); - REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::No); - - // An interactive dependency is imported. - REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::No); - REQUIRE(check("foo.h", true /*is_dependency*/, true /*is_interactive*/) == - ShouldParse::Yes); - - // A file whose timestamp has not changed is not imported. When the - // timestamp changes (either forward or backward) it is reimported. - auto check_timestamp_change = [&](int64_t timestamp) { - modification_timestamp_fetcher.entries["aa.cc"] = timestamp; - REQUIRE(check("aa.cc") == ShouldParse::Yes); - REQUIRE(check("aa.cc") == ShouldParse::Yes); - REQUIRE(check("aa.cc") == ShouldParse::Yes); - timestamp_manager.UpdateCachedModificationTime("aa.cc", timestamp); - REQUIRE(check("aa.cc") == ShouldParse::No); - }; - check_timestamp_change(5); - check_timestamp_change(6); - check_timestamp_change(5); - check_timestamp_change(4); - - // Argument change implies reimport, even if timestamp has not changed. - timestamp_manager.UpdateCachedModificationTime("aa.cc", 5); - modification_timestamp_fetcher.entries["aa.cc"] = 5; - REQUIRE(check("aa.cc", false /*is_dependency*/, false /*is_interactive*/, - {"b"} /*old_args*/, - {"b", "a"} /*new_args*/) == ShouldParse::Yes); - } - - // FIXME: validate other state like timestamp_manager, etc. - // FIXME: add more interesting tests that are not the happy path - // FIXME: test - // - IndexMain_DoCreateIndexUpdate - // - IndexMain_LoadPreviousIndex - // - QueryDb_ImportMain - - TEST_CASE_FIXTURE(Fixture, "index request with zero results") { - indexer = IIndexer::MakeTestIndexer({IIndexer::TestEntry{"foo.cc", 0}}); - - MakeRequest("foo.cc"); - - REQUIRE(queue->index_request.Size() == 1); - REQUIRE(queue->on_id_mapped.Size() == 0); - PumpOnce(); - REQUIRE(queue->index_request.Size() == 0); - REQUIRE(queue->on_id_mapped.Size() == 0); - - REQUIRE(file_consumer_shared.used_files.empty()); - } - - TEST_CASE_FIXTURE(Fixture, "one index request") { - indexer = IIndexer::MakeTestIndexer({IIndexer::TestEntry{"foo.cc", 100}}); - - MakeRequest("foo.cc"); - - REQUIRE(queue->index_request.Size() == 1); - REQUIRE(queue->on_id_mapped.Size() == 0); - PumpOnce(); - REQUIRE(queue->index_request.Size() == 0); - REQUIRE(queue->on_id_mapped.Size() == 100); - - REQUIRE(file_consumer_shared.used_files.empty()); - } - - TEST_CASE_FIXTURE(Fixture, "multiple index requests") { - indexer = IIndexer::MakeTestIndexer( - {IIndexer::TestEntry{"foo.cc", 100}, IIndexer::TestEntry{"bar.cc", 5}}); - - MakeRequest("foo.cc"); - MakeRequest("bar.cc"); - - REQUIRE(queue->index_request.Size() == 2); - //REQUIRE(queue->do_id_map.Size() == 0); - while (PumpOnce()) { - } - REQUIRE(queue->index_request.Size() == 0); - //REQUIRE(queue->do_id_map.Size() == 105); - - REQUIRE(file_consumer_shared.used_files.empty()); - } -} diff --git a/src/indexer.h b/src/indexer.h index c675c749..d332e1fa 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -105,7 +105,6 @@ struct FuncDef : NameMixin { // Functions that this function calls. std::vector callees; - int file_id; // Type which declares this one (ie, it is a method) Usr declaring_type = 0; int16_t qual_name_offset = 0; @@ -133,7 +132,6 @@ MAKE_REFLECT_STRUCT(FuncDef, comments, spell, extent, - file_id, declaring_type, bases, vars, @@ -177,7 +175,6 @@ struct TypeDef : NameMixin { // If set, then this is the same underlying type as the given value (ie, this // type comes from a using or typedef statement). Usr alias_of = 0; - int file_id; int16_t qual_name_offset = 0; int16_t short_name_offset = 0; @@ -201,7 +198,6 @@ MAKE_REFLECT_STRUCT(TypeDef, comments, spell, extent, - file_id, alias_of, bases, types, @@ -228,7 +224,6 @@ struct VarDef : NameMixin { Maybe spell; Maybe extent; - int file_id; // Type of the variable. Usr type = 0; @@ -259,7 +254,6 @@ MAKE_REFLECT_STRUCT(VarDef, comments, spell, extent, - file_id, type, kind, storage); diff --git a/src/language.cc b/src/language.cc new file mode 100644 index 00000000..ee749fcf --- /dev/null +++ b/src/language.cc @@ -0,0 +1,30 @@ +#include "language.h" + +#include "utils.h" + +LanguageId SourceFileLanguage(std::string_view path) { + if (EndsWith(path, ".c")) + return LanguageId::C; + else if (EndsWith(path, ".cpp") || EndsWith(path, ".cc")) + return LanguageId::Cpp; + else if (EndsWith(path, ".mm")) + return LanguageId::ObjCpp; + else if (EndsWith(path, ".m")) + return LanguageId::ObjC; + return LanguageId::Unknown; +} + +const char* LanguageIdentifier(LanguageId lang) { + switch (lang) { + case LanguageId::C: + return "c"; + case LanguageId::Cpp: + return "cpp"; + case LanguageId::ObjC: + return "objective-c"; + case LanguageId::ObjCpp: + return "objective-cpp"; + default: + return ""; + } +} diff --git a/src/language.h b/src/language.h index 393423cf..c2bc335c 100644 --- a/src/language.h +++ b/src/language.h @@ -2,8 +2,13 @@ #include "serializer.h" +#include + // Used to identify the language at a file level. The ordering is important, as // a file previously identified as `C`, will be changed to `Cpp` if it // encounters a c++ declaration. enum class LanguageId { Unknown = 0, C = 1, Cpp = 2, ObjC = 3, ObjCpp = 4 }; MAKE_REFLECT_TYPE_PROXY(LanguageId); + +LanguageId SourceFileLanguage(std::string_view path); +const char* LanguageIdentifier(LanguageId lang); diff --git a/src/messages/ccls_call_hierarchy.cc b/src/messages/ccls_call_hierarchy.cc index 410cfdcf..3b3794c1 100644 --- a/src/messages/ccls_call_hierarchy.cc +++ b/src/messages/ccls_call_hierarchy.cc @@ -108,7 +108,8 @@ bool Expand(MessageHandler* m, if (const auto* def = func.AnyDef()) for (SymbolRef ref : def->callees) if (ref.kind == SymbolKind::Func) - handle(Use{{ref.range, ref.usr, ref.kind, ref.role}, def->file_id}, + handle(Use{{ref.range, ref.usr, ref.kind, ref.role}, + def->spell->file_id}, call_type); } else { for (Use use : func.uses) diff --git a/src/messages/text_document_did_open.cc b/src/messages/text_document_did_open.cc index 06c83fa2..20d8e256 100644 --- a/src/messages/text_document_did_open.cc +++ b/src/messages/text_document_did_open.cc @@ -61,19 +61,21 @@ struct Handler_TextDocumentDidOpen include_complete->AddFile(working_file->filename); clang_complete->NotifyView(path); + if (params.args.size()) + project->SetFlagsForFile(params.args, path); - // Submit new index request. - Project::Entry entry = project->FindCompilationEntryForFile(path); - QueueManager::instance()->index_request.PushBack( + // Submit new index request if it is not a header file. + if (SourceFileLanguage(path) != LanguageId::Unknown) { + Project::Entry entry = project->FindCompilationEntryForFile(path); + QueueManager::instance()->index_request.PushBack( Index_Request( - entry.filename, params.args.size() ? params.args : entry.args, - true /*is_interactive*/, params.textDocument.text, cache_manager), + entry.filename, params.args.size() ? params.args : entry.args, + true /*is_interactive*/, params.textDocument.text, cache_manager), true /* priority */); - clang_complete->FlushSession(entry.filename); - LOG_S(INFO) << "Flushed clang complete sessions for " << entry.filename; - if (params.args.size()) - project->SetFlagsForFile(params.args, path); + clang_complete->FlushSession(entry.filename); + LOG_S(INFO) << "Flushed clang complete sessions for " << entry.filename; + } } }; REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen); diff --git a/src/messages/text_document_hover.cc b/src/messages/text_document_hover.cc index 7ea1aeee..455c61c5 100644 --- a/src/messages/text_document_hover.cc +++ b/src/messages/text_document_hover.cc @@ -21,14 +21,13 @@ std::optional GetComments(QueryDatabase* db, SymbolRef sym) { // Returns the hover or detailed name for `sym`, if any. std::optional GetHoverOrName(QueryDatabase* db, - const std::string& language, - SymbolRef sym) { - + LanguageId lang, + SymbolRef sym) { std::optional ret; WithEntity(db, sym, [&](const auto& entity) { if (const auto* def = entity.AnyDef()) { lsMarkedString m; - m.language = language; + m.language = LanguageIdentifier(lang); if (!def->hover.empty()) { m.value = def->hover; ret = m; diff --git a/src/position.h b/src/position.h index 748bb741..99ec7762 100644 --- a/src/position.h +++ b/src/position.h @@ -48,6 +48,21 @@ struct Range { } }; +namespace std { +template <> +struct hash { + std::size_t operator()(Range x) const { + union U { + Range range = {}; + uint64_t u64; + } u; + static_assert(sizeof(Range) == 8); + u.range = x; + return hash()(u.u64); + } +}; +} + // Reflection class Reader; class Writer; diff --git a/src/project.cc b/src/project.cc index 4ced67fb..d45cd063 100644 --- a/src/project.cc +++ b/src/project.cc @@ -91,18 +91,6 @@ bool ShouldAddToAngleIncludes(const std::string& arg) { return StartsWithAny(arg, kAngleIncludeArgs); } -LanguageId SourceFileLanguage(const std::string& path) { - if (EndsWith(path, ".c")) - return LanguageId::C; - else if (EndsWith(path, ".cpp") || EndsWith(path, ".cc")) - return LanguageId::Cpp; - else if (EndsWith(path, ".mm")) - return LanguageId::ObjCpp; - else if (EndsWith(path, ".m")) - return LanguageId::ObjC; - return LanguageId::Unknown; -} - Project::Entry GetCompilationEntryFromCompileCommandEntry( ProjectConfig* config, const CompileCommandsEntry& entry) { diff --git a/src/query.cc b/src/query.cc index 87fc67eb..761ca5e1 100644 --- a/src/query.cc +++ b/src/query.cc @@ -17,8 +17,7 @@ #include // Used by |HANDLE_MERGEABLE| so only |range| is needed. -MAKE_HASHABLE(Range, t.start, t.end); -MAKE_HASHABLE(Use, t.range); +MAKE_HASHABLE(Use, t.range, t.file_id); namespace { @@ -48,7 +47,6 @@ void AssignFileId(int file_id, std::vector& xs) { AssignFileId(file_id, x); } - void AddRange(int file_id, std::vector& into, const std::vector& from) { into.reserve(into.size() + from.size()); for (Use use : from) { @@ -62,39 +60,24 @@ void AddRange(int _, std::vector& into, const std::vector& from) { } template -void RemoveRange(std::vector& dest, const std::vector& to_remove) { +void RemoveRange(std::vector& from, const std::vector& to_remove) { if (to_remove.size()) { std::unordered_set to_remove_set(to_remove.begin(), to_remove.end()); - dest.erase( - std::remove_if(dest.begin(), dest.end(), + from.erase( + std::remove_if(from.begin(), from.end(), [&](const T& t) { return to_remove_set.count(t) > 0; }), - dest.end()); + from.end()); } } QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) { QueryFile::Def def; - def.path = indexed.path; - def.args = indexed.args; - def.includes = indexed.includes; - def.inactive_regions = indexed.skipped_by_preprocessor; - def.dependencies = indexed.dependencies; - - // Convert enum to markdown compatible strings - def.language = [&indexed]() { - switch (indexed.language) { - case LanguageId::C: - return "c"; - case LanguageId::Cpp: - return "cpp"; - case LanguageId::ObjC: - return "objective-c"; - case LanguageId::ObjCpp: - return "objective-cpp"; - default: - return ""; - } - }(); + def.path = std::move(indexed.path); + def.args = std::move(indexed.args); + def.includes = std::move(indexed.includes); + def.inactive_regions = std::move(indexed.skipped_by_preprocessor); + def.dependencies = std::move(indexed.dependencies); + def.language = indexed.language; auto add_all_symbols = [&](Use use, Usr usr, SymbolKind kind) { def.all_symbols.push_back(SymbolRef(use.range, usr, kind, use.role)); @@ -172,10 +155,8 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) { template bool TryReplaceDef(std::forward_list& def_list, Q&& def) { for (auto& def1 : def_list) - if (def1.file_id == def.file_id) { - if (!def1.spell || def.spell) { - def1 = std::move(def); - } + if (def1.spell->file_id == def.spell->file_id) { + def1 = std::move(def); return true; } return false; @@ -195,57 +176,57 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile* previous, if (!previous) previous = ∅ - r.files_def_update = BuildFileDefUpdate(*current); + r.files_def_update = BuildFileDefUpdate(std::move(*current)); for (auto& it : previous->usr2func) { auto& func = it.second; if (func.def.spell) r.funcs_removed.push_back(func.usr); - r.funcs_declarations[func.usr].first = func.declarations; - r.funcs_uses[func.usr].first = func.uses; - r.funcs_derived[func.usr].first = func.derived; + r.funcs_declarations[func.usr].first = std::move(func.declarations); + r.funcs_uses[func.usr].first = std::move(func.uses); + r.funcs_derived[func.usr].first = std::move(func.derived); } for (auto& it : current->usr2func) { auto& func = it.second; - if (func.def.detailed_name.size()) + if (func.def.spell && func.def.detailed_name.size()) r.funcs_def_update.emplace_back(it.first, func.def); - r.funcs_declarations[func.usr].second = func.declarations; - r.funcs_uses[func.usr].second = func.uses; - r.funcs_derived[func.usr].second = func.derived; + r.funcs_declarations[func.usr].second = std::move(func.declarations); + r.funcs_uses[func.usr].second = std::move(func.uses); + r.funcs_derived[func.usr].second = std::move(func.derived); } for (auto& it : previous->usr2type) { auto& type = it.second; if (type.def.spell) r.types_removed.push_back(type.usr); - r.types_declarations[type.usr].first = type.declarations; - r.types_uses[type.usr].first = type.uses; - r.types_derived[type.usr].first = type.derived; - r.types_instances[type.usr].first = type.instances; + r.types_declarations[type.usr].first = std::move(type.declarations); + r.types_uses[type.usr].first = std::move(type.uses); + r.types_derived[type.usr].first = std::move(type.derived); + r.types_instances[type.usr].first = std::move(type.instances); }; for (auto& it : current->usr2type) { auto& type = it.second; - if (type.def.detailed_name.size()) + if (type.def.spell && type.def.detailed_name.size()) r.types_def_update.emplace_back(it.first, type.def); - r.types_declarations[type.usr].second = type.declarations; - r.types_uses[type.usr].second = type.uses; - r.types_derived[type.usr].second = type.derived; - r.types_instances[type.usr].second = type.instances; + r.types_declarations[type.usr].second = std::move(type.declarations); + r.types_uses[type.usr].second = std::move(type.uses); + r.types_derived[type.usr].second = std::move(type.derived); + r.types_instances[type.usr].second = std::move(type.instances); }; for (auto& it : previous->usr2var) { auto& var = it.second; if (var.def.spell) r.vars_removed.push_back(var.usr); - r.vars_declarations[var.usr].first = var.declarations; - r.vars_uses[var.usr].first = var.uses; + r.vars_declarations[var.usr].first = std::move(var.declarations); + r.vars_uses[var.usr].first = std::move(var.uses); } for (auto& it : current->usr2var) { auto& var = it.second; - if (var.def.detailed_name.size()) + if (var.def.spell && var.def.detailed_name.size()) r.vars_def_update.emplace_back(it.first, var.def); - r.vars_declarations[var.usr].second = var.declarations; - r.vars_uses[var.usr].second = var.uses; + r.vars_declarations[var.usr].second = std::move(var.declarations); + r.vars_uses[var.usr].second = std::move(var.uses); } return r; @@ -281,7 +262,7 @@ void QueryDatabase::RemoveUsrs( for (auto usr : to_remove) { QueryFunc& func = Func(usr); func.def.remove_if([=](const QueryFunc::Def& def) { - return def.file_id == file_id; + return def.spell->file_id == file_id; }); } break; @@ -290,7 +271,7 @@ void QueryDatabase::RemoveUsrs( for (auto usr : to_remove) { QueryVar& var = Var(usr); var.def.remove_if([=](const QueryVar::Def& def) { - return def.file_id == file_id; + return def.spell->file_id == file_id; }); } break; @@ -312,6 +293,7 @@ void QueryDatabase::ApplyIndexUpdate(IndexUpdate* u) { #define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \ for (auto& it : u->update_var_name) { \ auto& entity = storage_name[it.first]; \ + AssignFileId(u->file_id, it.second.first); \ RemoveRange(entity.def_var_name, it.second.first); \ AssignFileId(u->file_id, it.second.second); \ AddRange(u->file_id, entity.def_var_name, it.second.second); \ diff --git a/src/query.h b/src/query.h index 6cbd9074..51d96ef3 100644 --- a/src/query.h +++ b/src/query.h @@ -26,8 +26,7 @@ struct QueryFile { struct Def { std::string path; std::vector args; - // Language identifier - std::string language; + LanguageId language; // Includes in the file. std::vector includes; // Outline of the file (ie, for code lens).