forked from qt-creator/qt-creator
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:
@@ -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(
|
||||||
|
@@ -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;
|
||||||
|
@@ -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,12 +666,20 @@ 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;
|
||||||
emit shadowDocumentSwitched(document->filePath());
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientPrivate::updateCompletionProvider(TextEditor::TextDocument *document)
|
void ClientPrivate::updateCompletionProvider(TextEditor::TextDocument *document)
|
||||||
@@ -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()) {
|
||||||
|
VersionedTextDocumentIdentifier docId(DocumentUri::fromFilePath(filePath));
|
||||||
|
docId.setVersion(++d->m_documentVersions[filePath]);
|
||||||
|
const DidChangeTextDocumentParams params(docId, content);
|
||||||
|
sendMessage(DidChangeTextDocumentNotification(params), SendDocUpdates::Ignore);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (documentForFilePath(filePath))
|
if (documentForFilePath(filePath))
|
||||||
return;
|
return;
|
||||||
const auto uri = DocumentUri::fromFilePath(filePath);
|
for (auto docIt = d->m_openedDocument.cbegin(); docIt != d->m_openedDocument.cend(); ++docIt) {
|
||||||
if (isNew) {
|
if (referencesShadowFile(docIt.key(), filePath))
|
||||||
const QString mimeType = mimeTypeForFile(filePath, MimeMatchMode::MatchExtension).name();
|
d->openShadowDocument(docIt.key(), shadowIt);
|
||||||
d->sendOpenNotification(filePath, mimeType, content, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VersionedTextDocumentIdentifier docId(uri);
|
|
||||||
docId.setVersion(++d->m_documentVersions[filePath]);
|
|
||||||
const DidChangeTextDocumentParams params(docId, content);
|
|
||||||
sendMessage(DidChangeTextDocumentNotification(params), SendDocUpdates::Ignore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user