Use non-inferred entries and build preamble for .h; index on didOpen if no pending requests; documentHighlight

This commit is contained in:
Fangrui Song 2018-09-30 22:54:48 -07:00
parent 083a629f90
commit fa2234c894
13 changed files with 92 additions and 93 deletions

View File

@ -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
// invalidate it.
if (!session->inferred) {
const auto &args = session->file.args; const auto &args = session->file.args;
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot( WorkingFiles::Snapshot snapshot =
{StripFileType(session->file.filename)}); session->wfiles->AsSnapshot({StripFileType(session->file.filename)});
if (std::unique_ptr<CompilerInvocation> CI = if (std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(args, session->FS)) BuildCompilerInvocation(args, session->FS))
session->BuildPreamble(*CI, request.path); 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)

View File

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

View File

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

View File

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

View File

@ -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) {
return usr == sym1.usr && kind == sym1.kind;
}))
continue;
if (auto ls_loc =
GetLsLocation(db, working_files,
Use{{sym.range, usr, kind, sym.role}, file_id})) {
lsDocumentHighlight highlight; lsDocumentHighlight highlight;
highlight.range = ls_loc->range; highlight.range = ls_loc->range;
if (use.role & Role::Write) if (sym.role & Role::Write)
highlight.kind = lsDocumentHighlightKind::Write; highlight.kind = lsDocumentHighlight::Write;
else if (use.role & Role::Read) else if (sym.role & Role::Read)
highlight.kind = lsDocumentHighlightKind::Read; highlight.kind = lsDocumentHighlight::Read;
else else
highlight.kind = lsDocumentHighlightKind::Text; highlight.kind = lsDocumentHighlight::Text;
highlight.role = use.role; highlight.role = sym.role;
out.result.push_back(highlight); out.result.push_back(highlight);
} }
});
break;
} }
std::sort(out.result.begin(), out.result.end());
pipeline::WriteStdout(kMethodType, out); pipeline::WriteStdout(kMethodType, out);
} }
}; };

View File

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

View File

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

View File

@ -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 (...) {
} }
} }

View File

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

View File

@ -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;
} }

View File

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

View File

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

View File

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