mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-27 10:02:03 +00:00
textDocument/references: include base references by default
This commit is contained in:
parent
ba45e7ca63
commit
4e2f64893c
15
README.md
15
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) <<<
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 &&
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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,
|
||||
|
@ -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_;
|
||||
|
Loading…
Reference in New Issue
Block a user