Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -240,9 +240,8 @@ formatCode(llvm::StringRef Code, PathRef File, ArrayRef Ranges); - void scheduleReparseAndDiags(PathRef File, VersionedDraft Contents, - WantDiagnostics WD, - IntrusiveRefCntPtr FS); + void consumeDiagnostics(PathRef File, DocVersion Version, + std::vector Diags); CompileArgsCache CompileArgs; DiagnosticsConsumer &DiagConsumer; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -118,8 +118,14 @@ void ClangdServer::addDocument(PathRef File, StringRef Contents, WantDiagnostics WantDiags) { DocVersion Version = DraftMgr.updateDraft(File, Contents); - scheduleReparseAndDiags(File, VersionedDraft{Version, Contents.str()}, - WantDiags, FSProvider.getFileSystem()); + ParseInputs Inputs = {CompileArgs.getCompileCommand(File), + FSProvider.getFileSystem(), Contents.str()}; + + Path FileStr = File.str(); + WorkScheduler.update(File, std::move(Inputs), WantDiags, + [this, FileStr, Version](std::vector Diags) { + consumeDiagnostics(FileStr, Version, std::move(Diags)); + }); } void ClangdServer::removeDocument(PathRef File) { @@ -129,16 +135,18 @@ } void ClangdServer::forceReparse(PathRef File) { - auto FileContents = DraftMgr.getDraft(File); - assert(FileContents.Draft && - "forceReparse() was called for non-added document"); - // forceReparse promises to request new compilation flags from CDB, so we // remove any cahced flags. CompileArgs.invalidate(File); - scheduleReparseAndDiags(File, std::move(FileContents), WantDiagnostics::Yes, - FSProvider.getFileSystem()); + tooling::CompileCommand NewCommand = CompileArgs.getCompileCommand(File); + DocVersion Version = DraftMgr.getVersion(File); + Path FileStr = File.str(); + WorkScheduler.updateCompileCommand( + File, std::move(NewCommand), FSProvider.getFileSystem(), + WantDiagnostics::Yes, [this, FileStr, Version](std::vector Diags) { + consumeDiagnostics(FileStr, Version, std::move(Diags)); + }); } void ClangdServer::codeComplete(PathRef File, Position Pos, @@ -499,38 +507,26 @@ WorkScheduler.runWithAST("Hover", File, Bind(Action, std::move(CB))); } -void ClangdServer::scheduleReparseAndDiags( - PathRef File, VersionedDraft Contents, WantDiagnostics WantDiags, - IntrusiveRefCntPtr FS) { - tooling::CompileCommand Command = CompileArgs.getCompileCommand(File); - - DocVersion Version = Contents.Version; - Path FileStr = File.str(); - - auto Callback = [this, Version, FileStr](std::vector Diags) { - // We need to serialize access to resulting diagnostics to avoid calling - // `onDiagnosticsReady` in the wrong order. - std::lock_guard DiagsLock(DiagnosticsMutex); - DocVersion &LastReportedDiagsVersion = ReportedDiagnosticVersions[FileStr]; - // FIXME(ibiryukov): get rid of '<' comparison here. In the current - // implementation diagnostics will not be reported after version counters' - // overflow. This should not happen in practice, since DocVersion is a - // 64-bit unsigned integer. - if (Version < LastReportedDiagsVersion) - return; - LastReportedDiagsVersion = Version; - - DiagConsumer.onDiagnosticsReady(FileStr, std::move(Diags)); - }; - - WorkScheduler.update(File, - ParseInputs{std::move(Command), std::move(FS), - std::move(*Contents.Draft)}, - WantDiags, std::move(Callback)); +void ClangdServer::consumeDiagnostics(PathRef File, DocVersion Version, + std::vector Diags) { + // We need to serialize access to resulting diagnostics to avoid calling + // `onDiagnosticsReady` in the wrong order. + std::lock_guard DiagsLock(DiagnosticsMutex); + DocVersion &LastReportedDiagsVersion = ReportedDiagnosticVersions[File]; + + // FIXME(ibiryukov): get rid of '<' comparison here. In the current + // implementation diagnostics will not be reported after version counters' + // overflow. This should not happen in practice, since DocVersion is a + // 64-bit unsigned integer. + if (Version < LastReportedDiagsVersion) + return; + LastReportedDiagsVersion = Version; + + DiagConsumer.onDiagnosticsReady(File, std::move(Diags)); } void ClangdServer::reparseOpenedFiles() { - for (const Path &FilePath : DraftMgr.getActiveFiles()) + for (const Path &FilePath : WorkScheduler.getTrackedFiles()) forceReparse(FilePath); } Index: clangd/TUScheduler.h =================================================================== --- clangd/TUScheduler.h +++ clangd/TUScheduler.h @@ -59,6 +59,18 @@ /// The order of results is unspecified. std::vector> getUsedBytesPerFile() const; + /// Returns a list of currently tracked files. File starts being trakced on + /// first update() call to it and stops being tracked on remove() call. + std::vector getTrackedFiles() const; + + /// Schedule a reparse for \p File with a new compile command. File must be + /// tracked. + /// FIXME: remove the callback from this function + void updateCompileCommand(PathRef File, tooling::CompileCommand NewCommand, + IntrusiveRefCntPtr FS, + WantDiagnostics WantDiags, + UniqueFunction)> OnUpdated); + /// Schedule an update for \p File. Adds \p File to a list of tracked files if /// \p File was not part of it before. /// FIXME(ibiryukov): remove the callback from this function. Index: clangd/TUScheduler.cpp =================================================================== --- clangd/TUScheduler.cpp +++ clangd/TUScheduler.cpp @@ -110,6 +110,9 @@ Deadline scheduleLocked(); /// Should the first task in the queue be skipped instead of run? bool shouldSkipHeadLocked() const; + /// Rebuilds AST and pushes the new diagnostics, if required. + void rebuildASTLocked(WantDiagnostics WantDiags, + UniqueFunction)> OnUpdated); struct Request { UniqueFunction Action; @@ -213,20 +216,8 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags, UniqueFunction)> OnUpdated) { auto Task = [=](decltype(OnUpdated) OnUpdated) mutable { - FileInputs = Inputs; - auto Diags = AST.rebuild(std::move(Inputs)); - - { - std::lock_guard Lock(Mutex); - if (AST.getPreamble()) - LastBuiltPreamble = AST.getPreamble(); - LastASTSize = AST.getUsedBytes(); - } - // We want to report the diagnostics even if this update was cancelled. - // It seems more useful than making the clients wait indefinitely if they - // spam us with updates. - if (Diags && WantDiags != WantDiagnostics::No) - OnUpdated(std::move(*Diags)); + FileInputs = std::move(Inputs); + rebuildASTLocked(WantDiags, std::move(OnUpdated)); }; startTask("Update", Bind(Task, std::move(OnUpdated)), WantDiags); @@ -388,6 +379,23 @@ llvm_unreachable("Unknown WantDiagnostics"); } +void ASTWorker::rebuildASTLocked( + WantDiagnostics WantDiags, + UniqueFunction)> OnUpdated) { + auto Diags = AST.rebuild(ParseInputs(FileInputs)); + { + std::lock_guard Lock(Mutex); + if (AST.getPreamble()) + LastBuiltPreamble = AST.getPreamble(); + LastASTSize = AST.getUsedBytes(); + } + // We want to report the diagnostics even if this update was cancelled. + // It seems more useful than making the clients wait indefinitely if they + // spam us with updates. + if (Diags && WantDiags != WantDiagnostics::No) + OnUpdated(std::move(*Diags)); +} + bool ASTWorker::blockUntilIdle(Deadline Timeout) const { std::unique_lock Lock(Mutex); return wait(Lock, RequestsCV, Timeout, [&] { return Requests.empty(); }); @@ -446,6 +454,20 @@ return true; } +void TUScheduler::updateCompileCommand( + PathRef File, tooling::CompileCommand NewCommand, + IntrusiveRefCntPtr FS, WantDiagnostics WantDiags, + UniqueFunction)> OnUpdated) { + auto FDIt = Files.find(File); + assert(FDIt != Files.end() && + "calling updateCompileCommand on non-tracked file"); + + FDIt->second->Inputs.CompileCommand = std::move(NewCommand); + FDIt->second->Inputs.FS = std::move(FS); + FDIt->second->Worker->update(FDIt->second->Inputs, WantDiags, + std::move(OnUpdated)); +} + void TUScheduler::update(PathRef File, ParseInputs Inputs, WantDiagnostics WantDiags, UniqueFunction)> OnUpdated) { @@ -533,5 +555,9 @@ return Result; } +std::vector TUScheduler::getTrackedFiles() const { + return std::vector(Files.keys().begin(), Files.keys().end()); +} + } // namespace clangd } // namespace clang