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 <gogan419@gmail.com>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Volodymyr Zibarov
2023-11-01 10:30:01 +02:00
parent f9d4697371
commit d0203a39fa
3 changed files with 122 additions and 2 deletions

View File

@@ -20,6 +20,7 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <cppeditor/clangdiagnosticconfig.h>
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppmodelmanager.h>
#include <projectexplorer/buildconfiguration.h>
@@ -83,6 +84,20 @@ void ClangCodeModelPlugin::initialize()
CppEditor::CppModelManager::activateClangCodeModel(std::make_unique<ClangModelManagerSupport>());
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<ActionContainer *> 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<Tests::ActivationSequenceProcessorTest>();
addTest<Tests::ClangdTestCompletion>();

View File

@@ -48,7 +48,10 @@
#include <utils/qtcassert.h>
#include <QApplication>
#include <QDateTime>
#include <QHash>
#include <QLabel>
#include <QLoggingCategory>
#include <QMenu>
#include <QTextBlock>
#include <QTimer>
@@ -61,6 +64,41 @@ using namespace ProjectExplorer;
using namespace Utils;
namespace ClangCodeModel::Internal {
namespace {
class IndexFiles
{
public:
QList<Utils::FilePath> files;
QDateTime minLastModifiedTime;
};
} // namespace
static Q_LOGGING_CATEGORY(clangdIndexLog, "qtc.clangcodemodel.clangd.index", QtWarningMsg);
static QHash<QString, IndexFiles> collectIndexedFiles(const Utils::FilePath &indexFolder)
{
QHash<QString, IndexFiles> 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<ClangdClient *>(clients.first());
}
void ClangModelManagerSupport::updateStaleIndexEntries()
{
QHash<FilePath, QDateTime> 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<QString, IndexFiles> 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() == "<configuration>")
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<FilePath> 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<ClangModelManagerSupport *>(
CppModelManager::modelManagerSupport(CppModelManager::Backend::Best));
QTC_ASSERT(instance, return);
instance->scheduleClientRestart(client);
}
}
}
}
ClangdClient *ClangModelManagerSupport::clientForFile(const FilePath &file)
{
return qobject_cast<ClangdClient *>(LanguageClientManager::clientForFilePath(file));

View File

@@ -14,8 +14,6 @@
#include <QObject>
#include <QPointer>
#include <memory>
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,