mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 09:05:10 +00:00
Use non-inferred entries and build preamble for .h; index on didOpen if no pending requests; documentHighlight
This commit is contained in:
parent
083a629f90
commit
fa2234c894
@ -219,16 +219,13 @@ void CompletionPreloadMain(CompletionManager *manager) {
|
|||||||
if (!session)
|
if (!session)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// For inferred session, don't build preamble because changes in a.h will
|
const auto &args = session->file.args;
|
||||||
// invalidate it.
|
WorkingFiles::Snapshot snapshot =
|
||||||
if (!session->inferred) {
|
session->wfiles->AsSnapshot({StripFileType(session->file.filename)});
|
||||||
const auto &args = session->file.args;
|
if (std::unique_ptr<CompilerInvocation> CI =
|
||||||
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot(
|
BuildCompilerInvocation(args, session->FS))
|
||||||
{StripFileType(session->file.filename)});
|
session->BuildPreamble(*CI, request.path);
|
||||||
if (std::unique_ptr<CompilerInvocation> CI =
|
|
||||||
BuildCompilerInvocation(args, session->FS))
|
|
||||||
session->BuildPreamble(*CI, request.path);
|
|
||||||
}
|
|
||||||
int debounce =
|
int debounce =
|
||||||
is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
|
is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
|
||||||
if (debounce >= 0) {
|
if (debounce >= 0) {
|
||||||
@ -528,7 +525,7 @@ bool CompletionManager::EnsureCompletionOrCreatePreloadSession(
|
|||||||
|
|
||||||
// No CompletionSession, create new one.
|
// No CompletionSession, create new one.
|
||||||
auto session = std::make_shared<ccls::CompletionSession>(
|
auto session = std::make_shared<ccls::CompletionSession>(
|
||||||
project_->FindCompilationEntryForFile(path), working_files_, PCH);
|
project_->FindEntry(path, false), working_files_, PCH);
|
||||||
if (session->file.filename != path) {
|
if (session->file.filename != path) {
|
||||||
session->inferred = true;
|
session->inferred = true;
|
||||||
session->file.filename = path;
|
session->file.filename = path;
|
||||||
@ -557,7 +554,7 @@ CompletionManager::TryGetSession(const std::string &path, bool preload,
|
|||||||
session = sessions.TryGet(path);
|
session = sessions.TryGet(path);
|
||||||
if (!session && !preload) {
|
if (!session && !preload) {
|
||||||
session = std::make_shared<ccls::CompletionSession>(
|
session = std::make_shared<ccls::CompletionSession>(
|
||||||
project_->FindCompilationEntryForFile(path), working_files_, PCH);
|
project_->FindEntry(path, false), working_files_, PCH);
|
||||||
sessions.Insert(path, session);
|
sessions.Insert(path, session);
|
||||||
LOG_S(INFO) << "create session for " << path;
|
LOG_S(INFO) << "create session for " << path;
|
||||||
if (is_open)
|
if (is_open)
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include <llvm/ADT/CachedHashString.h>
|
#include <llvm/ADT/CachedHashString.h>
|
||||||
#include <llvm/ADT/DenseMap.h>
|
#include <llvm/ADT/DenseMap.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@ -64,21 +64,11 @@ struct Handler_TextDocumentDefinition
|
|||||||
Out_TextDocumentDefinition out;
|
Out_TextDocumentDefinition out;
|
||||||
out.id = request->id;
|
out.id = request->id;
|
||||||
|
|
||||||
Maybe<Range> range;
|
|
||||||
SymbolKind kind;
|
|
||||||
Maybe<Use> on_def;
|
Maybe<Use> on_def;
|
||||||
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
|
||||||
lsPosition &ls_pos = params.position;
|
lsPosition &ls_pos = params.position;
|
||||||
|
|
||||||
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos)) {
|
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) {
|
||||||
if (!range) {
|
|
||||||
range = sym.range;
|
|
||||||
kind = sym.kind;
|
|
||||||
} else if (!(sym.range == *range && sym.kind == kind)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Found symbol. Return definition.
|
|
||||||
|
|
||||||
// Special cases which are handled:
|
// Special cases which are handled:
|
||||||
// - symbol has declaration but no definition (ie, pure virtual)
|
// - symbol has declaration but no definition (ie, pure virtual)
|
||||||
// - goto declaration while in definition of recursive type
|
// - goto declaration while in definition of recursive type
|
||||||
@ -117,6 +107,7 @@ struct Handler_TextDocumentDefinition
|
|||||||
out.result.erase(std::unique(out.result.begin(), out.result.end()),
|
out.result.erase(std::unique(out.result.begin(), out.result.end()),
|
||||||
out.result.end());
|
out.result.end());
|
||||||
} else {
|
} else {
|
||||||
|
Maybe<Range> range;
|
||||||
// Check #include
|
// Check #include
|
||||||
for (const IndexInclude &include : file->def->includes) {
|
for (const IndexInclude &include : file->def->includes) {
|
||||||
if (include.line == ls_pos.line) {
|
if (include.line == ls_pos.line) {
|
||||||
|
@ -113,8 +113,10 @@ struct Handler_TextDocumentDidOpen
|
|||||||
if (args.size())
|
if (args.size())
|
||||||
project->SetArgsForFile(args, path);
|
project->SetArgsForFile(args, path);
|
||||||
|
|
||||||
// Submit new index request if it is not a header file.
|
// Submit new index request if it is not a header file or there is no
|
||||||
if (SourceFileLanguage(path) != LanguageId::Unknown)
|
// pending index request.
|
||||||
|
if (SourceFileLanguage(path) != LanguageId::Unknown ||
|
||||||
|
!pipeline::pending_index_requests)
|
||||||
pipeline::Index(path, args, IndexMode::Normal);
|
pipeline::Index(path, args, IndexMode::Normal);
|
||||||
|
|
||||||
clang_complete->NotifyView(path);
|
clang_complete->NotifyView(path);
|
||||||
|
@ -5,11 +5,28 @@
|
|||||||
#include "pipeline.hh"
|
#include "pipeline.hh"
|
||||||
#include "query_utils.h"
|
#include "query_utils.h"
|
||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
using namespace ccls;
|
using namespace ccls;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
MethodType kMethodType = "textDocument/documentHighlight";
|
MethodType kMethodType = "textDocument/documentHighlight";
|
||||||
|
|
||||||
|
struct lsDocumentHighlight {
|
||||||
|
enum Kind { Text = 1, Read = 2, Write = 3 };
|
||||||
|
|
||||||
|
lsRange range;
|
||||||
|
int kind = 1;
|
||||||
|
|
||||||
|
// ccls extension
|
||||||
|
Role role = Role::None;
|
||||||
|
|
||||||
|
bool operator<(const lsDocumentHighlight &o) const {
|
||||||
|
return !(range == o.range) ? range < o.range : kind < o.kind;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind, role);
|
||||||
|
|
||||||
struct In_TextDocumentDocumentHighlight : public RequestInMessage {
|
struct In_TextDocumentDocumentHighlight : public RequestInMessage {
|
||||||
MethodType GetMethodType() const override { return kMethodType; }
|
MethodType GetMethodType() const override { return kMethodType; }
|
||||||
lsTextDocumentPositionParams params;
|
lsTextDocumentPositionParams params;
|
||||||
@ -32,39 +49,41 @@ struct Handler_TextDocumentDocumentHighlight
|
|||||||
QueryFile *file;
|
QueryFile *file;
|
||||||
if (!FindFileOrFail(db, project, request->id,
|
if (!FindFileOrFail(db, project, request->id,
|
||||||
request->params.textDocument.uri.GetPath(), &file,
|
request->params.textDocument.uri.GetPath(), &file,
|
||||||
&file_id)) {
|
&file_id))
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
WorkingFile *working_file =
|
WorkingFile *working_file =
|
||||||
working_files->GetFileByFilename(file->def->path);
|
working_files->GetFileByFilename(file->def->path);
|
||||||
|
|
||||||
Out_TextDocumentDocumentHighlight out;
|
Out_TextDocumentDocumentHighlight out;
|
||||||
out.id = request->id;
|
out.id = request->id;
|
||||||
|
|
||||||
for (SymbolRef sym :
|
std::vector<SymbolRef> syms = FindSymbolsAtLocation(
|
||||||
FindSymbolsAtLocation(working_file, file, request->params.position)) {
|
working_file, file, request->params.position, true);
|
||||||
// Found symbol. Return references to highlight.
|
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
||||||
EachOccurrence(db, sym, true, [&](Use use) {
|
if (refcnt <= 0)
|
||||||
if (use.file_id != file_id)
|
continue;
|
||||||
return;
|
Usr usr = sym.usr;
|
||||||
if (std::optional<lsLocation> ls_loc =
|
SymbolKind kind = sym.kind;
|
||||||
GetLsLocation(db, working_files, use)) {
|
if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) {
|
||||||
lsDocumentHighlight highlight;
|
return usr == sym1.usr && kind == sym1.kind;
|
||||||
highlight.range = ls_loc->range;
|
}))
|
||||||
if (use.role & Role::Write)
|
continue;
|
||||||
highlight.kind = lsDocumentHighlightKind::Write;
|
if (auto ls_loc =
|
||||||
else if (use.role & Role::Read)
|
GetLsLocation(db, working_files,
|
||||||
highlight.kind = lsDocumentHighlightKind::Read;
|
Use{{sym.range, usr, kind, sym.role}, file_id})) {
|
||||||
else
|
lsDocumentHighlight highlight;
|
||||||
highlight.kind = lsDocumentHighlightKind::Text;
|
highlight.range = ls_loc->range;
|
||||||
highlight.role = use.role;
|
if (sym.role & Role::Write)
|
||||||
out.result.push_back(highlight);
|
highlight.kind = lsDocumentHighlight::Write;
|
||||||
}
|
else if (sym.role & Role::Read)
|
||||||
});
|
highlight.kind = lsDocumentHighlight::Read;
|
||||||
break;
|
else
|
||||||
|
highlight.kind = lsDocumentHighlight::Text;
|
||||||
|
highlight.role = sym.role;
|
||||||
|
out.result.push_back(highlight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
std::sort(out.result.begin(), out.result.end());
|
||||||
pipeline::WriteStdout(kMethodType, out);
|
pipeline::WriteStdout(kMethodType, out);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -50,7 +50,8 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
|
|||||||
|
|
||||||
namespace ccls::pipeline {
|
namespace ccls::pipeline {
|
||||||
|
|
||||||
std::atomic<int64_t> loaded_ts = ATOMIC_VAR_INIT(0);
|
std::atomic<int64_t> loaded_ts = ATOMIC_VAR_INIT(0),
|
||||||
|
pending_index_requests = ATOMIC_VAR_INIT(0);
|
||||||
int64_t tick = 0;
|
int64_t tick = 0;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -163,6 +164,9 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
|
|||||||
return false;
|
return false;
|
||||||
auto &request = *opt_request;
|
auto &request = *opt_request;
|
||||||
bool loud = request.mode != IndexMode::OnChange;
|
bool loud = request.mode != IndexMode::OnChange;
|
||||||
|
struct RAII {
|
||||||
|
~RAII() { pending_index_requests--; }
|
||||||
|
} raii;
|
||||||
|
|
||||||
// Dummy one to trigger refresh semantic highlight.
|
// Dummy one to trigger refresh semantic highlight.
|
||||||
if (request.path.empty()) {
|
if (request.path.empty()) {
|
||||||
@ -177,7 +181,7 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Project::Entry entry = project->FindCompilationEntryForFile(request.path);
|
Project::Entry entry = project->FindEntry(request.path, true);
|
||||||
if (request.args.size())
|
if (request.args.size())
|
||||||
entry.args = request.args;
|
entry.args = request.args;
|
||||||
std::string path_to_index = entry.filename;
|
std::string path_to_index = entry.filename;
|
||||||
@ -535,6 +539,7 @@ void MainLoop() {
|
|||||||
|
|
||||||
void Index(const std::string &path, const std::vector<const char *> &args,
|
void Index(const std::string &path, const std::vector<const char *> &args,
|
||||||
IndexMode mode, lsRequestId id) {
|
IndexMode mode, lsRequestId id) {
|
||||||
|
pending_index_requests++;
|
||||||
index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive);
|
index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ enum class IndexMode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace pipeline {
|
namespace pipeline {
|
||||||
extern std::atomic<int64_t> loaded_ts;
|
extern std::atomic<int64_t> loaded_ts, pending_index_requests;
|
||||||
extern int64_t tick;
|
extern int64_t tick;
|
||||||
void Init();
|
void Init();
|
||||||
void LaunchStdin();
|
void LaunchStdin();
|
||||||
|
@ -374,13 +374,16 @@ void Project::SetArgsForFile(const std::vector<const char *> &args,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Project::Entry
|
Project::Entry Project::FindEntry(const std::string &path,
|
||||||
Project::FindCompilationEntryForFile(const std::string &filename) {
|
bool can_be_inferred) {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
auto it = path_to_entry_index.find(filename);
|
auto it = path_to_entry_index.find(path);
|
||||||
if (it != path_to_entry_index.end())
|
if (it != path_to_entry_index.end()) {
|
||||||
return entries[it->second];
|
Project::Entry &entry = entries[it->second];
|
||||||
|
if (can_be_inferred || entry.filename == path)
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We couldn't find the file. Try to infer it.
|
// We couldn't find the file. Try to infer it.
|
||||||
@ -388,7 +391,7 @@ Project::FindCompilationEntryForFile(const std::string &filename) {
|
|||||||
Entry *best_entry = nullptr;
|
Entry *best_entry = nullptr;
|
||||||
int best_score = INT_MIN;
|
int best_score = INT_MIN;
|
||||||
for (Entry &entry : entries) {
|
for (Entry &entry : entries) {
|
||||||
int score = ComputeGuessScore(filename, entry.filename);
|
int score = ComputeGuessScore(path, entry.filename);
|
||||||
if (score > best_score) {
|
if (score > best_score) {
|
||||||
best_score = score;
|
best_score = score;
|
||||||
best_entry = &entry;
|
best_entry = &entry;
|
||||||
@ -397,10 +400,10 @@ Project::FindCompilationEntryForFile(const std::string &filename) {
|
|||||||
|
|
||||||
Project::Entry result;
|
Project::Entry result;
|
||||||
result.is_inferred = true;
|
result.is_inferred = true;
|
||||||
result.filename = filename;
|
result.filename = path;
|
||||||
if (!best_entry) {
|
if (!best_entry) {
|
||||||
result.args.push_back("%clang");
|
result.args.push_back("%clang");
|
||||||
result.args.push_back(Intern(filename));
|
result.args.push_back(Intern(path));
|
||||||
} else {
|
} else {
|
||||||
result.args = best_entry->args;
|
result.args = best_entry->args;
|
||||||
|
|
||||||
@ -412,7 +415,7 @@ Project::FindCompilationEntryForFile(const std::string &filename) {
|
|||||||
try {
|
try {
|
||||||
if (arg == best_entry->filename ||
|
if (arg == best_entry->filename ||
|
||||||
sys::path::filename(arg) == best_entry_base_name)
|
sys::path::filename(arg) == best_entry_base_name)
|
||||||
arg = Intern(filename);
|
arg = Intern(path);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ struct Project {
|
|||||||
|
|
||||||
// Lookup the CompilationEntry for |filename|. If no entry was found this
|
// Lookup the CompilationEntry for |filename|. If no entry was found this
|
||||||
// will infer one based on existing project structure.
|
// will infer one based on existing project structure.
|
||||||
Entry FindCompilationEntryForFile(const std::string &filename);
|
Entry FindEntry(const std::string &path, bool can_be_inferred);
|
||||||
|
|
||||||
// If the client has overridden the flags, or specified them for a file
|
// If the client has overridden the flags, or specified them for a file
|
||||||
// that is not in the compilation_database.json make sure those changes
|
// that is not in the compilation_database.json make sure those changes
|
||||||
|
@ -296,7 +296,8 @@ std::optional<lsSymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
|||||||
|
|
||||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
||||||
QueryFile *file,
|
QueryFile *file,
|
||||||
lsPosition &ls_pos) {
|
lsPosition &ls_pos,
|
||||||
|
bool smallest) {
|
||||||
std::vector<SymbolRef> symbols;
|
std::vector<SymbolRef> symbols;
|
||||||
// If multiVersion > 0, index may not exist and thus index_lines is empty.
|
// If multiVersion > 0, index may not exist and thus index_lines is empty.
|
||||||
if (wfile && wfile->index_lines.size()) {
|
if (wfile && wfile->index_lines.size()) {
|
||||||
@ -341,6 +342,14 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
|||||||
return t > 0;
|
return t > 0;
|
||||||
return a.usr < b.usr;
|
return a.usr < b.usr;
|
||||||
});
|
});
|
||||||
|
if (symbols.size() && smallest) {
|
||||||
|
SymbolRef sym = symbols[0];
|
||||||
|
for (size_t i = 1; i < symbols.size(); i++)
|
||||||
|
if (!(sym.range == symbols[i].range && sym.kind == symbols[i].kind)) {
|
||||||
|
symbols.resize(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return symbols;
|
return symbols;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,8 @@ std::optional<lsSymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
|||||||
|
|
||||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *working_file,
|
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *working_file,
|
||||||
QueryFile *file,
|
QueryFile *file,
|
||||||
lsPosition &ls_pos);
|
lsPosition &ls_pos,
|
||||||
|
bool smallest = false);
|
||||||
|
|
||||||
template <typename Fn> void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) {
|
template <typename Fn> void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||||
switch (sym.kind) {
|
switch (sym.kind) {
|
||||||
|
26
src/symbol.h
26
src/symbol.h
@ -34,32 +34,6 @@ inline Role operator|(Role lhs, Role rhs) {
|
|||||||
return Role(uint16_t(lhs) | uint16_t(rhs));
|
return Role(uint16_t(lhs) | uint16_t(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A document highlight kind.
|
|
||||||
enum class lsDocumentHighlightKind {
|
|
||||||
// A textual occurrence.
|
|
||||||
Text = 1,
|
|
||||||
// Read-access of a symbol, like reading a variable.
|
|
||||||
Read = 2,
|
|
||||||
// Write-access of a symbol, like writing to a variable.
|
|
||||||
Write = 3
|
|
||||||
};
|
|
||||||
MAKE_REFLECT_TYPE_PROXY(lsDocumentHighlightKind);
|
|
||||||
|
|
||||||
// A document highlight is a range inside a text document which deserves
|
|
||||||
// special attention. Usually a document highlight is visualized by changing
|
|
||||||
// the background color of its range.
|
|
||||||
struct lsDocumentHighlight {
|
|
||||||
// The range this highlight applies to.
|
|
||||||
lsRange range;
|
|
||||||
|
|
||||||
// The highlight kind, default is DocumentHighlightKind.Text.
|
|
||||||
lsDocumentHighlightKind kind = lsDocumentHighlightKind::Text;
|
|
||||||
|
|
||||||
// ccls extension
|
|
||||||
Role role = Role::None;
|
|
||||||
};
|
|
||||||
MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind, role);
|
|
||||||
|
|
||||||
struct lsSymbolInformation {
|
struct lsSymbolInformation {
|
||||||
std::string_view name;
|
std::string_view name;
|
||||||
lsSymbolKind kind;
|
lsSymbolKind kind;
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
Loading…
Reference in New Issue
Block a user