diff --git a/src/clang_complete.cc b/src/clang_complete.cc index b68b4a2a..799830f0 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -219,16 +219,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) { @@ -528,7 +525,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; @@ -557,7 +554,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 dc9ccb76..f941c9d7 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index d5ed12f4..61995811 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -64,21 +64,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 @@ -117,6 +107,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 173251ba..67fc5539 100644 --- a/src/messages/textDocument_did.cc +++ b/src/messages/textDocument_did.cc @@ -113,8 +113,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 e67e6676..b6fcc41a 100644 --- a/src/messages/textDocument_documentHighlight.cc +++ b/src/messages/textDocument_documentHighlight.cc @@ -5,11 +5,28 @@ #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; @@ -32,39 +49,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 c6c56abd..c68774f2 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -50,7 +50,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 { @@ -163,6 +164,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()) { @@ -177,7 +181,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; @@ -535,6 +539,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 ee32482f..42230ad4 100644 --- a/src/pipeline.hh +++ b/src/pipeline.hh @@ -42,7 +42,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 1eb3fda4..d9b17ccc 100644 --- a/src/project.cc +++ b/src/project.cc @@ -374,13 +374,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. @@ -388,7 +391,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; @@ -397,10 +400,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; @@ -412,7 +415,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 b0400fcf..3c8ce989 100644 --- a/src/project.h +++ b/src/project.h @@ -47,7 +47,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 85f61e80..2c77abc7 100644 --- a/src/query_utils.cc +++ b/src/query_utils.cc @@ -296,7 +296,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()) { @@ -341,6 +342,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 c5e325c0..8f24ce70 100644 --- a/src/query_utils.h +++ b/src/query_utils.h @@ -39,7 +39,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 4d83f57c..f99c7fec 100644 --- a/src/symbol.h +++ b/src/symbol.h @@ -34,32 +34,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 dacef627..cc90bbb4 100644 --- a/src/utils.h +++ b/src/utils.h @@ -6,7 +6,6 @@ #include #include -#include #include #include #include