From 4e2f64893cb95eaa48a128a0e1dec20c31d3c261 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 16 May 2018 23:49:21 -0700 Subject: [PATCH] textDocument/references: include base references by default --- README.md | 15 +++++- src/filesystem.cc | 31 ++++++------- src/indexer.h | 3 ++ src/messages/text_document_references.cc | 58 ++++++++++++++++++------ src/project.cc | 11 +++-- src/query_utils.h | 24 ---------- src/threaded_queue.h | 40 +++------------- 7 files changed, 90 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 35553bfc..0da7b661 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,20 @@ a C/C++/Objective-C language server. * preprocessor skipped regions * semantic highlighting, including support for [rainbow semantic highlighting](https://medium.com/@evnbr/coding-in-color-3a6db2743a1e) -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. +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](../../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. + +The comparison with cquery as noted on 2018-05-17: + +| | cquery | ccls | +|------------ |--------------------------------|---------------------------| +| third_party | more | fewer | +| C++ | C++14 | C++17 | +| clang API | libclang (C) | libclang + clang/llvm C++ | +| Filesystem | AbsolutePath + custom routines | llvm/Support | +| index | | slight enhancement | +| pipeline | index merge+id remapping | simpler and more robust | + +cquery has system include path detection (through running the compiler driver) while ccls does not. # >>> [Getting started](../../wiki/Getting-started) (CLICK HERE) <<< diff --git a/src/filesystem.cc b/src/filesystem.cc index 1b343b14..b3c31fe6 100644 --- a/src/filesystem.cc +++ b/src/filesystem.cc @@ -3,32 +3,31 @@ using namespace llvm; #include "utils.h" -#include +#include void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix, const std::function& handler) { EnsureEndsInSlash(folder); - std::error_code ec; - if (recursive) - for (sys::fs::recursive_directory_iterator I(folder, ec), E; I != E && !ec; - I.increment(ec)) { - std::string path = I->path(), filename = sys::path::filename(path); - if (filename[0] != '.' || filename == ".ccls") { - if (!dir_prefix) - path = path.substr(folder.size()); - handler(path); - } - } - else + std::vector st{folder}; + while (st.size()) { + std::error_code ec; + folder = st.back(); + st.pop_back(); for (sys::fs::directory_iterator I(folder, ec), E; I != E && !ec; I.increment(ec)) { + auto Status = I->status(); + if (!Status) continue; std::string path = I->path(), filename = sys::path::filename(path); if (filename[0] != '.' || filename == ".ccls") { - if (!dir_prefix) - path = path.substr(folder.size()); - handler(path); + if (sys::fs::is_regular_file(*Status)) { + if (!dir_prefix) + path = path.substr(folder.size()); + handler(path); + } else if (recursive && sys::fs::is_directory(*Status)) + st.push_back(path); } } + } } diff --git a/src/indexer.h b/src/indexer.h index 55dc00e8..2c35784e 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -114,6 +114,7 @@ struct FuncDef : NameMixin { lsSymbolKind kind = lsSymbolKind::Unknown; StorageClass storage = StorageClass::Invalid; + std::vector GetBases() const { return bases; } bool operator==(const FuncDef& o) const { return detailed_name == o.detailed_name && spell == o.spell && extent == o.extent && declaring_type == o.declaring_type && @@ -182,6 +183,7 @@ struct TypeDef : NameMixin { int16_t short_name_size = 0; lsSymbolKind kind = lsSymbolKind::Unknown; + std::vector GetBases() const { return bases; } bool operator==(const TypeDef& o) const { return detailed_name == o.detailed_name && spell == o.spell && extent == o.extent && alias_of == o.alias_of && bases == o.bases && @@ -240,6 +242,7 @@ struct VarDef : NameMixin { bool is_local() const { return kind == lsSymbolKind::Variable; } + std::vector GetBases() const { return {}; } bool operator==(const VarDef& o) const { return detailed_name == o.detailed_name && spell == o.spell && extent == o.extent && type == o.type && kind == o.kind && diff --git a/src/messages/text_document_references.cc b/src/messages/text_document_references.cc index 53232609..dce6e5b7 100644 --- a/src/messages/text_document_references.cc +++ b/src/messages/text_document_references.cc @@ -2,7 +2,7 @@ #include "query_utils.h" #include "queue_manager.h" -#include +#include namespace { MethodType kMethodType = "textDocument/references"; @@ -10,8 +10,9 @@ MethodType kMethodType = "textDocument/references"; struct In_TextDocumentReferences : public RequestInMessage { MethodType GetMethodType() const override { return kMethodType; } struct lsReferenceContext { + bool base = true; // Include the declaration of the current symbol. - bool includeDeclaration; + bool includeDeclaration = false; // Include references with these |Role| bits set. Role role = Role::All; }; @@ -24,6 +25,7 @@ struct In_TextDocumentReferences : public RequestInMessage { Params params; }; MAKE_REFLECT_STRUCT(In_TextDocumentReferences::lsReferenceContext, + base, includeDeclaration, role); MAKE_REFLECT_STRUCT(In_TextDocumentReferences::Params, @@ -60,17 +62,47 @@ struct Handler_TextDocumentReferences for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) { // Found symbol. Return references. - EachOccurrenceWithParent( - db, sym, params.context.includeDeclaration, - [&](Use use, lsSymbolKind parent_kind) { - if (use.role & params.context.role) - if (std::optional ls_loc = - GetLsLocationEx(db, working_files, use, container)) { - if (container) - ls_loc->parentKind = parent_kind; - out.result.push_back(*ls_loc); - } - }); + std::unordered_set seen; + seen.insert(sym.usr); + std::vector stack{sym.usr}; + if (sym.kind != SymbolKind::Func) + params.context.base = false; + while (stack.size()) { + sym.usr = stack.back(); + stack.pop_back(); + auto fn = [&](Use use, lsSymbolKind parent_kind) { + if (use.role & params.context.role) + if (std::optional ls_loc = + GetLsLocationEx(db, working_files, use, container)) { + if (container) + ls_loc->parentKind = parent_kind; + out.result.push_back(*ls_loc); + } + }; + WithEntity(db, sym, [&](const auto& entity) { + lsSymbolKind parent_kind = lsSymbolKind::Unknown; + for (auto& def : entity.def) + if (def.spell) { + parent_kind = GetSymbolKind(db, sym); + if (params.context.base) + for (Usr usr : def.GetBases()) + if (!seen.count(usr)) { + seen.insert(usr); + stack.push_back(usr); + } + break; + } + for (Use use : entity.uses) + fn(use, parent_kind); + if (params.context.includeDeclaration) { + for (auto& def : entity.def) + if (def.spell) + fn(*def.spell, parent_kind); + for (Use use : entity.declarations) + fn(use, parent_kind); + } + }); + } break; } diff --git a/src/project.cc b/src/project.cc index 063c9e5a..4800033b 100644 --- a/src/project.cc +++ b/src/project.cc @@ -95,6 +95,8 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry( } if (args.empty()) return result; + args.insert(args.end(), config->extra_flags.begin(), + config->extra_flags.end()); std::unique_ptr Opts = driver::createDriverOptTable(); unsigned MissingArgIndex, MissingArgCount; @@ -111,6 +113,11 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry( config->angle_dirs.insert(entry.ResolveIfRelative(A->getValue())); for (const auto* A : Args.filtered(OPT_I, OPT_iquote)) config->quote_dirs.insert(entry.ResolveIfRelative(A->getValue())); + for (const auto* A : Args.filtered(OPT_idirafter)) { + std::string dir = entry.ResolveIfRelative(A->getValue()); + config->angle_dirs.insert(dir); + config->quote_dirs.insert(dir); + } for (size_t i = 1; i < args.size(); i++) // This is most likely the file path we will be passing to clang. The @@ -122,10 +129,6 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry( continue; } - // We don't do any special processing on user-given extra flags. - for (const auto& flag : config->extra_flags) - args.push_back(flag); - if (!Args.hasArg(OPT_resource_dir)) args.push_back("-resource-dir=" + g_config->clang.resourceDir); if (!Args.hasArg(OPT_working_directory)) diff --git a/src/query_utils.h b/src/query_utils.h index e76d3212..1e65b2a3 100644 --- a/src/query_utils.h +++ b/src/query_utils.h @@ -110,30 +110,6 @@ void EachOccurrence(QueryDatabase* db, lsSymbolKind GetSymbolKind(QueryDatabase* db, SymbolIdx sym); -template -void EachOccurrenceWithParent(QueryDatabase* db, - SymbolIdx sym, - bool include_decl, - Fn&& fn) { - WithEntity(db, sym, [&](const auto& entity) { - lsSymbolKind parent_kind = lsSymbolKind::Unknown; - for (auto& def : entity.def) - if (def.spell) { - parent_kind = GetSymbolKind(db, sym); - break; - } - for (Use use : entity.uses) - fn(use, parent_kind); - if (include_decl) { - for (auto& def : entity.def) - if (def.spell) - fn(*def.spell, parent_kind); - for (Use use : entity.declarations) - fn(use, parent_kind); - } - }); -} - template void EachDefinedEntity(std::unordered_map& collection, const std::vector& usrs, diff --git a/src/threaded_queue.h b/src/threaded_queue.h index 70e50252..93f1ccf1 100644 --- a/src/threaded_queue.h +++ b/src/threaded_queue.h @@ -69,13 +69,11 @@ struct MultiQueueWaiter { template struct ThreadedQueue : public BaseThreadQueue { public: - ThreadedQueue() : total_count_(0) { + ThreadedQueue() { owned_waiter_ = std::make_unique(); waiter_ = owned_waiter_.get(); } - - explicit ThreadedQueue(MultiQueueWaiter* waiter) - : total_count_(0), waiter_(waiter) {} + explicit ThreadedQueue(MultiQueueWaiter* waiter) : waiter_(waiter) {} // Returns the number of elements in the queue. This is lock-free. size_t Size() const { return total_count_; } @@ -92,10 +90,6 @@ struct ThreadedQueue : public BaseThreadQueue { waiter_->cv.notify_one(); } - void PushFront(T&& t, bool priority = false) { - Push<&std::deque::push_front>(std::move(t), priority); - } - void PushBack(T&& t, bool priority = false) { Push<&std::deque::push_back>(std::move(t), priority); } @@ -162,7 +156,7 @@ struct ThreadedQueue : public BaseThreadQueue { // Get the first element from the queue without blocking. Returns a null // value if the queue is empty. - std::optional TryPopFrontHelper(int which) { + std::optional TryPopFront() { std::lock_guard lock(mutex_); auto execute = [&](std::deque* q) { auto val = std::move(q->front()); @@ -170,35 +164,13 @@ struct ThreadedQueue : public BaseThreadQueue { --total_count_; return std::move(val); }; - if (which & 2 && priority_.size()) - return execute(&priority_); - if (which & 1 && queue_.size()) - return execute(&queue_); - return std::nullopt; - } - - std::optional TryPopFront() { return TryPopFrontHelper(3); } - - std::optional TryPopBack() { - std::lock_guard lock(mutex_); - auto execute = [&](std::deque* q) { - auto val = std::move(q->back()); - q->pop_back(); - --total_count_; - return std::move(val); - }; - // Reversed - if (queue_.size()) - return execute(&queue_); if (priority_.size()) return execute(&priority_); + if (queue_.size()) + return execute(&queue_); return std::nullopt; } - std::optional TryPopFrontLow() { return TryPopFrontHelper(1); } - - std::optional TryPopFrontHigh() { return TryPopFrontHelper(2); } - template void Iterate(Fn fn) { std::lock_guard lock(mutex_); @@ -211,7 +183,7 @@ struct ThreadedQueue : public BaseThreadQueue { mutable std::mutex mutex_; private: - std::atomic total_count_; + std::atomic total_count_{0}; std::deque priority_; std::deque queue_; MultiQueueWaiter* waiter_;