diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index ac194abfbf9..e8b8f5e28df 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -83,6 +84,20 @@ void ClangCodeModelPlugin::initialize() CppEditor::CppModelManager::activateClangCodeModel(std::make_unique()); createCompilationDBAction(); + QAction * const updateStaleIndexEntries + = new QAction(Tr::tr("Update potentially stale clangd index entries"), this); + Command * const cmd = ActionManager::registerAction(updateStaleIndexEntries, + "ClangCodeModel.UpdateStaleIndexEntries"); + connect(updateStaleIndexEntries, &QAction::triggered, this, + [] { ClangModelManagerSupport::updateStaleIndexEntries(); }); + const QList menus; + namespace CppConstants = CppEditor::Constants; + for (ActionContainer * const menu : {ActionManager::actionContainer(CppConstants::M_TOOLS_CPP), + ActionManager::actionContainer(CppConstants::M_CONTEXT)}) { + QTC_ASSERT(menu, continue); + menu->addAction(cmd, CppEditor::Constants::G_GLOBAL); + } + #ifdef WITH_TESTS addTest(); addTest(); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index a25ceef0ff5..caefe586308 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -48,7 +48,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -61,6 +64,41 @@ using namespace ProjectExplorer; using namespace Utils; namespace ClangCodeModel::Internal { +namespace { +class IndexFiles +{ +public: + QList files; + QDateTime minLastModifiedTime; +}; +} // namespace + +static Q_LOGGING_CATEGORY(clangdIndexLog, "qtc.clangcodemodel.clangd.index", QtWarningMsg); + +static QHash collectIndexedFiles(const Utils::FilePath &indexFolder) +{ + QHash result; + QDirIterator dirIt(indexFolder.toFSPathString(), QDir::Files); + while (dirIt.hasNext()) { + dirIt.next(); + FilePath path = FilePath::fromString(dirIt.filePath()); + if (path.suffix() != "idx") + continue; + + QString baseName = path.completeBaseName(); + const int dotPos = baseName.lastIndexOf('.'); + if (dotPos <= 0) + continue; + + baseName = baseName.left(dotPos); + IndexFiles &indexFiles = result[baseName]; + indexFiles.files.push_back(path); + QDateTime time = path.lastModified(); + if (indexFiles.minLastModifiedTime.isNull() || time < indexFiles.minLastModifiedTime) + indexFiles.minLastModifiedTime = time; + } + return result; +} static Project *fallbackProject() { @@ -644,6 +682,73 @@ ClangdClient *ClangModelManagerSupport::clientWithProject(const Project *project return clients.empty() ? nullptr : qobject_cast(clients.first()); } +void ClangModelManagerSupport::updateStaleIndexEntries() +{ + QHash lastModifiedCache; + for (const Project * const project : ProjectManager::projects()) { + const FilePath jsonDbDir = getJsonDbDir(project); + if (jsonDbDir.isEmpty()) + continue; + + const FilePath indexFolder = jsonDbDir / ".cache" / "clangd" / "index"; + if (!indexFolder.exists()) + continue; + + const QHash indexedFiles = collectIndexedFiles(indexFolder); + bool restartCodeModel = false; + const CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); + for (const CPlusPlus::Document::Ptr &document : snapshot) { + const FilePath sourceFile = document->filePath(); + if (sourceFile.fileName() == "") + continue; + + if (!project->isKnownFile(sourceFile)) { + qCDebug(clangdIndexLog) << "Not in project:" << sourceFile.fileName(); + continue; + } + + const auto indexFilesIt = indexedFiles.find(sourceFile.fileName()); + if (indexFilesIt == indexedFiles.end()) { + qCDebug(clangdIndexLog) << "No index files for:" << sourceFile.fileName(); + continue; + } + + const QDateTime sourceIndexedTime = indexFilesIt->minLastModifiedTime; + + bool rescan = false; + QSet allIncludes = snapshot.allIncludesForDocument(sourceFile); + for (const FilePath &includeFile : qAsConst(allIncludes)) { + auto includeFileTimeIt = lastModifiedCache.find(includeFile); + if (includeFileTimeIt == lastModifiedCache.end()) { + includeFileTimeIt = lastModifiedCache.insert(includeFile, + includeFile.lastModified()); + } + if (sourceIndexedTime < includeFileTimeIt.value()) { + qCDebug(clangdIndexLog) << "Rescan file:" << sourceFile.fileName() + << "indexed at" << sourceIndexedTime + << "changed include:" << includeFile.fileName() + << "last modified at" << includeFileTimeIt.value(); + rescan = true; + break; + } + } + if (rescan) { + for (const FilePath &indexFile : indexFilesIt->files) + indexFile.removeFile(); + restartCodeModel = true; + } + } + if (restartCodeModel) { + if (ClangdClient * const client = clientForProject(project)) { + const auto instance = dynamic_cast( + CppModelManager::modelManagerSupport(CppModelManager::Backend::Best)); + QTC_ASSERT(instance, return); + instance->scheduleClientRestart(client); + } + } + } +} + ClangdClient *ClangModelManagerSupport::clientForFile(const FilePath &file) { return qobject_cast(LanguageClientManager::clientForFilePath(file)); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index cc361127836..595bfdd7950 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -14,8 +14,6 @@ #include #include -#include - QT_BEGIN_NAMESPACE class QMenu; class QTimer; @@ -50,6 +48,8 @@ public: static ClangdClient *clientForProject(const ProjectExplorer::Project *project); static ClangdClient *clientForFile(const Utils::FilePath &file); + static void updateStaleIndexEntries(); + private: void followSymbol(const CppEditor::CursorInEditor &data, const Utils::LinkHandler &processLinkCallback, bool resolveTarget,