mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-21 23:25:07 +00:00
textDocument/references: include base references by default
This commit is contained in:
parent
c279090ac5
commit
6d96d9dce2
15
README.md
15
README.md
@ -14,7 +14,20 @@ a C/C++/Objective-C language server.
|
|||||||
* preprocessor skipped regions
|
* preprocessor skipped regions
|
||||||
* semantic highlighting, including support for [rainbow semantic highlighting](https://medium.com/@evnbr/coding-in-color-3a6db2743a1e)
|
* 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) <<<
|
# >>> [Getting started](../../wiki/Getting-started) (CLICK HERE) <<<
|
||||||
|
|
||||||
|
@ -3,32 +3,31 @@ using namespace llvm;
|
|||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <utility>
|
#include <vector>
|
||||||
|
|
||||||
void GetFilesInFolder(std::string folder,
|
void GetFilesInFolder(std::string folder,
|
||||||
bool recursive,
|
bool recursive,
|
||||||
bool dir_prefix,
|
bool dir_prefix,
|
||||||
const std::function<void(const std::string&)>& handler) {
|
const std::function<void(const std::string&)>& handler) {
|
||||||
EnsureEndsInSlash(folder);
|
EnsureEndsInSlash(folder);
|
||||||
std::error_code ec;
|
std::vector<std::string> st{folder};
|
||||||
if (recursive)
|
while (st.size()) {
|
||||||
for (sys::fs::recursive_directory_iterator I(folder, ec), E; I != E && !ec;
|
std::error_code ec;
|
||||||
I.increment(ec)) {
|
folder = st.back();
|
||||||
std::string path = I->path(), filename = sys::path::filename(path);
|
st.pop_back();
|
||||||
if (filename[0] != '.' || filename == ".ccls") {
|
|
||||||
if (!dir_prefix)
|
|
||||||
path = path.substr(folder.size());
|
|
||||||
handler(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
for (sys::fs::directory_iterator I(folder, ec), E; I != E && !ec;
|
for (sys::fs::directory_iterator I(folder, ec), E; I != E && !ec;
|
||||||
I.increment(ec)) {
|
I.increment(ec)) {
|
||||||
|
auto Status = I->status();
|
||||||
|
if (!Status) continue;
|
||||||
std::string path = I->path(), filename = sys::path::filename(path);
|
std::string path = I->path(), filename = sys::path::filename(path);
|
||||||
if (filename[0] != '.' || filename == ".ccls") {
|
if (filename[0] != '.' || filename == ".ccls") {
|
||||||
if (!dir_prefix)
|
if (sys::fs::is_regular_file(*Status)) {
|
||||||
path = path.substr(folder.size());
|
if (!dir_prefix)
|
||||||
handler(path);
|
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;
|
lsSymbolKind kind = lsSymbolKind::Unknown;
|
||||||
StorageClass storage = StorageClass::Invalid;
|
StorageClass storage = StorageClass::Invalid;
|
||||||
|
|
||||||
|
std::vector<Usr> GetBases() const { return bases; }
|
||||||
bool operator==(const FuncDef& o) const {
|
bool operator==(const FuncDef& o) const {
|
||||||
return detailed_name == o.detailed_name && spell == o.spell &&
|
return detailed_name == o.detailed_name && spell == o.spell &&
|
||||||
extent == o.extent && declaring_type == o.declaring_type &&
|
extent == o.extent && declaring_type == o.declaring_type &&
|
||||||
@ -182,6 +183,7 @@ struct TypeDef : NameMixin<TypeDef> {
|
|||||||
int16_t short_name_size = 0;
|
int16_t short_name_size = 0;
|
||||||
lsSymbolKind kind = lsSymbolKind::Unknown;
|
lsSymbolKind kind = lsSymbolKind::Unknown;
|
||||||
|
|
||||||
|
std::vector<Usr> GetBases() const { return bases; }
|
||||||
bool operator==(const TypeDef& o) const {
|
bool operator==(const TypeDef& o) const {
|
||||||
return detailed_name == o.detailed_name && spell == o.spell &&
|
return detailed_name == o.detailed_name && spell == o.spell &&
|
||||||
extent == o.extent && alias_of == o.alias_of && bases == o.bases &&
|
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; }
|
bool is_local() const { return kind == lsSymbolKind::Variable; }
|
||||||
|
|
||||||
|
std::vector<Usr> GetBases() const { return {}; }
|
||||||
bool operator==(const VarDef& o) const {
|
bool operator==(const VarDef& o) const {
|
||||||
return detailed_name == o.detailed_name && spell == o.spell &&
|
return detailed_name == o.detailed_name && spell == o.spell &&
|
||||||
extent == o.extent && type == o.type && kind == o.kind &&
|
extent == o.extent && type == o.type && kind == o.kind &&
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "query_utils.h"
|
#include "query_utils.h"
|
||||||
#include "queue_manager.h"
|
#include "queue_manager.h"
|
||||||
|
|
||||||
#include <loguru.hpp>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
MethodType kMethodType = "textDocument/references";
|
MethodType kMethodType = "textDocument/references";
|
||||||
@ -10,8 +10,9 @@ MethodType kMethodType = "textDocument/references";
|
|||||||
struct In_TextDocumentReferences : public RequestInMessage {
|
struct In_TextDocumentReferences : public RequestInMessage {
|
||||||
MethodType GetMethodType() const override { return kMethodType; }
|
MethodType GetMethodType() const override { return kMethodType; }
|
||||||
struct lsReferenceContext {
|
struct lsReferenceContext {
|
||||||
|
bool base = true;
|
||||||
// Include the declaration of the current symbol.
|
// Include the declaration of the current symbol.
|
||||||
bool includeDeclaration;
|
bool includeDeclaration = false;
|
||||||
// Include references with these |Role| bits set.
|
// Include references with these |Role| bits set.
|
||||||
Role role = Role::All;
|
Role role = Role::All;
|
||||||
};
|
};
|
||||||
@ -24,6 +25,7 @@ struct In_TextDocumentReferences : public RequestInMessage {
|
|||||||
Params params;
|
Params params;
|
||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(In_TextDocumentReferences::lsReferenceContext,
|
MAKE_REFLECT_STRUCT(In_TextDocumentReferences::lsReferenceContext,
|
||||||
|
base,
|
||||||
includeDeclaration,
|
includeDeclaration,
|
||||||
role);
|
role);
|
||||||
MAKE_REFLECT_STRUCT(In_TextDocumentReferences::Params,
|
MAKE_REFLECT_STRUCT(In_TextDocumentReferences::Params,
|
||||||
@ -60,17 +62,47 @@ struct Handler_TextDocumentReferences
|
|||||||
|
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) {
|
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) {
|
||||||
// Found symbol. Return references.
|
// Found symbol. Return references.
|
||||||
EachOccurrenceWithParent(
|
std::unordered_set<Usr> seen;
|
||||||
db, sym, params.context.includeDeclaration,
|
seen.insert(sym.usr);
|
||||||
[&](Use use, lsSymbolKind parent_kind) {
|
std::vector<Usr> stack{sym.usr};
|
||||||
if (use.role & params.context.role)
|
if (sym.kind != SymbolKind::Func)
|
||||||
if (std::optional<lsLocationEx> ls_loc =
|
params.context.base = false;
|
||||||
GetLsLocationEx(db, working_files, use, container)) {
|
while (stack.size()) {
|
||||||
if (container)
|
sym.usr = stack.back();
|
||||||
ls_loc->parentKind = parent_kind;
|
stack.pop_back();
|
||||||
out.result.push_back(*ls_loc);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +95,8 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
|
|||||||
}
|
}
|
||||||
if (args.empty())
|
if (args.empty())
|
||||||
return result;
|
return result;
|
||||||
|
args.insert(args.end(), config->extra_flags.begin(),
|
||||||
|
config->extra_flags.end());
|
||||||
|
|
||||||
std::unique_ptr<OptTable> Opts = driver::createDriverOptTable();
|
std::unique_ptr<OptTable> Opts = driver::createDriverOptTable();
|
||||||
unsigned MissingArgIndex, MissingArgCount;
|
unsigned MissingArgIndex, MissingArgCount;
|
||||||
@ -111,6 +113,11 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
|
|||||||
config->angle_dirs.insert(entry.ResolveIfRelative(A->getValue()));
|
config->angle_dirs.insert(entry.ResolveIfRelative(A->getValue()));
|
||||||
for (const auto* A : Args.filtered(OPT_I, OPT_iquote))
|
for (const auto* A : Args.filtered(OPT_I, OPT_iquote))
|
||||||
config->quote_dirs.insert(entry.ResolveIfRelative(A->getValue()));
|
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++)
|
for (size_t i = 1; i < args.size(); i++)
|
||||||
// This is most likely the file path we will be passing to clang. The
|
// This is most likely the file path we will be passing to clang. The
|
||||||
@ -122,10 +129,6 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
|
|||||||
continue;
|
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))
|
if (!Args.hasArg(OPT_resource_dir))
|
||||||
args.push_back("-resource-dir=" + g_config->clang.resourceDir);
|
args.push_back("-resource-dir=" + g_config->clang.resourceDir);
|
||||||
if (!Args.hasArg(OPT_working_directory))
|
if (!Args.hasArg(OPT_working_directory))
|
||||||
|
@ -110,30 +110,6 @@ void EachOccurrence(QueryDatabase* db,
|
|||||||
|
|
||||||
lsSymbolKind GetSymbolKind(QueryDatabase* db, SymbolIdx sym);
|
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>
|
template <typename Q, typename Fn>
|
||||||
void EachDefinedEntity(std::unordered_map<Usr, Q>& collection,
|
void EachDefinedEntity(std::unordered_map<Usr, Q>& collection,
|
||||||
const std::vector<Usr>& usrs,
|
const std::vector<Usr>& usrs,
|
||||||
|
@ -69,13 +69,11 @@ struct MultiQueueWaiter {
|
|||||||
template <class T>
|
template <class T>
|
||||||
struct ThreadedQueue : public BaseThreadQueue {
|
struct ThreadedQueue : public BaseThreadQueue {
|
||||||
public:
|
public:
|
||||||
ThreadedQueue() : total_count_(0) {
|
ThreadedQueue() {
|
||||||
owned_waiter_ = std::make_unique<MultiQueueWaiter>();
|
owned_waiter_ = std::make_unique<MultiQueueWaiter>();
|
||||||
waiter_ = owned_waiter_.get();
|
waiter_ = owned_waiter_.get();
|
||||||
}
|
}
|
||||||
|
explicit ThreadedQueue(MultiQueueWaiter* waiter) : waiter_(waiter) {}
|
||||||
explicit ThreadedQueue(MultiQueueWaiter* waiter)
|
|
||||||
: total_count_(0), waiter_(waiter) {}
|
|
||||||
|
|
||||||
// Returns the number of elements in the queue. This is lock-free.
|
// Returns the number of elements in the queue. This is lock-free.
|
||||||
size_t Size() const { return total_count_; }
|
size_t Size() const { return total_count_; }
|
||||||
@ -92,10 +90,6 @@ struct ThreadedQueue : public BaseThreadQueue {
|
|||||||
waiter_->cv.notify_one();
|
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) {
|
void PushBack(T&& t, bool priority = false) {
|
||||||
Push<&std::deque<T>::push_back>(std::move(t), priority);
|
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
|
// Get the first element from the queue without blocking. Returns a null
|
||||||
// value if the queue is empty.
|
// value if the queue is empty.
|
||||||
std::optional<T> TryPopFrontHelper(int which) {
|
std::optional<T> TryPopFront() {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
auto execute = [&](std::deque<T>* q) {
|
auto execute = [&](std::deque<T>* q) {
|
||||||
auto val = std::move(q->front());
|
auto val = std::move(q->front());
|
||||||
@ -170,35 +164,13 @@ struct ThreadedQueue : public BaseThreadQueue {
|
|||||||
--total_count_;
|
--total_count_;
|
||||||
return std::move(val);
|
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())
|
if (priority_.size())
|
||||||
return execute(&priority_);
|
return execute(&priority_);
|
||||||
|
if (queue_.size())
|
||||||
|
return execute(&queue_);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<T> TryPopFrontLow() { return TryPopFrontHelper(1); }
|
|
||||||
|
|
||||||
std::optional<T> TryPopFrontHigh() { return TryPopFrontHelper(2); }
|
|
||||||
|
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void Iterate(Fn fn) {
|
void Iterate(Fn fn) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
@ -211,7 +183,7 @@ struct ThreadedQueue : public BaseThreadQueue {
|
|||||||
mutable std::mutex mutex_;
|
mutable std::mutex mutex_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<int> total_count_;
|
std::atomic<int> total_count_{0};
|
||||||
std::deque<T> priority_;
|
std::deque<T> priority_;
|
||||||
std::deque<T> queue_;
|
std::deque<T> queue_;
|
||||||
MultiQueueWaiter* waiter_;
|
MultiQueueWaiter* waiter_;
|
||||||
|
Loading…
Reference in New Issue
Block a user