From d0203a39fa9ec63ee155d536151c39447e83e056 Mon Sep 17 00:00:00 2001 From: Volodymyr Zibarov Date: Wed, 1 Nov 2023 10:30:01 +0200 Subject: [PATCH] ClangCodeModel: Add menu action to re-index files ... that depend on changed headers. When changing a header file the clangd code model is not updated for files that including it if they are not opened in editor. This is not to be done automatically, as it would be a performance hazard to rescan many files, for example when changing a widely used header. Add a menu action to trigger such re-indexing manually to solve the issue. Task-number: QTCREATORBUG-27387 Change-Id: Ia8033401f847627cee041b102f9ac6f3af3dd709 Reviewed-by: Volodymyr Zibarov Reviewed-by: Christian Kandeler --- .../clangcodemodel/clangcodemodelplugin.cpp | 15 +++ .../clangmodelmanagersupport.cpp | 105 ++++++++++++++++++ .../clangcodemodel/clangmodelmanagersupport.h | 4 +- 3 files changed, 122 insertions(+), 2 deletions(-) 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,