From 84984c6c2720df8454b4c0bfec296db792253792 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 30 Sep 2018 22:54:48 -0700 Subject: [PATCH] Use non-inferred entries and build preamble for .h; index on didOpen if no pending requests; documentHighlight --- src/clang_complete.cc | 21 +++--- src/indexer.h | 1 - src/messages/textDocument_definition.cc | 13 +--- src/messages/textDocument_did.cc | 6 +- .../textDocument_documentHighlight.cc | 69 ++++++++++++------- src/pipeline.cc | 9 ++- src/pipeline.hh | 2 +- src/project.cc | 21 +++--- src/project.h | 2 +- src/query_utils.cc | 11 ++- src/query_utils.h | 3 +- src/symbol.h | 26 ------- src/utils.h | 1 - 13 files changed, 92 insertions(+), 93 deletions(-) diff --git a/src/clang_complete.cc b/src/clang_complete.cc index 6a785470..b66a01f8 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -231,16 +231,13 @@ void CompletionPreloadMain(CompletionManager *manager) { if (!session) 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; - WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot( - {StripFileType(session->file.filename)}); - if (std::unique_ptr CI = - BuildCompilerInvocation(args, session->FS)) - session->BuildPreamble(*CI, request.path); - } + const auto &args = session->file.args; + WorkingFiles::Snapshot snapshot = + session->wfiles->AsSnapshot({StripFileType(session->file.filename)}); + if (std::unique_ptr CI = + BuildCompilerInvocation(args, session->FS)) + session->BuildPreamble(*CI, request.path); + int debounce = is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave; if (debounce >= 0) { @@ -539,7 +536,7 @@ bool CompletionManager::EnsureCompletionOrCreatePreloadSession( // No CompletionSession, create new one. auto session = std::make_shared( - project_->FindCompilationEntryForFile(path), working_files_, PCH); + project_->FindEntry(path, false), working_files_, PCH); if (session->file.filename != path) { session->inferred = true; session->file.filename = path; @@ -568,7 +565,7 @@ CompletionManager::TryGetSession(const std::string &path, bool preload, session = sessions.TryGet(path); if (!session && !preload) { session = std::make_shared( - project_->FindCompilationEntryForFile(path), working_files_, PCH); + project_->FindEntry(path, false), working_files_, PCH); sessions.Insert(path, session); LOG_S(INFO) << "create session for " << path; if (is_open) diff --git a/src/indexer.h b/src/indexer.h index 0e6f4e71..2a911067 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -29,7 +29,6 @@ limitations under the License. #include #include -#include #include #include #include diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index df28502d..2718abd7 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -76,21 +76,11 @@ struct Handler_TextDocumentDefinition Out_TextDocumentDefinition out; out.id = request->id; - Maybe range; - SymbolKind kind; Maybe on_def; WorkingFile *wfile = working_files->GetFileByFilename(file->def->path); lsPosition &ls_pos = params.position; - for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos)) { - if (!range) { - range = sym.range; - kind = sym.kind; - } else if (!(sym.range == *range && sym.kind == kind)) { - break; - } - // Found symbol. Return definition. - + for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) { // Special cases which are handled: // - symbol has declaration but no definition (ie, pure virtual) // - goto declaration while in definition of recursive type @@ -129,6 +119,7 @@ struct Handler_TextDocumentDefinition out.result.erase(std::unique(out.result.begin(), out.result.end()), out.result.end()); } else { + Maybe range; // Check #include for (const IndexInclude &include : file->def->includes) { if (include.line == ls_pos.line) { diff --git a/src/messages/textDocument_did.cc b/src/messages/textDocument_did.cc index 5a069ee2..30bf2acd 100644 --- a/src/messages/textDocument_did.cc +++ b/src/messages/textDocument_did.cc @@ -125,8 +125,10 @@ struct Handler_TextDocumentDidOpen if (args.size()) project->SetArgsForFile(args, path); - // Submit new index request if it is not a header file. - if (SourceFileLanguage(path) != LanguageId::Unknown) + // Submit new index request if it is not a header file or there is no + // pending index request. + if (SourceFileLanguage(path) != LanguageId::Unknown || + !pipeline::pending_index_requests) pipeline::Index(path, args, IndexMode::Normal); clang_complete->NotifyView(path); diff --git a/src/messages/textDocument_documentHighlight.cc b/src/messages/textDocument_documentHighlight.cc index 871f617b..e3d6e35d 100644 --- a/src/messages/textDocument_documentHighlight.cc +++ b/src/messages/textDocument_documentHighlight.cc @@ -17,11 +17,28 @@ limitations under the License. #include "pipeline.hh" #include "query_utils.h" #include "symbol.h" + +#include using namespace ccls; namespace { 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 { MethodType GetMethodType() const override { return kMethodType; } lsTextDocumentPositionParams params; @@ -44,39 +61,41 @@ struct Handler_TextDocumentDocumentHighlight QueryFile *file; if (!FindFileOrFail(db, project, request->id, request->params.textDocument.uri.GetPath(), &file, - &file_id)) { + &file_id)) return; - } - WorkingFile *working_file = working_files->GetFileByFilename(file->def->path); Out_TextDocumentDocumentHighlight out; out.id = request->id; - for (SymbolRef sym : - FindSymbolsAtLocation(working_file, file, request->params.position)) { - // Found symbol. Return references to highlight. - EachOccurrence(db, sym, true, [&](Use use) { - if (use.file_id != file_id) - return; - if (std::optional ls_loc = - GetLsLocation(db, working_files, use)) { - lsDocumentHighlight highlight; - highlight.range = ls_loc->range; - if (use.role & Role::Write) - highlight.kind = lsDocumentHighlightKind::Write; - else if (use.role & Role::Read) - highlight.kind = lsDocumentHighlightKind::Read; - else - highlight.kind = lsDocumentHighlightKind::Text; - highlight.role = use.role; - out.result.push_back(highlight); - } - }); - break; + std::vector syms = FindSymbolsAtLocation( + working_file, file, request->params.position, true); + for (auto [sym, refcnt] : file->symbol2refcnt) { + if (refcnt <= 0) + continue; + Usr usr = sym.usr; + SymbolKind kind = sym.kind; + 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; + highlight.range = ls_loc->range; + if (sym.role & Role::Write) + highlight.kind = lsDocumentHighlight::Write; + else if (sym.role & Role::Read) + highlight.kind = lsDocumentHighlight::Read; + 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); } }; diff --git a/src/pipeline.cc b/src/pipeline.cc index f4e5cc1f..c61469c0 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -62,7 +62,8 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) { namespace ccls::pipeline { -std::atomic loaded_ts = ATOMIC_VAR_INIT(0); +std::atomic loaded_ts = ATOMIC_VAR_INIT(0), + pending_index_requests = ATOMIC_VAR_INIT(0); int64_t tick = 0; namespace { @@ -175,6 +176,9 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles, return false; auto &request = *opt_request; bool loud = request.mode != IndexMode::OnChange; + struct RAII { + ~RAII() { pending_index_requests--; } + } raii; // Dummy one to trigger refresh semantic highlight. if (request.path.empty()) { @@ -189,7 +193,7 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles, return false; } - Project::Entry entry = project->FindCompilationEntryForFile(request.path); + Project::Entry entry = project->FindEntry(request.path, true); if (request.args.size()) entry.args = request.args; std::string path_to_index = entry.filename; @@ -547,6 +551,7 @@ void MainLoop() { void Index(const std::string &path, const std::vector &args, IndexMode mode, lsRequestId id) { + pending_index_requests++; index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive); } diff --git a/src/pipeline.hh b/src/pipeline.hh index a232d513..b4cc514b 100644 --- a/src/pipeline.hh +++ b/src/pipeline.hh @@ -39,7 +39,7 @@ enum class IndexMode { }; namespace pipeline { -extern std::atomic loaded_ts; +extern std::atomic loaded_ts, pending_index_requests; extern int64_t tick; void Init(); void LaunchStdin(); diff --git a/src/project.cc b/src/project.cc index 70802538..ca1a0f38 100644 --- a/src/project.cc +++ b/src/project.cc @@ -386,13 +386,16 @@ void Project::SetArgsForFile(const std::vector &args, } } -Project::Entry -Project::FindCompilationEntryForFile(const std::string &filename) { +Project::Entry Project::FindEntry(const std::string &path, + bool can_be_inferred) { { std::lock_guard lock(mutex_); - auto it = path_to_entry_index.find(filename); - if (it != path_to_entry_index.end()) - return entries[it->second]; + auto it = path_to_entry_index.find(path); + if (it != path_to_entry_index.end()) { + 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. @@ -400,7 +403,7 @@ Project::FindCompilationEntryForFile(const std::string &filename) { Entry *best_entry = nullptr; int best_score = INT_MIN; for (Entry &entry : entries) { - int score = ComputeGuessScore(filename, entry.filename); + int score = ComputeGuessScore(path, entry.filename); if (score > best_score) { best_score = score; best_entry = &entry; @@ -409,10 +412,10 @@ Project::FindCompilationEntryForFile(const std::string &filename) { Project::Entry result; result.is_inferred = true; - result.filename = filename; + result.filename = path; if (!best_entry) { result.args.push_back("%clang"); - result.args.push_back(Intern(filename)); + result.args.push_back(Intern(path)); } else { result.args = best_entry->args; @@ -424,7 +427,7 @@ Project::FindCompilationEntryForFile(const std::string &filename) { try { if (arg == best_entry->filename || sys::path::filename(arg) == best_entry_base_name) - arg = Intern(filename); + arg = Intern(path); } catch (...) { } } diff --git a/src/project.h b/src/project.h index 125b2af0..59a89855 100644 --- a/src/project.h +++ b/src/project.h @@ -59,7 +59,7 @@ struct Project { // Lookup the CompilationEntry for |filename|. If no entry was found this // 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 // that is not in the compilation_database.json make sure those changes diff --git a/src/query_utils.cc b/src/query_utils.cc index c2e0f0b7..27ce5f71 100644 --- a/src/query_utils.cc +++ b/src/query_utils.cc @@ -308,7 +308,8 @@ std::optional GetSymbolInfo(DB *db, SymbolIdx sym, std::vector FindSymbolsAtLocation(WorkingFile *wfile, QueryFile *file, - lsPosition &ls_pos) { + lsPosition &ls_pos, + bool smallest) { std::vector symbols; // If multiVersion > 0, index may not exist and thus index_lines is empty. if (wfile && wfile->index_lines.size()) { @@ -353,6 +354,14 @@ std::vector FindSymbolsAtLocation(WorkingFile *wfile, return t > 0; 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; } diff --git a/src/query_utils.h b/src/query_utils.h index 0ec2501c..642a6ba1 100644 --- a/src/query_utils.h +++ b/src/query_utils.h @@ -51,7 +51,8 @@ std::optional GetSymbolInfo(DB *db, SymbolIdx sym, std::vector FindSymbolsAtLocation(WorkingFile *working_file, QueryFile *file, - lsPosition &ls_pos); + lsPosition &ls_pos, + bool smallest = false); template void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) { switch (sym.kind) { diff --git a/src/symbol.h b/src/symbol.h index 16e4de11..beec82b8 100644 --- a/src/symbol.h +++ b/src/symbol.h @@ -46,32 +46,6 @@ inline Role operator|(Role lhs, Role 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 { std::string_view name; lsSymbolKind kind; diff --git a/src/utils.h b/src/utils.h index 7b4087ce..2e556074 100644 --- a/src/utils.h +++ b/src/utils.h @@ -18,7 +18,6 @@ limitations under the License. #include #include -#include #include #include #include