ClangCodeModel: Do not make clangd open all ui headers

This amends 01ceb3a3cb, where we failed to
consider the case of projects with lots of UI headers, which cause
excessive memory use by clangd with our current simplistic approach.
Instead, we now only open ui headers that are used by currently open
documents.
Note that this approach will fail for indirect includes via header files,
but people who do that do not deserve happiness.

Change-Id: I1ef2add701e0f13dc0da79267d3c1367c1b496cc
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2022-07-25 15:25:56 +02:00
parent a6d1f7594c
commit 5f9c30cacf
4 changed files with 97 additions and 24 deletions

View File

@@ -1143,6 +1143,14 @@ DiagnosticManager *ClangdClient::createDiagnosticManager()
return diagnosticManager; return diagnosticManager;
} }
bool ClangdClient::referencesShadowFile(const TextEditor::TextDocument *doc,
const Utils::FilePath &candidate)
{
const QRegularExpression includeRex("#include.*" + candidate.fileName() + R"([>"])");
const QTextCursor includePos = doc->document()->find(includeRex);
return !includePos.isNull();
}
RefactoringChangesData *ClangdClient::createRefactoringChangesBackend() const RefactoringChangesData *ClangdClient::createRefactoringChangesBackend() const
{ {
return new CppEditor::CppRefactoringChangesData( return new CppEditor::CppRefactoringChangesData(

View File

@@ -145,6 +145,8 @@ private:
const CustomInspectorTabs createCustomInspectorTabs() override; const CustomInspectorTabs createCustomInspectorTabs() override;
TextEditor::RefactoringChangesData *createRefactoringChangesBackend() const override; TextEditor::RefactoringChangesData *createRefactoringChangesBackend() const override;
LanguageClient::DiagnosticManager *createDiagnosticManager() override; LanguageClient::DiagnosticManager *createDiagnosticManager() override;
bool referencesShadowFile(const TextEditor::TextDocument *doc,
const Utils::FilePath &candidate) override;
class Private; class Private;
class VirtualFunctionAssistProcessor; class VirtualFunctionAssistProcessor;

View File

@@ -263,6 +263,12 @@ public:
void sendOpenNotification(const FilePath &filePath, const QString &mimeType, void sendOpenNotification(const FilePath &filePath, const QString &mimeType,
const QString &content, int version); const QString &content, int version);
void sendCloseNotification(const FilePath &filePath); void sendCloseNotification(const FilePath &filePath);
void openRequiredShadowDocuments(const TextEditor::TextDocument *doc);
void closeRequiredShadowDocuments(const TextEditor::TextDocument *doc);
using ShadowDocIterator = QMap<FilePath, QPair<QString, QList<const TextEditor::TextDocument *>>>::iterator;
void openShadowDocument(const TextEditor::TextDocument *requringDoc, ShadowDocIterator shadowIt);
void closeShadowDocument(ShadowDocIterator docIt);
bool reset(); bool reset();
@@ -276,7 +282,9 @@ public:
// Used for build system artifacts (e.g. UI headers) that Qt Creator "live-generates" ahead of // Used for build system artifacts (e.g. UI headers) that Qt Creator "live-generates" ahead of
// the build. // the build.
QMap<FilePath, QString> m_shadowDocuments; // The Value is the file content + the documents that require the shadow file to be open
// (empty <=> shadow document is not open).
QMap<FilePath, QPair<QString, QList<const TextEditor::TextDocument *>>> m_shadowDocuments;
QSet<TextEditor::TextDocument *> m_postponedDocuments; QSet<TextEditor::TextDocument *> m_postponedDocuments;
QMap<Utils::FilePath, int> m_documentVersions; QMap<Utils::FilePath, int> m_documentVersions;
@@ -577,10 +585,12 @@ void Client::openDocument(TextEditor::TextDocument *document)
} }
const FilePath &filePath = document->filePath(); const FilePath &filePath = document->filePath();
if (d->m_shadowDocuments.contains(filePath)) { const auto shadowIt = d->m_shadowDocuments.find(filePath);
d->sendCloseNotification(filePath); if (shadowIt != d->m_shadowDocuments.end()) {
d->closeShadowDocument(shadowIt);
emit shadowDocumentSwitched(filePath); emit shadowDocumentSwitched(filePath);
} }
d->openRequiredShadowDocuments(document);
const QString method(DidOpenTextDocumentNotification::methodName); const QString method(DidOpenTextDocumentNotification::methodName);
if (Utils::optional<bool> registered = d->m_dynamicCapabilities.isRegistered(method)) { if (Utils::optional<bool> registered = d->m_dynamicCapabilities.isRegistered(method)) {
@@ -656,11 +666,19 @@ void Client::closeDocument(TextEditor::TextDocument *document)
if (d->m_state != Initialized) if (d->m_state != Initialized)
return; return;
const auto shadowIt = d->m_shadowDocuments.constFind(document->filePath()); d->closeRequiredShadowDocuments(document);
const auto shadowIt = d->m_shadowDocuments.find(document->filePath());
if (shadowIt == d->m_shadowDocuments.constEnd()) if (shadowIt == d->m_shadowDocuments.constEnd())
return; return;
d->sendOpenNotification(document->filePath(), document->mimeType(), shadowIt.value(), QTC_CHECK(shadowIt.value().second.isEmpty());
++d->m_documentVersions[document->filePath()]); bool isReferenced = false;
for (auto it = d->m_openedDocument.cbegin(); it != d->m_openedDocument.cend(); ++it) {
if (referencesShadowFile(it.key(), shadowIt.key())) {
d->openShadowDocument(it.key(), shadowIt);
isReferenced = true;
}
}
if (isReferenced)
emit shadowDocumentSwitched(document->filePath()); emit shadowDocumentSwitched(document->filePath());
} }
@@ -871,6 +889,22 @@ void ClientPrivate::sendCloseNotification(const FilePath &filePath)
Client::SendDocUpdates::Ignore); Client::SendDocUpdates::Ignore);
} }
void ClientPrivate::openRequiredShadowDocuments(const TextEditor::TextDocument *doc)
{
for (auto it = m_shadowDocuments.begin(); it != m_shadowDocuments.end(); ++it) {
if (!it.value().second.contains(doc) && q->referencesShadowFile(doc, it.key()))
openShadowDocument(doc, it);
}
}
void ClientPrivate::closeRequiredShadowDocuments(const TextEditor::TextDocument *doc)
{
for (auto it = m_shadowDocuments.begin(); it != m_shadowDocuments.end(); ++it) {
if (it.value().second.removeOne(doc) && it.value().second.isEmpty())
closeShadowDocument(it);
}
}
bool Client::documentOpen(const TextEditor::TextDocument *document) const bool Client::documentOpen(const TextEditor::TextDocument *document) const
{ {
return d->m_openedDocument.contains(const_cast<TextEditor::TextDocument *>(document)); return d->m_openedDocument.contains(const_cast<TextEditor::TextDocument *>(document));
@@ -888,24 +922,25 @@ TextEditor::TextDocument *Client::documentForFilePath(const Utils::FilePath &fil
void Client::setShadowDocument(const Utils::FilePath &filePath, const QString &content) void Client::setShadowDocument(const Utils::FilePath &filePath, const QString &content)
{ {
QTC_ASSERT(reachable(), return); QTC_ASSERT(reachable(), return);
const auto it = d->m_shadowDocuments.find(filePath); auto shadowIt = d->m_shadowDocuments.find(filePath);
const bool isNew = it == d->m_shadowDocuments.end(); if (shadowIt == d->m_shadowDocuments.end()) {
if (isNew) shadowIt = d->m_shadowDocuments.insert(filePath, {content, {}});
d->m_shadowDocuments.insert(filePath, content); } else {
else shadowIt.value().first = content;
it.value() = content; if (!shadowIt.value().second.isEmpty()) {
if (documentForFilePath(filePath)) VersionedTextDocumentIdentifier docId(DocumentUri::fromFilePath(filePath));
return;
const auto uri = DocumentUri::fromFilePath(filePath);
if (isNew) {
const QString mimeType = mimeTypeForFile(filePath, MimeMatchMode::MatchExtension).name();
d->sendOpenNotification(filePath, mimeType, content, 0);
}
VersionedTextDocumentIdentifier docId(uri);
docId.setVersion(++d->m_documentVersions[filePath]); docId.setVersion(++d->m_documentVersions[filePath]);
const DidChangeTextDocumentParams params(docId, content); const DidChangeTextDocumentParams params(docId, content);
sendMessage(DidChangeTextDocumentNotification(params), SendDocUpdates::Ignore); sendMessage(DidChangeTextDocumentNotification(params), SendDocUpdates::Ignore);
return;
}
}
if (documentForFilePath(filePath))
return;
for (auto docIt = d->m_openedDocument.cbegin(); docIt != d->m_openedDocument.cend(); ++docIt) {
if (referencesShadowFile(docIt.key(), filePath))
d->openShadowDocument(docIt.key(), shadowIt);
}
} }
void Client::removeShadowDocument(const Utils::FilePath &filePath) void Client::removeShadowDocument(const Utils::FilePath &filePath)
@@ -913,10 +948,27 @@ void Client::removeShadowDocument(const Utils::FilePath &filePath)
const auto it = d->m_shadowDocuments.find(filePath); const auto it = d->m_shadowDocuments.find(filePath);
if (it == d->m_shadowDocuments.end()) if (it == d->m_shadowDocuments.end())
return; return;
if (!it.value().second.isEmpty())
d->closeShadowDocument(it);
d->m_shadowDocuments.erase(it); d->m_shadowDocuments.erase(it);
if (documentForFilePath(filePath)) }
void ClientPrivate::openShadowDocument(const TextEditor::TextDocument *requringDoc,
ShadowDocIterator shadowIt)
{
shadowIt.value().second << requringDoc;
if (shadowIt.value().second.size() > 1)
return; return;
d->sendCloseNotification(filePath); const auto uri = DocumentUri::fromFilePath(shadowIt.key());
const QString mimeType = mimeTypeForFile(shadowIt.key(), MimeMatchMode::MatchExtension).name();
sendOpenNotification(shadowIt.key(), mimeType, shadowIt.value().first,
++m_documentVersions[shadowIt.key()]);
}
void ClientPrivate::closeShadowDocument(ShadowDocIterator shadowIt)
{
sendCloseNotification(shadowIt.key());
shadowIt.value().second.clear();
} }
void Client::documentContentsSaved(TextEditor::TextDocument *document) void Client::documentContentsSaved(TextEditor::TextDocument *document)
@@ -948,6 +1000,7 @@ void Client::documentContentsSaved(TextEditor::TextDocument *document)
return; return;
DidSaveTextDocumentParams params( DidSaveTextDocumentParams params(
TextDocumentIdentifier(DocumentUri::fromFilePath(document->filePath()))); TextDocumentIdentifier(DocumentUri::fromFilePath(document->filePath())));
d->openRequiredShadowDocuments(document);
if (includeText) if (includeText)
params.setText(document->plainText()); params.setText(document->plainText());
sendMessage(DidSaveTextDocumentNotification(params), SendDocUpdates::Send, Schedule::Now); sendMessage(DidSaveTextDocumentNotification(params), SendDocUpdates::Send, Schedule::Now);
@@ -1971,6 +2024,14 @@ QTextCursor Client::adjustedCursorForHighlighting(const QTextCursor &cursor,
return cursor; return cursor;
} }
bool Client::referencesShadowFile(const TextEditor::TextDocument *doc,
const Utils::FilePath &candidate)
{
Q_UNUSED(doc)
Q_UNUSED(candidate)
return false;
}
} // namespace LanguageClient } // namespace LanguageClient
#include <client.moc> #include <client.moc>

View File

@@ -226,6 +226,8 @@ private:
virtual void handleDocumentOpened(TextEditor::TextDocument *) {} virtual void handleDocumentOpened(TextEditor::TextDocument *) {}
virtual QTextCursor adjustedCursorForHighlighting(const QTextCursor &cursor, virtual QTextCursor adjustedCursorForHighlighting(const QTextCursor &cursor,
TextEditor::TextDocument *doc); TextEditor::TextDocument *doc);
virtual bool referencesShadowFile(const TextEditor::TextDocument *doc,
const Utils::FilePath &candidate);
}; };
} // namespace LanguageClient } // namespace LanguageClient