Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -192,10 +192,13 @@ std::future addDocument(PathRef File, StringRef Contents); /// Remove \p File from list of tracked files, schedule a request to free /// resources associated with it. - /// \return A future that will become ready the file is removed and all - /// associated reosources are freed. + /// \return A future that will become ready when the file is removed and all + /// associated resources are freed. std::future removeDocument(PathRef File); /// Force \p File to be reparsed using the latest contents. + /// Will also check if CompileCommand, provided by GlobalCompilationDatabase + /// for \p File has changed. If it has, will remove currently stored Preamble + /// and AST and rebuild them from scratch. std::future forceReparse(PathRef File); /// Run code completion for \p File at \p Pos. If \p OverridenContents is not Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -154,9 +154,20 @@ } std::future ClangdServer::forceReparse(PathRef File) { - // The addDocument schedules the reparse even if the contents of the file - // never changed, so we just call it here. - return addDocument(File, getDocument(File)); + auto FileContents = DraftMgr.getDraft(File); + assert(FileContents.Draft && + "forceReparse() was called for non-added document"); + + auto TaggedFS = FSProvider.getTaggedFileSystem(File); + auto Recreated = Units.recreateFileIfCompileCommandChanged( + File, ResourceDir, CDB, PCHs, TaggedFS.Value); + + // Note that std::future from this cleanup action is ignored. + scheduleCancelRebuild(std::move(Recreated.RemovedFile)); + // Schedule a reparse. + return scheduleReparseAndDiags(File, std::move(FileContents), + std::move(Recreated.FileInCollection), + std::move(TaggedFS)); } Tagged> Index: clangd/ClangdUnitStore.h =================================================================== --- clangd/ClangdUnitStore.h +++ clangd/ClangdUnitStore.h @@ -42,6 +42,16 @@ return It->second; } + struct RecreateResult { + std::shared_ptr FileInCollection; + std::shared_ptr RemovedFile; + }; + + RecreateResult recreateFileIfCompileCommandChanged( + PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB, + std::shared_ptr PCHs, + IntrusiveRefCntPtr VFS); + std::shared_ptr getFile(PathRef File) { std::lock_guard Lock(Mutex); @@ -59,6 +69,9 @@ tooling::CompileCommand getCompileCommand(GlobalCompilationDatabase &CDB, PathRef File, PathRef ResourceDir); + bool compileCommandsAreEqual(tooling::CompileCommand const &LHS, + tooling::CompileCommand const &RHS); + std::mutex Mutex; llvm::StringMap> OpenedFiles; }; Index: clangd/ClangdUnitStore.cpp =================================================================== --- clangd/ClangdUnitStore.cpp +++ clangd/ClangdUnitStore.cpp @@ -9,6 +9,7 @@ #include "ClangdUnitStore.h" #include "llvm/Support/Path.h" +#include using namespace clang::clangd; using namespace clang; @@ -25,6 +26,34 @@ return Result; } +CppFileCollection::RecreateResult +CppFileCollection::recreateFileIfCompileCommandChanged( + PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB, + std::shared_ptr PCHs, + IntrusiveRefCntPtr VFS) { + auto NewCommand = getCompileCommand(CDB, File, ResourceDir); + + std::lock_guard Lock(Mutex); + + RecreateResult Result; + auto It = OpenedFiles.find(File); + if (It == OpenedFiles.end()) { + It = OpenedFiles + .try_emplace(File, CppFile::Create(File, std::move(NewCommand), + std::move(PCHs))) + .first; + Result.RemovedFile = nullptr; + } else if (!compileCommandsAreEqual(It->second->getCompileCommand(), + NewCommand)) { + Result.RemovedFile = std::move(It->second); + It->second = CppFile::Create(File, std::move(NewCommand), std::move(PCHs)); + } else { + Result.RemovedFile = nullptr; + } + Result.FileInCollection = It->second; + return Result; +} + tooling::CompileCommand CppFileCollection::getCompileCommand(GlobalCompilationDatabase &CDB, PathRef File, PathRef ResourceDir) { @@ -39,3 +68,12 @@ std::string(ResourceDir)); return std::move(Commands.front()); } + +bool CppFileCollection::compileCommandsAreEqual( + tooling::CompileCommand const &LHS, tooling::CompileCommand const &RHS) { + // tooling::CompileCommand.Output is ignored, it's not relevant for clangd. + return LHS.Directory == RHS.Directory && + LHS.CommandLine.size() == RHS.CommandLine.size() && + std::equal(LHS.CommandLine.begin(), LHS.CommandLine.end(), + RHS.CommandLine.begin()); +}