textDocument/references: include base references by default

This commit is contained in:
Fangrui Song 2018-05-16 23:49:21 -07:00
parent ba45e7ca63
commit 4e2f64893c
7 changed files with 90 additions and 92 deletions

View File

@ -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) <<<

View File

@ -3,32 +3,31 @@ using namespace llvm;
#include "utils.h"
#include <utility>
#include <vector>
void GetFilesInFolder(std::string folder,
bool recursive,
bool dir_prefix,
const std::function<void(const std::string&)>& 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<std::string> 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);
}
}
}
}

View File

@ -114,6 +114,7 @@ struct FuncDef : NameMixin<FuncDef> {
lsSymbolKind kind = lsSymbolKind::Unknown;
StorageClass storage = StorageClass::Invalid;
std::vector<Usr> 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<TypeDef> {
int16_t short_name_size = 0;
lsSymbolKind kind = lsSymbolKind::Unknown;
std::vector<Usr> 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<VarDef> {
bool is_local() const { return kind == lsSymbolKind::Variable; }
std::vector<Usr> 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 &&

View File

@ -2,7 +2,7 @@
#include "query_utils.h"
#include "queue_manager.h"
#include <loguru.hpp>
#include <unordered_set>
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<lsLocationEx> ls_loc =
GetLsLocationEx(db, working_files, use, container)) {
if (container)
ls_loc->parentKind = parent_kind;
out.result.push_back(*ls_loc);
}
});
std::unordered_set<Usr> seen;
seen.insert(sym.usr);
std::vector<Usr> 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<lsLocationEx> 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;
}

View File

@ -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<OptTable> 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))

View File

@ -110,30 +110,6 @@ void EachOccurrence(QueryDatabase* db,
lsSymbolKind GetSymbolKind(QueryDatabase* db, SymbolIdx sym);
template <typename Fn>
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 <typename Q, typename Fn>
void EachDefinedEntity(std::unordered_map<Usr, Q>& collection,
const std::vector<Usr>& usrs,

View File

@ -69,13 +69,11 @@ struct MultiQueueWaiter {
template <class T>
struct ThreadedQueue : public BaseThreadQueue {
public:
ThreadedQueue() : total_count_(0) {
ThreadedQueue() {
owned_waiter_ = std::make_unique<MultiQueueWaiter>();
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<T>::push_front>(std::move(t), priority);
}
void PushBack(T&& t, bool priority = false) {
Push<&std::deque<T>::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<T> TryPopFrontHelper(int which) {
std::optional<T> TryPopFront() {
std::lock_guard<std::mutex> lock(mutex_);
auto execute = [&](std::deque<T>* 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<T> TryPopFront() { return TryPopFrontHelper(3); }
std::optional<T> TryPopBack() {
std::lock_guard<std::mutex> lock(mutex_);
auto execute = [&](std::deque<T>* 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<T> TryPopFrontLow() { return TryPopFrontHelper(1); }
std::optional<T> TryPopFrontHigh() { return TryPopFrontHelper(2); }
template <typename Fn>
void Iterate(Fn fn) {
std::lock_guard<std::mutex> lock(mutex_);
@ -211,7 +183,7 @@ struct ThreadedQueue : public BaseThreadQueue {
mutable std::mutex mutex_;
private:
std::atomic<int> total_count_;
std::atomic<int> total_count_{0};
std::deque<T> priority_;
std::deque<T> queue_;
MultiQueueWaiter* waiter_;