diff --git a/src/clang_complete.cc b/src/clang_complete.cc index f9373e60..213110eb 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -611,6 +611,7 @@ void DiagnosticMain(ClangCompleteManager *manager) { BuildCompilerInvocation(session->file.args, session->FS); if (!CI) continue; + CI->getLangOpts()->SpellChecking = true; StoreDiags DC; WorkingFiles::Snapshot snapshot = manager->working_files_->AsSnapshot({StripFileType(path)}); diff --git a/src/config.h b/src/config.h index 3a134b25..60389bf1 100644 --- a/src/config.h +++ b/src/config.h @@ -189,7 +189,7 @@ struct Config { // Allow indexing on textDocument/didChange. // May be too slow for big projects, so it is off by default. - bool onDidChange = false; + bool onChange = false; // Whether to reparse a file if write times of its dependencies have // changed. The file will always be reparsed if its own write time changes. @@ -233,7 +233,7 @@ MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange, onOpen, whitelist) MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist) MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion, - multiVersionBlacklist, multiVersionWhitelist, onDidChange, + multiVersionBlacklist, multiVersionWhitelist, onChange, reparseForDependency, threads, whitelist); MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); MAKE_REFLECT_STRUCT(Config::Xref, container, maxNum); diff --git a/src/indexer.cc b/src/indexer.cc index a77ab524..211f2dac 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -1116,9 +1116,10 @@ public: void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override { llvm::sys::fs::UniqueID UniqueID; auto range = FromCharRange(SM, param.Ctx->getLangOpts(), Range, &UniqueID); - const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Range.getBegin())); - if (IndexFile *db = param.ConsumeFile(*FE)) - db->skipped_ranges.push_back(range); + if (const FileEntry *FE = + SM.getFileEntryForID(SM.getFileID(Range.getBegin()))) + if (IndexFile *db = param.ConsumeFile(*FE)) + db->skipped_ranges.push_back(range); } }; @@ -1187,7 +1188,7 @@ void Init() { std::vector> Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, const std::vector &args, - const std::vector &file_contents) { + const std::vector> &remapped) { if (!g_config->index.enabled) return {}; @@ -1201,7 +1202,14 @@ Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, CI->getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1; CI->getLangOpts()->RetainCommentsFromSystemHeaders = true; - CI->getLangOpts()->SpellChecking = false; + std::vector> Bufs; + for (auto &[filename, content] : remapped) { + Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(content)); + CI->getPreprocessorOpts().addRemappedFile( + filename == file ? CI->getFrontendOpts().Inputs[0].getFile() + : StringRef(filename), + Bufs.back().get()); + } DiagnosticConsumer DC; auto Clang = std::make_unique(PCH); @@ -1248,6 +1256,8 @@ Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, LOG_S(ERROR) << "failed to index " << file; return {}; } + for (auto &Buf : Bufs) + Buf.release(); auto result = param.file_consumer->TakeLocalState(); for (std::unique_ptr &entry : result) { diff --git a/src/indexer.h b/src/indexer.h index 3b855927..c2c9f2ba 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -276,5 +276,5 @@ void Init(); std::vector> Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, const std::vector &args, - const std::vector &file_contents); -} + const std::vector>& remapped); +} // namespace ccls::idx diff --git a/src/messages/textDocument_didChange.cc b/src/messages/textDocument_didChange.cc index 696063f4..df5efebd 100644 --- a/src/messages/textDocument_didChange.cc +++ b/src/messages/textDocument_didChange.cc @@ -26,9 +26,9 @@ struct Handler_TextDocumentDidChange void Run(In_TextDocumentDidChange *request) override { std::string path = request->params.textDocument.uri.GetPath(); working_files->OnChange(request->params); - if (g_config->index.onDidChange) { + if (g_config->index.onChange) { Project::Entry entry = project->FindCompilationEntryForFile(path); - pipeline::Index(entry.filename, entry.args, true); + pipeline::Index(entry.filename, entry.args, IndexMode::OnChange); } clang_complete->NotifyEdit(path); clang_complete->DiagnosticsUpdate( diff --git a/src/messages/textDocument_didOpen.cc b/src/messages/textDocument_didOpen.cc index 16d5c503..fcb2e45e 100644 --- a/src/messages/textDocument_didOpen.cc +++ b/src/messages/textDocument_didOpen.cc @@ -23,8 +23,7 @@ struct In_TextDocumentDidOpen : public NotificationInMessage { // If specified (e.g. ["clang++", "-DM", "a.cc"]), it overrides the project // entry (e.g. loaded from compile_commands.json or .ccls). std::vector args; - }; - Params params; + } params; }; MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen::Params, textDocument, args); MAKE_REFLECT_STRUCT(In_TextDocumentDidOpen, params); @@ -60,7 +59,8 @@ struct Handler_TextDocumentDidOpen if (SourceFileLanguage(path) != LanguageId::Unknown) { Project::Entry entry = project->FindCompilationEntryForFile(path); pipeline::Index(entry.filename, - params.args.size() ? params.args : entry.args, true); + params.args.size() ? params.args : entry.args, + IndexMode::Normal); clang_complete->FlushSession(entry.filename); } diff --git a/src/messages/textDocument_didSave.cc b/src/messages/textDocument_didSave.cc index 7c8fc791..2cf37f46 100644 --- a/src/messages/textDocument_didSave.cc +++ b/src/messages/textDocument_didSave.cc @@ -20,8 +20,7 @@ struct In_TextDocumentDidSave : public NotificationInMessage { // Optional the content when saved. Depends on the includeText value // when the save notifcation was requested. // std::string text; - }; - Params params; + } params; }; MAKE_REFLECT_STRUCT(In_TextDocumentDidSave::Params, textDocument); MAKE_REFLECT_STRUCT(In_TextDocumentDidSave, params); @@ -35,25 +34,8 @@ struct Handler_TextDocumentDidSave const auto ¶ms = request->params; std::string path = params.textDocument.uri.GetPath(); - // Send out an index request, and copy the current buffer state so we - // can update the cached index contents when the index is done. - // - // We also do not index if there is already an index request or if - // the client requested indexing on didChange instead. - // - // TODO: Cancel outgoing index request. Might be tricky to make - // efficient since we have to cancel. - // - we could have an |atomic active_cancellations| variable - // that all of the indexers check before accepting an index. if - // zero we don't slow down fast-path. if non-zero we acquire - // mutex and check to see if we should skip the current request. - // if so, ignore that index response. - // TODO: send as priority request - if (!g_config->index.onDidChange) { - Project::Entry entry = project->FindCompilationEntryForFile(path); - pipeline::Index(entry.filename, entry.args, true); - } - + Project::Entry entry = project->FindCompilationEntryForFile(path); + pipeline::Index(entry.filename, entry.args, IndexMode::Normal); clang_complete->NotifySave(path); } }; diff --git a/src/messages/workspace_didChangeWatchedFiles.cc b/src/messages/workspace_didChangeWatchedFiles.cc index f6f623ce..9fcedcf4 100644 --- a/src/messages/workspace_didChangeWatchedFiles.cc +++ b/src/messages/workspace_didChangeWatchedFiles.cc @@ -50,18 +50,20 @@ struct Handler_WorkspaceDidChangeWatchedFiles continue; entry = project->entries[it->second]; } - bool is_interactive = - working_files->GetFileByFilename(entry.filename) != nullptr; + IndexMode mode = + working_files->GetFileByFilename(entry.filename) != nullptr + ? IndexMode::Normal + : IndexMode::NonInteractive; switch (event.type) { case lsFileChangeType::Created: case lsFileChangeType::Changed: { - pipeline::Index(path, entry.args, is_interactive); - if (is_interactive) + pipeline::Index(path, entry.args, mode); + if (mode == IndexMode::Normal) clang_complete->NotifySave(path); break; } case lsFileChangeType::Deleted: - pipeline::Index(path, entry.args, is_interactive); + pipeline::Index(path, entry.args, mode); break; } } diff --git a/src/pipeline.cc b/src/pipeline.cc index 880b1b42..1f58371f 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -66,7 +66,7 @@ namespace { struct Index_Request { std::string path; std::vector args; - bool is_interactive; + IndexMode mode; lsRequestId id; }; @@ -146,6 +146,7 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files, if (!opt_request) return false; auto &request = *opt_request; + bool loud = request.mode != IndexMode::OnChange; // Dummy one to trigger refresh semantic highlight. if (request.path.empty()) { @@ -156,7 +157,7 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files, } if (std::string reason; !matcher.IsMatch(request.path, &reason)) { - LOG_S(INFO) << "skip " << request.path << " for " << reason; + LOG_IF_S(INFO, loud) << "skip " << request.path << " for " << reason; return false; } @@ -179,6 +180,8 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files, if (!write_time) return true; int reparse = vfs->Stamp(path_to_index, *write_time); + if (g_config->index.onChange) + reparse = 2; if (!vfs->Mark(path_to_index, g_thread_id, 1) && !reparse) return true; @@ -214,21 +217,29 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files, auto dependencies = prev->dependencies; if (reparse) { IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); - on_indexed->PushBack(std::move(update), request.is_interactive); + on_indexed->PushBack(std::move(update), + request.mode != IndexMode::NonInteractive); } for (const auto &dep : dependencies) if (vfs->Mark(dep.first().str(), 0, 2) && (prev = RawCacheLoad(dep.first().str()))) { IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); - on_indexed->PushBack(std::move(update), request.is_interactive); + on_indexed->PushBack(std::move(update), + request.mode != IndexMode::NonInteractive); } return true; } - LOG_S(INFO) << "parse " << path_to_index; + LOG_IF_S(INFO, loud) << "parse " << path_to_index; + std::vector> remapped; + if (g_config->index.onChange) { + std::string content = working_files->GetContent(request.path); + if (content.size()) + remapped.emplace_back(request.path, content); + } auto indexes = - idx::Index(vfs, entry.directory, path_to_index, entry.args, {}); + idx::Index(vfs, entry.directory, path_to_index, entry.args, remapped); if (indexes.empty()) { if (g_config->index.enabled && request.id.Valid()) { @@ -247,16 +258,15 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files, if (!(vfs->Stamp(path, curr->last_write_time) || path == path_to_index)) continue; if (std::string reason; !matcher.IsMatch(path, &reason)) { - LOG_S(INFO) << "skip emitting and storing index of " << path << " for " - << reason; + LOG_IF_S(INFO, loud) << "skip emitting and storing index of " << path << " for " + << reason; continue; } - LOG_S(INFO) << "emit index for " << path; + LOG_IF_S(INFO, loud) << "emit index for " << path; prev = RawCacheLoad(path); // Write current index to disk if requested. - LOG_S(INFO) << "store index for " << path; { std::string cache_path = GetCachePath(path); WriteToFile(cache_path, curr->file_contents); @@ -273,9 +283,11 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files, // Build delta update. IndexUpdate update = IndexUpdate::CreateDelta(prev.get(), curr.get()); - LOG_S(INFO) << "built index for " << path << " (is_delta=" << !!prev << ")"; + LOG_IF_S(INFO, loud) << "store index for " << path << " (delta: " << !!prev + << ")"; - on_indexed->PushBack(std::move(update), request.is_interactive); + on_indexed->PushBack(std::move(update), + request.mode != IndexMode::NonInteractive); } return true; @@ -328,12 +340,14 @@ void Main_OnIndexed(DB *db, SemanticHighlightSymbolCache *semantic_cache, // Update indexed content, skipped ranges, and semantic highlighting. if (update->files_def_update) { auto &def_u = *update->files_def_update; - LOG_S(INFO) << "apply index for " << def_u.first.path; - if (WorkingFile *working_file = + if (WorkingFile *wfile = working_files->GetFileByFilename(def_u.first.path)) { - working_file->SetIndexContent(def_u.second); - EmitSkippedRanges(working_file, def_u.first.skipped_ranges); - EmitSemanticHighlighting(db, semantic_cache, working_file, + // FIXME With index.onChange: true, use buffer_content only for + // request.path + wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content + : def_u.second); + EmitSkippedRanges(wfile, def_u.first.skipped_ranges); + EmitSemanticHighlighting(db, semantic_cache, wfile, &db->files[update->file_id]); } } @@ -480,8 +494,8 @@ void MainLoop() { } void Index(const std::string &path, const std::vector &args, - bool interactive, lsRequestId id) { - index_request->PushBack({path, args, interactive, id}, interactive); + IndexMode mode, lsRequestId id) { + index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive); } std::optional LoadCachedFileContents(const std::string &path) { diff --git a/src/pipeline.hh b/src/pipeline.hh index 7c74e088..9e793976 100644 --- a/src/pipeline.hh +++ b/src/pipeline.hh @@ -29,8 +29,14 @@ class DiagnosticsPublisher { std::vector diagnostics); }; -namespace ccls::pipeline { +namespace ccls { +enum class IndexMode { + NonInteractive, + OnChange, + Normal, +}; +namespace pipeline { void Init(); void LaunchStdin(); void LaunchStdout(); @@ -40,11 +46,10 @@ void Indexer_Main(DiagnosticsPublisher* diag_pub, WorkingFiles* working_files); void MainLoop(); -void Index(const std::string& path, - const std::vector& args, - bool is_interactive, - lsRequestId id = {}); +void Index(const std::string &path, const std::vector &args, + IndexMode mode, lsRequestId id = {}); std::optional LoadCachedFileContents(const std::string& path); void WriteStdout(MethodType method, lsBaseOutMessage& response); } +} diff --git a/src/project.cc b/src/project.cc index 7a4b338f..f5f02bfd 100644 --- a/src/project.cc +++ b/src/project.cc @@ -448,10 +448,12 @@ void Project::ForAllFilteredFiles( void Project::Index(WorkingFiles *wfiles, lsRequestId id) { ForAllFilteredFiles([&](int i, const Project::Entry &entry) { - bool is_interactive = wfiles->GetFileByFilename(entry.filename) != nullptr; - pipeline::Index(entry.filename, entry.args, is_interactive, id); + bool interactive = wfiles->GetFileByFilename(entry.filename) != nullptr; + pipeline::Index(entry.filename, entry.args, + interactive ? IndexMode::Normal : IndexMode::NonInteractive, + id); }); // Dummy request to indicate that project is loaded and // trigger refreshing semantic highlight for all working files. - pipeline::Index("", {}, false); + pipeline::Index("", {}, IndexMode::NonInteractive); }