From bf698b85d4875fb16c5465eb038e476d970d6c5a Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sat, 22 Sep 2018 01:37:00 -0700 Subject: [PATCH] Publish diagnostics of inferred files and change diagnostics.{onChange,onOpen,onSave} from bool to debounce time --- src/clang_complete.cc | 258 +++++++++--------- src/clang_complete.hh | 31 +-- src/config.h | 26 +- src/message_handler.h | 2 - src/messages/ccls_reload.cc | 2 + src/messages/initialize.cc | 1 - src/messages/textDocument_didChange.cc | 5 +- src/messages/textDocument_didClose.cc | 2 +- src/messages/textDocument_didOpen.cc | 6 +- .../workspace_didChangeWatchedFiles.cc | 4 +- src/pipeline.cc | 43 +-- src/pipeline.hh | 12 - src/project.cc | 9 - src/utils.cc | 9 + src/utils.h | 3 + 15 files changed, 185 insertions(+), 228 deletions(-) diff --git a/src/clang_complete.cc b/src/clang_complete.cc index 3fba8924..e3f814e8 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -6,6 +6,7 @@ #include "clang_utils.h" #include "filesystem.hh" #include "log.hh" +#include "match.h" #include "platform.h" #include @@ -20,7 +21,10 @@ using namespace clang; using namespace llvm; #include +#include +#include #include +namespace chrono = std::chrono; namespace ccls { namespace { @@ -68,22 +72,34 @@ class StoreDiags : public DiagnosticConsumer { const LangOptions *LangOpts; std::optional last; std::vector output; + std::string path; + std::unordered_map FID2concerned; void Flush() { if (!last) return; - bool mentions = last->inside_main || last->edits.size(); + bool mentions = last->concerned || last->edits.size(); if (!mentions) for (auto &N : last->notes) - if (N.inside_main) + if (N.concerned) mentions = true; if (mentions) output.push_back(std::move(*last)); last.reset(); } public: + StoreDiags(std::string path) : path(std::move(path)) {} std::vector Take() { return std::move(output); } + bool IsConcerned(const SourceManager &SM, SourceLocation L) { + FileID FID = SM.getFileID(L); + auto it = FID2concerned.try_emplace(FID.getHashValue()); + if (it.second) { + const FileEntry *FE = SM.getFileEntryForID(FID); + it.first->second = FE && FileName(*FE) == path; + } + return it.first->second; + } void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override { LangOpts = &Opts; } @@ -96,24 +112,25 @@ public: SourceLocation L = Info.getLocation(); if (!L.isValid()) return; const SourceManager &SM = Info.getSourceManager(); - bool inside_main = SM.isInMainFile(L); + StringRef Filename = SM.getFilename(Info.getLocation()); + bool concerned = IsConcerned(SM, Info.getLocation()); auto fillDiagBase = [&](DiagBase &d) { llvm::SmallString<64> Message; Info.FormatDiagnostic(Message); d.range = FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts)); d.message = Message.str(); - d.inside_main = inside_main; - d.file = SM.getFilename(Info.getLocation()); + d.concerned = concerned; + d.file = Filename; d.level = Level; d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); }; auto addFix = [&](bool SyntheticMessage) -> bool { - if (!inside_main) + if (!concerned) return false; for (const FixItHint &FixIt : Info.getFixItHints()) { - if (!SM.isInMainFile(FixIt.RemoveRange.getBegin())) + if (!IsConcerned(SM, FixIt.RemoveRange.getBegin())) return false; lsTextEdit edit; edit.newText = FixIt.CodeToInsert; @@ -146,9 +163,10 @@ std::unique_ptr BuildCompilerInstance( CompletionSession &session, std::unique_ptr CI, DiagnosticConsumer &DC, const WorkingFiles::Snapshot &snapshot, std::vector> &Bufs) { + std::string main = ResolveIfRelative(session.file.directory, CI->getFrontendOpts().Inputs[0].getFile()); for (auto &file : snapshot.files) { Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content)); - if (file.filename == session.file.filename) { + if (file.filename == main) if (auto Preamble = session.GetPreamble()) { #if LLVM_VERSION_MAJOR >= 7 Preamble->Preamble.OverridePreamble(*CI, session.FS, @@ -157,14 +175,10 @@ std::unique_ptr BuildCompilerInstance( Preamble->Preamble.AddImplicitPreamble(*CI, session.FS, Bufs.back().get()); #endif - } else { - CI->getPreprocessorOpts().addRemappedFile( - CI->getFrontendOpts().Inputs[0].getFile(), Bufs.back().get()); + continue; } - } else { - CI->getPreprocessorOpts().addRemappedFile(file.filename, - Bufs.back().get()); - } + CI->getPreprocessorOpts().addRemappedFile(file.filename, + Bufs.back().get()); } auto Clang = std::make_unique(session.PCH); @@ -190,52 +204,53 @@ bool Parse(CompilerInstance &Clang) { void CompletionPreloadMain(CompletionManager *manager) { while (true) { - // Fetching the completion request blocks until we have a request. auto request = manager->preload_requests_.Dequeue(); - // If we don't get a session then that means we don't care about the file - // anymore - abandon the request. - std::shared_ptr session = manager->TryGetSession( - request.path, false /*mark_as_completion*/, false /*create_if_needed*/); + bool is_open = false; + std::shared_ptr session = + manager->TryGetSession(request.path, true, &is_open); if (!session) continue; - const auto &args = session->file.args; - WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot( - {StripFileType(session->file.filename)}); - - LOG_S(INFO) << "create completion session for " << session->file.filename; - if (std::unique_ptr CI = - BuildCompilerInvocation(args, session->FS)) - session->BuildPreamble(*CI); - if (g_config->diagnostics.onSave) { + // 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); + } + int debounce = + is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave; + if (debounce >= 0) { lsTextDocumentIdentifier document; document.uri = lsDocumentUri::FromPath(request.path); - manager->diagnostic_request_.PushBack({document}, true); + manager->DiagnosticsUpdate(request.path, debounce); } } } -void CompletionMain(CompletionManager *completion_manager) { +void CompletionMain(CompletionManager *manager) { while (true) { // Fetching the completion request blocks until we have a request. std::unique_ptr request = - completion_manager->completion_request_.Dequeue(); + manager->completion_request_.Dequeue(); // Drop older requests if we're not buffering. while (g_config->completion.dropOldRequests && - !completion_manager->completion_request_.IsEmpty()) { - completion_manager->on_dropped_(request->id); + !manager->completion_request_.IsEmpty()) { + manager->on_dropped_(request->id); request->Consumer.reset(); request->on_complete(nullptr); - request = completion_manager->completion_request_.Dequeue(); + request = manager->completion_request_.Dequeue(); } std::string path = request->document.uri.GetPath(); std::shared_ptr session = - completion_manager->TryGetSession(path, true /*mark_as_completion*/, - true /*create_if_needed*/); + manager->TryGetSession(path, false); std::unique_ptr CI = BuildCompilerInvocation(session->file.args, session->FS); @@ -251,7 +266,7 @@ void CompletionMain(CompletionManager *completion_manager) { DiagnosticConsumer DC; WorkingFiles::Snapshot snapshot = - completion_manager->working_files_->AsSnapshot({StripFileType(path)}); + manager->working_files_->AsSnapshot({StripFileType(path)}); std::vector> Bufs; auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs); if (!Clang) @@ -285,25 +300,31 @@ llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) { } void printDiag(llvm::raw_string_ostream &OS, const DiagBase &d) { - if (d.inside_main) + if (d.concerned) OS << llvm::sys::path::filename(d.file); else OS << d.file; auto pos = d.range.start; OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":" - << (d.inside_main ? " " : "\n"); + << (d.concerned ? " " : "\n"); OS << diagLeveltoString(d.level) << ": " << d.message; } void DiagnosticMain(CompletionManager *manager) { while (true) { - // Fetching the completion request blocks until we have a request. CompletionManager::DiagnosticRequest request = manager->diagnostic_request_.Dequeue(); - std::string path = request.document.uri.GetPath(); + const std::string &path = request.path; + int64_t wait = request.wait_until - + chrono::duration_cast( + chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + if (wait > 0) + std::this_thread::sleep_for(chrono::duration( + std::min(wait, request.debounce))); - std::shared_ptr session = manager->TryGetSession( - path, true /*mark_as_completion*/, true /*create_if_needed*/); + std::shared_ptr session = + manager->TryGetSession(path, false); std::unique_ptr CI = BuildCompilerInvocation(session->file.args, session->FS); @@ -311,7 +332,7 @@ void DiagnosticMain(CompletionManager *manager) { continue; CI->getDiagnosticOpts().IgnoreWarnings = false; CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking; - StoreDiags DC; + StoreDiags DC(path); WorkingFiles::Snapshot snapshot = manager->working_files_->AsSnapshot({StripFileType(path)}); std::vector> Bufs; @@ -347,9 +368,12 @@ void DiagnosticMain(CompletionManager *manager) { return ret; }; + std::vector diags = DC.Take(); + if (std::shared_ptr preamble = session->GetPreamble()) + diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end()); std::vector ls_diags; - for (auto &d : DC.Take()) { - if (!d.inside_main) + for (auto &d : diags) { + if (!d.concerned) continue; std::string buf; llvm::raw_string_ostream OS(buf); @@ -364,7 +388,7 @@ void DiagnosticMain(CompletionManager *manager) { OS.flush(); ls_diag.message = std::move(buf); for (auto &n : d.notes) { - if (!n.inside_main) + if (!n.concerned) continue; lsDiagnostic &ls_diag1 = ls_diags.emplace_back(); Fill(n, ls_diag1); @@ -374,6 +398,12 @@ void DiagnosticMain(CompletionManager *manager) { ls_diag1.message = std::move(buf); } } + + { + std::lock_guard lock(session->wfiles->files_mutex); + if (WorkingFile *wfile = session->wfiles->GetFileByFilenameNoLock(path)) + wfile->diagnostics_ = ls_diags; + } manager->on_diagnostic_(path, ls_diags); } } @@ -385,9 +415,10 @@ std::shared_ptr CompletionSession::GetPreamble() { return preamble; } -void CompletionSession::BuildPreamble(CompilerInvocation &CI) { +void CompletionSession::BuildPreamble(CompilerInvocation &CI, + const std::string &main) { std::shared_ptr OldP = GetPreamble(); - std::string content = wfiles->GetContent(file.filename); + std::string content = wfiles->GetContent(main); std::unique_ptr Buf = llvm::MemoryBuffer::getMemBuffer(content); auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0); @@ -401,7 +432,7 @@ void CompletionSession::BuildPreamble(CompilerInvocation &CI) { CI.getPreprocessorOpts().WriteCommentListToPCH = false; #endif - StoreDiags DC; + StoreDiags DC(main); IntrusiveRefCntPtr DE = CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false); PreambleCallbacks PP; @@ -421,8 +452,8 @@ CompletionManager::CompletionManager(Project *project, OnDropped on_dropped) : project_(project), working_files_(working_files), on_diagnostic_(on_diagnostic), on_dropped_(on_dropped), - preloaded_sessions_(kMaxPreloadedSessions), - completion_sessions_(kMaxCompletionSessions), + preloads(kMaxPreloadedSessions), + sessions(kMaxCompletionSessions), PCH(std::make_shared()) { std::thread([&]() { set_thread_name("comp"); @@ -441,22 +472,28 @@ CompletionManager::CompletionManager(Project *project, .detach(); } -void CompletionManager::CodeComplete( - const lsRequestId &id, - const lsTextDocumentPositionParams &completion_location, - const OnComplete &on_complete) { -} - -void CompletionManager::DiagnosticsUpdate( - const lsTextDocumentIdentifier &document) { - bool has = false; - diagnostic_request_.Iterate([&](const DiagnosticRequest &request) { - if (request.document.uri == document.uri) - has = true; - }); - if (!has) - diagnostic_request_.PushBack(DiagnosticRequest{document}, - true /*priority*/); +void CompletionManager::DiagnosticsUpdate(const std::string &path, + int debounce) { + static GroupMatch match(g_config->diagnostics.whitelist, + g_config->diagnostics.blacklist); + if (!match.IsMatch(path)) + return; + int64_t now = chrono::duration_cast( + chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + bool flag = false; + { + std::lock_guard lock(diag_mutex); + int64_t &next = next_diag[path]; + auto &d = g_config->diagnostics; + if (next <= now || + now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) { + next = now + debounce; + flag = true; + } + } + if (flag) + diagnostic_request_.PushBack({path, now + debounce, debounce}, false); } void CompletionManager::NotifyView(const std::string &path) { @@ -466,94 +503,67 @@ void CompletionManager::NotifyView(const std::string &path) { } void CompletionManager::NotifySave(const std::string &filename) { - // - // On save, always reparse. - // - EnsureCompletionOrCreatePreloadSession(filename); preload_requests_.PushBack(PreloadRequest{filename}, true); } -void CompletionManager::NotifyClose(const std::string &filename) { - // - // On close, we clear any existing CompletionSession instance. - // - +void CompletionManager::OnClose(const std::string &filename) { std::lock_guard lock(sessions_lock_); - - // Take and drop. It's okay if we don't actually drop the file, it'll - // eventually get pushed out of the caches as the user opens other files. - auto preloaded_ptr = preloaded_sessions_.TryTake(filename); - LOG_IF_S(INFO, !!preloaded_ptr) - << "Dropped preloaded-based code completion session for " << filename; - auto completion_ptr = completion_sessions_.TryTake(filename); - LOG_IF_S(INFO, !!completion_ptr) - << "Dropped completion-based code completion session for " << filename; - - // We should never have both a preloaded and completion session. - assert((preloaded_ptr && completion_ptr) == false); + preloads.TryTake(filename); + sessions.TryTake(filename); } bool CompletionManager::EnsureCompletionOrCreatePreloadSession( const std::string &path) { std::lock_guard lock(sessions_lock_); - - // Check for an existing CompletionSession. - if (preloaded_sessions_.TryGet(path) || - completion_sessions_.TryGet(path)) { + if (preloads.TryGet(path) || sessions.TryGet(path)) return false; - } // No CompletionSession, create new one. auto session = std::make_shared( project_->FindCompilationEntryForFile(path), working_files_, PCH); - preloaded_sessions_.Insert(session->file.filename, session); + if (session->file.filename != path) { + session->inferred = true; + session->file.filename = path; + } + preloads.Insert(path, session); + LOG_S(INFO) << "create preload session for " << path; return true; } std::shared_ptr -CompletionManager::TryGetSession(const std::string &path, - bool mark_as_completion, - bool create_if_needed) { +CompletionManager::TryGetSession(const std::string &path, bool preload, + bool *is_open) { std::lock_guard lock(sessions_lock_); + std::shared_ptr session = preloads.TryGet(path); - // Try to find a preloaded session. - std::shared_ptr preloaded = - preloaded_sessions_.TryGet(path); - - if (preloaded) { - // If this request is for a completion, we should move it to - // |completion_sessions|. - if (mark_as_completion) { - preloaded_sessions_.TryTake(path); - completion_sessions_.Insert(path, preloaded); + if (session) { + if (!preload) { + preloads.TryTake(path); + sessions.Insert(path, session); + if (is_open) + *is_open = true; } - return preloaded; + return session; } - // Try to find a completion session. If none create one. - std::shared_ptr session = - completion_sessions_.TryGet(path); - if (!session && create_if_needed) { + session = sessions.TryGet(path); + if (!session && !preload) { session = std::make_shared( project_->FindCompilationEntryForFile(path), working_files_, PCH); - completion_sessions_.Insert(path, session); + sessions.Insert(path, session); + LOG_S(INFO) << "create session for " << path; + if (is_open) + *is_open = true; } return session; } -void CompletionManager::FlushSession(const std::string &path) { - std::lock_guard lock(sessions_lock_); - - preloaded_sessions_.TryTake(path); - completion_sessions_.TryTake(path); -} - void CompletionManager::FlushAllSessions() { LOG_S(INFO) << "flush all clang complete sessions"; std::lock_guard lock(sessions_lock_); - preloaded_sessions_.Clear(); - completion_sessions_.Clear(); + preloads.Clear(); + sessions.Clear(); } diff --git a/src/clang_complete.hh b/src/clang_complete.hh index f22dd849..677bc204 100644 --- a/src/clang_complete.hh +++ b/src/clang_complete.hh @@ -27,7 +27,7 @@ struct DiagBase { std::string file; clang::DiagnosticsEngine::Level level = clang::DiagnosticsEngine::Note; unsigned category; - bool inside_main = false; + bool concerned = false; }; struct Note : DiagBase {}; struct Diag : DiagBase { @@ -46,10 +46,10 @@ struct CompletionSession : public std::enable_shared_from_this { std::mutex mutex; std::shared_ptr preamble; - std::vector diags; Project::Entry file; WorkingFiles *wfiles; + bool inferred = false; // TODO share llvm::IntrusiveRefCntPtr FS = @@ -61,7 +61,7 @@ struct CompletionSession : file(file), wfiles(wfiles), PCH(PCH) {} std::shared_ptr GetPreamble(); - void BuildPreamble(clang::CompilerInvocation &CI); + void BuildPreamble(clang::CompilerInvocation &CI, const std::string &main); }; } @@ -95,19 +95,16 @@ struct CompletionManager { OnComplete on_complete; }; struct DiagnosticRequest { - lsTextDocumentIdentifier document; + std::string path; + int64_t wait_until; + int64_t debounce; }; CompletionManager(Project *project, WorkingFiles *working_files, OnDiagnostic on_diagnostic, OnDropped on_dropped); - // Start a code completion at the given location. |on_complete| will run when - // completion results are available. |on_complete| may run on any thread. - void CodeComplete(const lsRequestId &request_id, - const lsTextDocumentPositionParams &completion_location, - const OnComplete &on_complete); // Request a diagnostics update. - void DiagnosticsUpdate(const lsTextDocumentIdentifier &document); + void DiagnosticsUpdate(const std::string &path, int debounce); // Notify the completion manager that |filename| has been viewed and we // should begin preloading completion data. @@ -117,7 +114,7 @@ struct CompletionManager { void NotifySave(const std::string &path); // Notify the completion manager that |filename| has been closed. Any existing // completion session will be dropped. - void NotifyClose(const std::string &path); + void OnClose(const std::string &path); // Ensures there is a completion or preloaded session. Returns true if a new // session was created. @@ -125,11 +122,8 @@ struct CompletionManager { // Tries to find an edit session for |filename|. This will move the session // from view to edit. std::shared_ptr - TryGetSession(const std::string &path, bool mark_as_completion, - bool create_if_needed); + TryGetSession(const std::string &path, bool preload, bool *is_open = nullptr); - // Flushes all saved sessions with the supplied filename - void FlushSession(const std::string &path); // Flushes all saved sessions void FlushAllSessions(void); @@ -147,14 +141,17 @@ struct CompletionManager { // CompletionSession instances which are preloaded, ie, files which the user // has viewed but not requested code completion for. - LruSessionCache preloaded_sessions_; + LruSessionCache preloads; // CompletionSession instances which the user has actually performed // completion on. This is more rare so these instances tend to stay alive // much longer than the ones in |preloaded_sessions_|. - LruSessionCache completion_sessions_; + LruSessionCache sessions; // Mutex which protects |view_sessions_| and |edit_sessions_|. std::mutex sessions_lock_; + std::mutex diag_mutex; + std::unordered_map next_diag; + // Request a code completion at the given location. ThreadedQueue> completion_request_; ThreadedQueue diagnostic_request_; diff --git a/src/config.h b/src/config.h index fc0d0166..2c5352c9 100644 --- a/src/config.h +++ b/src/config.h @@ -144,20 +144,18 @@ struct Config { // blacklisted files. std::vector blacklist; - // How often should ccls publish diagnostics in completion? - // -1: never - // 0: as often as possible - // xxx: at most every xxx milliseconds - int frequencyMs = 0; + // Time to wait before computing diagnostics for textDocument/didChange. + // -1: disable diagnostics on change + // 0: immediately + // positive (e.g. 500): wait for 500 milliseconds. didChange requests in + // this period of time will only cause one computation. + int onChange = 1000; - // If true, diagnostics will be reported for textDocument/didChange. - bool onChange = true; + // Time to wait before computing diagnostics for textDocument/didOpen. + int onOpen = 0; - // If true, diagnostics will be reported for textDocument/didOpen. - bool onOpen = true; - - // If true, diagnostics will be reported for textDocument/didSave. - bool onSave = true; + // Time to wait before computing diagnostics for textDocument/didSave. + int onSave = 0; bool spellChecking = true; @@ -246,8 +244,8 @@ MAKE_REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel, dropOldRequests, duplicateOptional, filterAndSort, includeBlacklist, includeMaxPathSize, includeSuffixWhitelist, includeWhitelist); -MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange, - onOpen, onSave, spellChecking, whitelist) +MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave, + spellChecking, whitelist) MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist) MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion, multiVersionBlacklist, multiVersionWhitelist, onChange, diff --git a/src/message_handler.h b/src/message_handler.h index 801761c2..e4e51c9c 100644 --- a/src/message_handler.h +++ b/src/message_handler.h @@ -15,7 +15,6 @@ struct CompletionManager; struct Config; -class DiagnosticsPublisher; struct GroupMatch; struct VFS; struct IncludeComplete; @@ -78,7 +77,6 @@ struct MessageHandler { DB *db = nullptr; MultiQueueWaiter *waiter = nullptr; Project *project = nullptr; - DiagnosticsPublisher *diag_pub = nullptr; VFS *vfs = nullptr; SemanticHighlight *highlight = nullptr; WorkingFiles *working_files = nullptr; diff --git a/src/messages/ccls_reload.cc b/src/messages/ccls_reload.cc index 55963e81..4f2f35d0 100644 --- a/src/messages/ccls_reload.cc +++ b/src/messages/ccls_reload.cc @@ -1,6 +1,7 @@ // Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 +#include "clang_complete.hh" #include "match.h" #include "message_handler.h" #include "pipeline.hh" @@ -38,6 +39,7 @@ struct Handler_CclsReload : BaseMessageHandler { vfs->Clear(); db->clear(); project->Index(working_files, lsRequestId()); + clang_complete->FlushAllSessions(); return; } diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 23c4bf24..5ba4caad 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -474,7 +474,6 @@ struct Handler_Initialize : BaseMessageHandler { sys::fs::create_directories(g_config->cacheDirectory + '@' + escaped); } - diag_pub->Init(); idx::Init(); highlight->Init(); diff --git a/src/messages/textDocument_didChange.cc b/src/messages/textDocument_didChange.cc index 53978df8..a91665f6 100644 --- a/src/messages/textDocument_didChange.cc +++ b/src/messages/textDocument_didChange.cc @@ -30,9 +30,8 @@ struct Handler_TextDocumentDidChange if (g_config->index.onChange) pipeline::Index(path, {}, IndexMode::OnChange); clang_complete->NotifyView(path); - if (g_config->diagnostics.onChange) - clang_complete->DiagnosticsUpdate( - params.textDocument.AsTextDocumentIdentifier()); + if (g_config->diagnostics.onChange >= 0) + clang_complete->DiagnosticsUpdate(path, g_config->diagnostics.onChange); } }; REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidChange); diff --git a/src/messages/textDocument_didClose.cc b/src/messages/textDocument_didClose.cc index 2509e20d..62645c88 100644 --- a/src/messages/textDocument_didClose.cc +++ b/src/messages/textDocument_didClose.cc @@ -35,7 +35,7 @@ struct Handler_TextDocumentDidClose // Remove internal state. working_files->OnClose(request->params.textDocument); - clang_complete->NotifyClose(path); + clang_complete->OnClose(path); } }; REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidClose); diff --git a/src/messages/textDocument_didOpen.cc b/src/messages/textDocument_didOpen.cc index 7721b21e..f67c9008 100644 --- a/src/messages/textDocument_didOpen.cc +++ b/src/messages/textDocument_didOpen.cc @@ -59,14 +59,10 @@ struct Handler_TextDocumentDidOpen project->SetArgsForFile(args, path); // Submit new index request if it is not a header file. - if (SourceFileLanguage(path) != LanguageId::Unknown) { + if (SourceFileLanguage(path) != LanguageId::Unknown) pipeline::Index(path, args, IndexMode::Normal); - clang_complete->FlushSession(path); - } clang_complete->NotifyView(path); - if (g_config->diagnostics.onOpen) - clang_complete->DiagnosticsUpdate({params.textDocument.uri}); } }; REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen); diff --git a/src/messages/workspace_didChangeWatchedFiles.cc b/src/messages/workspace_didChangeWatchedFiles.cc index 19bf28ea..90c8e600 100644 --- a/src/messages/workspace_didChangeWatchedFiles.cc +++ b/src/messages/workspace_didChangeWatchedFiles.cc @@ -52,12 +52,12 @@ struct Handler_WorkspaceDidChangeWatchedFiles if (mode == IndexMode::Normal) clang_complete->NotifySave(path); else - clang_complete->FlushSession(path); + clang_complete->OnClose(path); break; } case lsFileChangeType::Deleted: pipeline::Index(path, {}, mode); - clang_complete->FlushSession(path); + clang_complete->OnClose(path); break; } } diff --git a/src/pipeline.cc b/src/pipeline.cc index 4a885520..f5d763ac 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -27,41 +27,6 @@ using namespace llvm; #include #endif -void DiagnosticsPublisher::Init() { - frequencyMs_ = g_config->diagnostics.frequencyMs; - match_ = std::make_unique(g_config->diagnostics.whitelist, - g_config->diagnostics.blacklist); -} - -void DiagnosticsPublisher::Publish(WorkingFiles *working_files, - std::string path, - std::vector diagnostics) { - bool good = true; - // Cache diagnostics so we can show fixits. - working_files->DoActionOnFile(path, [&](WorkingFile *working_file) { - if (working_file) { - good = working_file->diagnostics_.empty(); - working_file->diagnostics_ = diagnostics; - } - }); - - int64_t now = - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - if (frequencyMs_ >= 0 && - (nextPublish_ <= now || (!good && diagnostics.empty())) && - match_->IsMatch(path)) { - nextPublish_ = now + frequencyMs_; - - Out_TextDocumentPublishDiagnostics out; - out.params.uri = lsDocumentUri::FromPath(path); - out.params.diagnostics = diagnostics; - ccls::pipeline::WriteStdout(kMethodType_TextDocumentPublishDiagnostics, - out); - } -} - void VFS::Clear() { std::lock_guard lock(mutex); state.clear(); @@ -485,12 +450,15 @@ void MainLoop() { SemanticHighlight highlight; WorkingFiles working_files; VFS vfs; - DiagnosticsPublisher diag_pub; CompletionManager clang_complete( &project, &working_files, [&](std::string path, std::vector diagnostics) { - diag_pub.Publish(&working_files, path, diagnostics); + Out_TextDocumentPublishDiagnostics out; + out.params.uri = lsDocumentUri::FromPath(path); + out.params.diagnostics = diagnostics; + ccls::pipeline::WriteStdout(kMethodType_TextDocumentPublishDiagnostics, + out); }, [](lsRequestId id) { if (id.Valid()) { @@ -511,7 +479,6 @@ void MainLoop() { handler->db = &db; handler->waiter = indexer_waiter; handler->project = &project; - handler->diag_pub = &diag_pub; handler->vfs = &vfs; handler->highlight = &highlight; handler->working_files = &working_files; diff --git a/src/pipeline.hh b/src/pipeline.hh index 610fde24..ed00dda8 100644 --- a/src/pipeline.hh +++ b/src/pipeline.hh @@ -19,18 +19,6 @@ struct Project; struct WorkingFiles; struct lsBaseOutMessage; -class DiagnosticsPublisher { - std::unique_ptr match_; - int64_t nextPublish_ = 0; - int frequencyMs_; - - public: - void Init(); - void Publish(WorkingFiles* working_files, - std::string path, - std::vector diagnostics); -}; - struct VFS { struct State { int64_t timestamp; diff --git a/src/project.cc b/src/project.cc index 90363338..681e8d97 100644 --- a/src/project.cc +++ b/src/project.cc @@ -54,15 +54,6 @@ enum OptionClass { Separate, }; -std::string ResolveIfRelative(const std::string &directory, - const std::string &path) { - if (sys::path::is_absolute(path)) - return path; - SmallString<256> Ret; - sys::path::append(Ret, directory, path); - return NormalizePath(Ret.str()); -} - struct ProjectProcessor { ProjectConfig *config; std::unordered_set command_set; diff --git a/src/utils.cc b/src/utils.cc index 9dfd7bec..2cdaa9ad 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -105,6 +105,15 @@ std::string EscapeFileName(std::string path) { return path; } +std::string ResolveIfRelative(const std::string &directory, + const std::string &path) { + if (sys::path::is_absolute(path)) + return path; + SmallString<256> Ret; + sys::path::append(Ret, directory, path); + return NormalizePath(Ret.str()); +} + std::optional LastWriteTime(const std::string &path) { sys::fs::file_status Status; if (sys::fs::status(path, Status)) diff --git a/src/utils.h b/src/utils.h index 61c50dfd..dacef627 100644 --- a/src/utils.h +++ b/src/utils.h @@ -58,6 +58,9 @@ void EnsureEndsInSlash(std::string &path); // e.g. foo/bar.c => foo_bar.c std::string EscapeFileName(std::string path); +std::string ResolveIfRelative(const std::string &directory, + const std::string &path); + std::optional LastWriteTime(const std::string &path); std::optional ReadContent(const std::string &filename); void WriteToFile(const std::string &filename, const std::string &content);