forked from qt-creator/qt-creator
ClangCodeModel: Restart clangd on "internal" changes to non-open files
Our previous "openExtraFile" approach does not reliably trigger re- indexing. Task-number: QTCREATORBUG-26521 Change-Id: Ibb4008d9cc72afddf6709c6f9442426440b72d01 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -167,9 +167,33 @@ static void updateParserConfig(ClangdClient *client)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool projectIsParsing(const ClangdClient *client)
|
||||||
|
{
|
||||||
|
for (const ProjectExplorer::Project * const p : projectsForClient(client)) {
|
||||||
|
const ProjectExplorer::BuildSystem * const bs = p && p->activeTarget()
|
||||||
|
? p->activeTarget()->buildSystem() : nullptr;
|
||||||
|
if (bs && (bs->isParsing() || bs->isWaitingForParse()))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ClangModelManagerSupport::ClangModelManagerSupport()
|
ClangModelManagerSupport::ClangModelManagerSupport()
|
||||||
|
: m_clientRestartTimer(new QTimer(this))
|
||||||
{
|
{
|
||||||
|
m_clientRestartTimer->setInterval(3000);
|
||||||
|
connect(m_clientRestartTimer, &QTimer::timeout, this, [this] {
|
||||||
|
const auto clients = m_clientsToRestart;
|
||||||
|
m_clientsToRestart.clear();
|
||||||
|
for (ClangdClient * const client : clients) {
|
||||||
|
if (client && client->state() != Client::Shutdown
|
||||||
|
&& client->state() != Client::ShutdownRequested
|
||||||
|
&& !projectIsParsing(client)) {
|
||||||
|
updateLanguageClient(client->project());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
watchForExternalChanges();
|
watchForExternalChanges();
|
||||||
watchForInternalChanges();
|
watchForInternalChanges();
|
||||||
setupClangdConfigFile();
|
setupClangdConfigFile();
|
||||||
@@ -594,32 +618,8 @@ void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *client)
|
|||||||
// workflow, e.g. a git branch switch will hit at least one open file.
|
// workflow, e.g. a git branch switch will hit at least one open file.
|
||||||
void ClangModelManagerSupport::watchForExternalChanges()
|
void ClangModelManagerSupport::watchForExternalChanges()
|
||||||
{
|
{
|
||||||
static const auto projectIsParsing = [](const ClangdClient *client) {
|
|
||||||
for (const ProjectExplorer::Project * const p : projectsForClient(client)) {
|
|
||||||
const ProjectExplorer::BuildSystem * const bs = p && p->activeTarget()
|
|
||||||
? p->activeTarget()->buildSystem() : nullptr;
|
|
||||||
if (bs && (bs->isParsing() || bs->isWaitingForParse()))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto timer = new QTimer(this);
|
|
||||||
timer->setInterval(3000);
|
|
||||||
connect(timer, &QTimer::timeout, this, [this] {
|
|
||||||
const auto clients = m_clientsToRestart;
|
|
||||||
m_clientsToRestart.clear();
|
|
||||||
for (ClangdClient * const client : clients) {
|
|
||||||
if (client && client->state() != Client::Shutdown
|
|
||||||
&& client->state() != Client::ShutdownRequested
|
|
||||||
&& !projectIsParsing(client)) {
|
|
||||||
updateLanguageClient(client->project());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedExternally,
|
connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedExternally,
|
||||||
this, [this, timer](const QSet<Utils::FilePath> &files) {
|
this, [this](const QSet<Utils::FilePath> &files) {
|
||||||
if (!LanguageClientManager::hasClients<ClangdClient>())
|
if (!LanguageClientManager::hasClients<ClangdClient>())
|
||||||
return;
|
return;
|
||||||
for (const Utils::FilePath &file : files) {
|
for (const Utils::FilePath &file : files) {
|
||||||
@@ -631,18 +631,8 @@ void ClangModelManagerSupport::watchForExternalChanges()
|
|||||||
if (!project)
|
if (!project)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ClangdClient * const client = clientForProject(project);
|
if (ClangdClient * const client = clientForProject(project))
|
||||||
if (client && !m_clientsToRestart.contains(client)) {
|
scheduleClientRestart(client);
|
||||||
|
|
||||||
// If a project file was changed, it is very likely that we will have to generate
|
|
||||||
// a new compilation database, in which case the client will be restarted via
|
|
||||||
// a different code path.
|
|
||||||
if (projectIsParsing(client))
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_clientsToRestart.append(client);
|
|
||||||
timer->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's unlikely that the same signal carries files from different projects,
|
// It's unlikely that the same signal carries files from different projects,
|
||||||
// so we exit the loop as soon as we have dealt with one project, as the
|
// so we exit the loop as soon as we have dealt with one project, as the
|
||||||
@@ -652,29 +642,43 @@ void ClangModelManagerSupport::watchForExternalChanges()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If Qt Creator changes a file that is not open (e.g. as part of a quickfix), we have to
|
||||||
|
// restart clangd for reliable re-parsing and re-indexing.
|
||||||
void ClangModelManagerSupport::watchForInternalChanges()
|
void ClangModelManagerSupport::watchForInternalChanges()
|
||||||
{
|
{
|
||||||
connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedInternally,
|
connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedInternally,
|
||||||
this, [](const Utils::FilePaths &filePaths) {
|
this, [this](const Utils::FilePaths &filePaths) {
|
||||||
for (const Utils::FilePath &fp : filePaths) {
|
for (const Utils::FilePath &fp : filePaths) {
|
||||||
ClangdClient * const client = clientForFile(fp);
|
const ProjectFile::Kind kind = ProjectFile::classify(fp.toString());
|
||||||
if (!client || client->documentForFilePath(fp))
|
if (!ProjectFile::isSource(kind) && !ProjectFile::isHeader(kind))
|
||||||
continue;
|
continue;
|
||||||
client->openExtraFile(fp);
|
ProjectExplorer::Project * const project
|
||||||
|
= ProjectExplorer::SessionManager::projectForFile(fp);
|
||||||
// We need to give clangd some time to start re-parsing the file.
|
if (!project)
|
||||||
// Closing right away does not work, and neither does doing it queued.
|
continue;
|
||||||
// If it turns out that this delay is not always enough, we'll need to come up
|
if (ClangdClient * const client = clientForProject(project);
|
||||||
// with something more clever.
|
client && !client->documentForFilePath(fp)) {
|
||||||
// Ideally, clangd would implement workspace/didChangeWatchedFiles; let's keep
|
scheduleClientRestart(client);
|
||||||
// any eye on that.
|
}
|
||||||
QTimer::singleShot(5000, client, [client, fp] {
|
|
||||||
if (!client->documentForFilePath(fp))
|
|
||||||
client->closeExtraFile(fp); });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangModelManagerSupport::scheduleClientRestart(ClangdClient *client)
|
||||||
|
{
|
||||||
|
if (m_clientsToRestart.contains(client))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If a project file was changed, it is very likely that we will have to generate
|
||||||
|
// a new compilation database, in which case the client will be restarted via
|
||||||
|
// a different code path.
|
||||||
|
if (projectIsParsing(client))
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_clientsToRestart.append(client);
|
||||||
|
m_clientRestartTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor)
|
void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(editor, return);
|
QTC_ASSERT(editor, return);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QMenu;
|
class QMenu;
|
||||||
|
class QTimer;
|
||||||
class QWidget;
|
class QWidget;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
@@ -85,10 +86,12 @@ private:
|
|||||||
void claimNonProjectSources(ClangdClient *client);
|
void claimNonProjectSources(ClangdClient *client);
|
||||||
void watchForExternalChanges();
|
void watchForExternalChanges();
|
||||||
void watchForInternalChanges();
|
void watchForInternalChanges();
|
||||||
|
void scheduleClientRestart(ClangdClient *client);
|
||||||
static ClangdClient *clientWithProject(const ProjectExplorer::Project *project);
|
static ClangdClient *clientWithProject(const ProjectExplorer::Project *project);
|
||||||
|
|
||||||
Utils::FutureSynchronizer m_generatorSynchronizer;
|
Utils::FutureSynchronizer m_generatorSynchronizer;
|
||||||
QList<QPointer<ClangdClient>> m_clientsToRestart;
|
QList<QPointer<ClangdClient>> m_clientsToRestart;
|
||||||
|
QTimer * const m_clientRestartTimer;
|
||||||
QHash<Utils::FilePath, QString> m_queuedShadowDocuments;
|
QHash<Utils::FilePath, QString> m_queuedShadowDocuments;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user