forked from qt-creator/qt-creator
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:
@@ -20,6 +20,7 @@
|
|||||||
#include <coreplugin/progressmanager/progressmanager.h>
|
#include <coreplugin/progressmanager/progressmanager.h>
|
||||||
|
|
||||||
#include <cppeditor/clangdiagnosticconfig.h>
|
#include <cppeditor/clangdiagnosticconfig.h>
|
||||||
|
#include <cppeditor/cppeditorconstants.h>
|
||||||
#include <cppeditor/cppmodelmanager.h>
|
#include <cppeditor/cppmodelmanager.h>
|
||||||
|
|
||||||
#include <projectexplorer/buildconfiguration.h>
|
#include <projectexplorer/buildconfiguration.h>
|
||||||
@@ -83,6 +84,20 @@ void ClangCodeModelPlugin::initialize()
|
|||||||
CppEditor::CppModelManager::activateClangCodeModel(std::make_unique<ClangModelManagerSupport>());
|
CppEditor::CppModelManager::activateClangCodeModel(std::make_unique<ClangModelManagerSupport>());
|
||||||
createCompilationDBAction();
|
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
|
#ifdef WITH_TESTS
|
||||||
addTest<Tests::ActivationSequenceProcessorTest>();
|
addTest<Tests::ActivationSequenceProcessorTest>();
|
||||||
addTest<Tests::ClangdTestCompletion>();
|
addTest<Tests::ClangdTestCompletion>();
|
||||||
|
|||||||
@@ -48,7 +48,10 @@
|
|||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QHash>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QLoggingCategory>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@@ -61,6 +64,41 @@ using namespace ProjectExplorer;
|
|||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace ClangCodeModel::Internal {
|
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()
|
static Project *fallbackProject()
|
||||||
{
|
{
|
||||||
@@ -644,6 +682,73 @@ ClangdClient *ClangModelManagerSupport::clientWithProject(const Project *project
|
|||||||
return clients.empty() ? nullptr : qobject_cast<ClangdClient *>(clients.first());
|
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)
|
ClangdClient *ClangModelManagerSupport::clientForFile(const FilePath &file)
|
||||||
{
|
{
|
||||||
return qobject_cast<ClangdClient *>(LanguageClientManager::clientForFilePath(file));
|
return qobject_cast<ClangdClient *>(LanguageClientManager::clientForFilePath(file));
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QMenu;
|
class QMenu;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
@@ -50,6 +48,8 @@ public:
|
|||||||
static ClangdClient *clientForProject(const ProjectExplorer::Project *project);
|
static ClangdClient *clientForProject(const ProjectExplorer::Project *project);
|
||||||
static ClangdClient *clientForFile(const Utils::FilePath &file);
|
static ClangdClient *clientForFile(const Utils::FilePath &file);
|
||||||
|
|
||||||
|
static void updateStaleIndexEntries();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void followSymbol(const CppEditor::CursorInEditor &data,
|
void followSymbol(const CppEditor::CursorInEditor &data,
|
||||||
const Utils::LinkHandler &processLinkCallback, bool resolveTarget,
|
const Utils::LinkHandler &processLinkCallback, bool resolveTarget,
|
||||||
|
|||||||
Reference in New Issue
Block a user