aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2023-01-16 17:03:26 +0100
committerChristian Kandeler <christian.kandeler@qt.io>2023-01-27 10:02:52 +0000
commit0d909c353c54ecf2fee2d13b7d2fe19eee01591d (patch)
tree22fa92a342dab8f9039c49e6cb99ee618462650b
parentb4b260071587d681c78768a7a84807226857b081 (diff)
Designer: Update C++ code model on an object name change in designer
We try to locate the old symbol name in the generated ui header and rename the symbol in the background. Task-number: QTCREATORBUG-1179 Change-Id: Iaf68e3922cd728cbc87d0dc97125e34b8bdaa6be Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: David Schulz <david.schulz@qt.io>
-rw-r--r--src/plugins/clangcodemodel/clangdclient.cpp19
-rw-r--r--src/plugins/clangcodemodel/clangdclient.h3
-rw-r--r--src/plugins/clangcodemodel/clangdfindreferences.cpp7
-rw-r--r--src/plugins/clangcodemodel/clangdfindreferences.h4
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.cpp9
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.h3
-rw-r--r--src/plugins/clangcodemodel/test/clangdtests.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp7
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.h1
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.cpp16
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.h2
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.cpp13
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.h5
-rw-r--r--src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp5
-rw-r--r--src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h3
-rw-r--r--src/plugins/cppeditor/cppeditorwidget.cpp12
-rw-r--r--src/plugins/cppeditor/cppeditorwidget.h6
-rw-r--r--src/plugins/cppeditor/cppfindreferences.cpp13
-rw-r--r--src/plugins/cppeditor/cppfindreferences.h8
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.cpp20
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.h10
-rw-r--r--src/plugins/cppeditor/cppmodelmanagersupport.h4
-rw-r--r--src/plugins/designer/qtcreatorintegration.cpp234
-rw-r--r--src/plugins/designer/qtcreatorintegration.h8
-rw-r--r--src/plugins/languageclient/languageclientsymbolsupport.cpp28
-rw-r--r--src/plugins/languageclient/languageclientsymbolsupport.h9
-rw-r--r--src/plugins/projectexplorer/buildsystem.cpp6
-rw-r--r--src/plugins/projectexplorer/buildsystem.h4
-rw-r--r--src/plugins/projectexplorer/extracompiler.cpp37
-rw-r--r--src/plugins/projectexplorer/extracompiler.h3
-rw-r--r--src/plugins/qbsprojectmanager/qbsproject.cpp7
-rw-r--r--src/plugins/qbsprojectmanager/qbsproject.h2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp15
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.h1
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.cpp5
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.h2
36 files changed, 470 insertions, 63 deletions
diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp
index 6735b96aeb..6d2d2fb95d 100644
--- a/src/plugins/clangcodemodel/clangdclient.cpp
+++ b/src/plugins/clangcodemodel/clangdclient.cpp
@@ -292,7 +292,7 @@ public:
void findUsages(TextDocument *document, const QTextCursor &cursor,
const QString &searchTerm, const std::optional<QString> &replacement,
- bool categorize);
+ const std::function<void()> &callback, bool categorize);
void handleDeclDefSwitchReplies();
@@ -509,7 +509,8 @@ void ClangdClient::closeExtraFile(const Utils::FilePath &filePath)
}
void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
- const std::optional<QString> &replacement)
+ const std::optional<QString> &replacement,
+ const std::function<void()> &renameCallback)
{
// Quick check: Are we even on anything searchable?
const QTextCursor adjustedCursor = d->adjustedCursor(cursor, document);
@@ -519,7 +520,7 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
if (replacement && versionNumber() >= QVersionNumber(16)
&& Utils::qtcEnvironmentVariable("QTC_CLANGD_RENAMING") != "0") {
- symbolSupport().renameSymbol(document, adjustedCursor, *replacement,
+ symbolSupport().renameSymbol(document, adjustedCursor, *replacement, renameCallback,
CppEditor::preferLowerCaseFileNames());
return;
}
@@ -530,19 +531,20 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
if (searchTerm != "operator" && Utils::allOf(searchTerm, [](const QChar &c) {
return c.isLetterOrNumber() || c == '_';
})) {
- d->findUsages(document, adjustedCursor, searchTerm, replacement, categorize);
+ d->findUsages(document, adjustedCursor, searchTerm, replacement, renameCallback, categorize);
return;
}
// Otherwise get the proper spelling of the search term from clang, so we can put it into the
// search widget.
- const auto symbolInfoHandler = [this, doc = QPointer(document), adjustedCursor, replacement, categorize]
+ const auto symbolInfoHandler = [this, doc = QPointer(document), adjustedCursor, replacement,
+ renameCallback, categorize]
(const QString &name, const QString &, const MessageId &) {
if (!doc)
return;
if (name.isEmpty())
return;
- d->findUsages(doc.data(), adjustedCursor, name, replacement, categorize);
+ d->findUsages(doc.data(), adjustedCursor, name, replacement, renameCallback, categorize);
};
requestSymbolInfo(document->filePath(), Range(adjustedCursor).start(), symbolInfoHandler);
}
@@ -704,10 +706,11 @@ CppEditor::ClangdSettings::Data ClangdClient::settingsData() const { return d->s
void ClangdClient::Private::findUsages(TextDocument *document,
const QTextCursor &cursor, const QString &searchTerm,
- const std::optional<QString> &replacement, bool categorize)
+ const std::optional<QString> &replacement, const std::function<void()> &renameCallback,
+ bool categorize)
{
const auto findRefs = new ClangdFindReferences(q, document, cursor, searchTerm, replacement,
- categorize);
+ renameCallback, categorize);
if (isTesting) {
connect(findRefs, &ClangdFindReferences::foundReferences,
q, &ClangdClient::foundReferences);
diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h
index ff64a9c3f5..772920b277 100644
--- a/src/plugins/clangcodemodel/clangdclient.h
+++ b/src/plugins/clangcodemodel/clangdclient.h
@@ -54,7 +54,8 @@ public:
void closeExtraFile(const Utils::FilePath &filePath);
void findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor,
- const std::optional<QString> &replacement);
+ const std::optional<QString> &replacement,
+ const std::function<void()> &renameCallback);
void checkUnused(const Utils::Link &link, Core::SearchResult *search,
const Utils::LinkHandler &callback);
void followSymbol(TextEditor::TextDocument *document,
diff --git a/src/plugins/clangcodemodel/clangdfindreferences.cpp b/src/plugins/clangcodemodel/clangdfindreferences.cpp
index 2458183164..f2102325f0 100644
--- a/src/plugins/clangcodemodel/clangdfindreferences.cpp
+++ b/src/plugins/clangcodemodel/clangdfindreferences.cpp
@@ -109,7 +109,8 @@ public:
ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *document,
const QTextCursor &cursor, const QString &searchTerm,
- const std::optional<QString> &replacement, bool categorize)
+ const std::optional<QString> &replacement, const std::function<void()> &callback,
+ bool categorize)
: QObject(client), d(new ClangdFindReferences::Private(this))
{
d->categorize = categorize;
@@ -130,6 +131,7 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d
replacement ? SearchResultWindow::SearchAndReplace : SearchResultWindow::SearchOnly,
SearchResultWindow::PreserveCaseDisabled,
"CppEditor");
+ d->search->makeNonInteractive(callback);
if (categorize)
d->search->setFilter(new CppSearchResultFilter);
if (d->replacementData) {
@@ -150,7 +152,8 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d
connect(d->search, &SearchResult::activated, [](const SearchResultItem& item) {
EditorManager::openEditorAtSearchResult(item);
});
- SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
+ if (d->search->isInteractive())
+ SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
const std::optional<MessageId> requestId = client->symbolSupport().findUsages(
document, cursor, [self = QPointer(this)](const QList<Location> &locations) {
diff --git a/src/plugins/clangcodemodel/clangdfindreferences.h b/src/plugins/clangcodemodel/clangdfindreferences.h
index ce6d472630..d904db31f4 100644
--- a/src/plugins/clangcodemodel/clangdfindreferences.h
+++ b/src/plugins/clangcodemodel/clangdfindreferences.h
@@ -27,7 +27,9 @@ class ClangdFindReferences : public QObject
public:
ClangdFindReferences(ClangdClient *client, TextEditor::TextDocument *document,
const QTextCursor &cursor, const QString &searchTerm,
- const std::optional<QString> &replacement, bool categorize);
+ const std::optional<QString> &replacement,
+ const std::function<void()> &callback,
+ bool categorize);
ClangdFindReferences(ClangdClient *client, const Utils::Link &link, Core::SearchResult *search,
const Utils::LinkHandler &callback);
~ClangdFindReferences();
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
index e9742195f8..64a8dac7bd 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
@@ -313,16 +313,17 @@ void ClangModelManagerSupport::startLocalRenaming(const CppEditor::CursorInEdito
}
void ClangModelManagerSupport::globalRename(const CppEditor::CursorInEditor &cursor,
- const QString &replacement)
+ const QString &replacement,
+ const std::function<void()> &callback)
{
if (ClangdClient * const client = clientForFile(cursor.filePath());
client && client->isFullyIndexed()) {
QTC_ASSERT(client->documentOpen(cursor.textDocument()),
client->openDocument(cursor.textDocument()));
- client->findUsages(cursor.textDocument(), cursor.cursor(), replacement);
+ client->findUsages(cursor.textDocument(), cursor.cursor(), replacement, callback);
return;
}
- CppModelManager::globalRename(cursor, replacement, CppModelManager::Backend::Builtin);
+ CppModelManager::globalRename(cursor, replacement, callback, CppModelManager::Backend::Builtin);
}
void ClangModelManagerSupport::findUsages(const CppEditor::CursorInEditor &cursor) const
@@ -331,7 +332,7 @@ void ClangModelManagerSupport::findUsages(const CppEditor::CursorInEditor &curso
client && client->isFullyIndexed()) {
QTC_ASSERT(client->documentOpen(cursor.textDocument()),
client->openDocument(cursor.textDocument()));
- client->findUsages(cursor.textDocument(), cursor.cursor(), {});
+ client->findUsages(cursor.textDocument(), cursor.cursor(), {}, {});
return;
}
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h
index 983d8e89ea..a901b4407e 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h
@@ -60,7 +60,8 @@ private:
void startLocalRenaming(const CppEditor::CursorInEditor &data,
const CppEditor::ProjectPart *projectPart,
CppEditor::RenameCallback &&renameSymbolsCallback) override;
- void globalRename(const CppEditor::CursorInEditor &cursor, const QString &replacement) override;
+ void globalRename(const CppEditor::CursorInEditor &cursor, const QString &replacement,
+ const std::function<void()> &callback) override;
void findUsages(const CppEditor::CursorInEditor &cursor) const override;
void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit) override;
void checkUnused(const Utils::Link &link, Core::SearchResult *search,
diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp
index affeb8db5f..c2517c1357 100644
--- a/src/plugins/clangcodemodel/test/clangdtests.cpp
+++ b/src/plugins/clangcodemodel/test/clangdtests.cpp
@@ -294,7 +294,7 @@ void ClangdTestFindReferences::test()
QVERIFY(doc);
QTextCursor cursor(doc->document());
cursor.setPosition(pos);
- client()->findUsages(doc, cursor, {});
+ client()->findUsages(doc, cursor, {}, {});
QVERIFY(waitForSignalOrTimeout(client(), &ClangdClient::findUsagesDone, timeOutInMs()));
QCOMPARE(m_actualResults.size(), expectedResults.size());
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
index 37cc9ff29f..daef1d30da 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
@@ -1401,4 +1401,11 @@ void CMakeBuildSystem::runGenerator(Id id)
proc->start();
}
+ExtraCompiler *CMakeBuildSystem::extraCompilerForSource(const Utils::FilePath &source)
+{
+ return Utils::findOrDefault(m_extraCompilers, [source](ExtraCompiler *ec) {
+ return ec->source() == source;
+ });
+}
+
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
index e9b1acedc7..ec257ea09c 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
@@ -122,6 +122,7 @@ signals:
private:
QList<QPair<Utils::Id, QString>> generators() const override;
void runGenerator(Utils::Id id) override;
+ ProjectExplorer::ExtraCompiler *extraCompilerForSource(const Utils::FilePath &source) override;
enum ForceEnabledChanged { False, True };
void clearError(ForceEnabledChanged fec = ForceEnabledChanged::False);
diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp
index 0d590743b5..bb16aaf23d 100644
--- a/src/plugins/coreplugin/find/searchresultwidget.cpp
+++ b/src/plugins/coreplugin/find/searchresultwidget.cpp
@@ -468,12 +468,16 @@ void SearchResultWidget::handleReplaceButton()
{
// check if button is actually enabled, because this is also triggered
// by pressing return in replace line edit
- if (m_replaceButton->isEnabled()) {
- m_infoBar.clear();
- setShowReplaceUI(false);
- emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(),
- m_preserveCaseSupported && m_preserveCaseCheck->isChecked());
- }
+ if (m_replaceButton->isEnabled())
+ doReplace();
+}
+
+void SearchResultWidget::doReplace()
+{
+ m_infoBar.clear();
+ setShowReplaceUI(false);
+ emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(),
+ m_preserveCaseSupported && m_preserveCaseCheck->isChecked());
}
void SearchResultWidget::cancel()
diff --git a/src/plugins/coreplugin/find/searchresultwidget.h b/src/plugins/coreplugin/find/searchresultwidget.h
index f761dde0e1..f10bd5dd67 100644
--- a/src/plugins/coreplugin/find/searchresultwidget.h
+++ b/src/plugins/coreplugin/find/searchresultwidget.h
@@ -39,6 +39,7 @@ public:
void setSupportsReplace(bool replaceSupported, const QString &group);
bool supportsReplace() const;
+ void triggerReplace() { doReplace(); }
void setTextToReplace(const QString &textToReplace);
QString textToReplace() const;
@@ -91,6 +92,7 @@ signals:
private:
void handleJumpToSearchResult(const SearchResultItem &item);
void handleReplaceButton();
+ void doReplace();
void cancel();
void searchAgain();
diff --git a/src/plugins/coreplugin/find/searchresultwindow.cpp b/src/plugins/coreplugin/find/searchresultwindow.cpp
index 29c1953a08..3b2ea6e8ae 100644
--- a/src/plugins/coreplugin/find/searchresultwindow.cpp
+++ b/src/plugins/coreplugin/find/searchresultwindow.cpp
@@ -850,6 +850,12 @@ void SearchResult::setFilter(SearchResultFilter *filter)
void SearchResult::finishSearch(bool canceled, const QString &reason)
{
m_widget->finishSearch(canceled, reason);
+ if (m_finishedHandler) {
+ if (!canceled)
+ m_widget->triggerReplace();
+ m_finishedHandler();
+ m_finishedHandler = {};
+ }
}
/*!
@@ -893,6 +899,13 @@ void SearchResult::popup()
m_widget->sendRequestPopup();
}
+void Core::SearchResult::makeNonInteractive(const std::function<void ()> &callback)
+{
+ QTC_ASSERT(callback, return);
+ m_widget->setEnabled(false);
+ m_finishedHandler = callback;
+}
+
} // namespace Core
#include "searchresultwindow.moc"
diff --git a/src/plugins/coreplugin/find/searchresultwindow.h b/src/plugins/coreplugin/find/searchresultwindow.h
index e8758e657c..da1bcf67e4 100644
--- a/src/plugins/coreplugin/find/searchresultwindow.h
+++ b/src/plugins/coreplugin/find/searchresultwindow.h
@@ -12,6 +12,8 @@
#include <QStringList>
#include <QIcon>
+#include <functional>
+
QT_BEGIN_NAMESPACE
class QFont;
QT_END_NAMESPACE
@@ -53,6 +55,8 @@ public:
void setSearchAgainSupported(bool supported);
QWidget *additionalReplaceWidget() const;
void setAdditionalReplaceWidget(QWidget *widget);
+ void makeNonInteractive(const std::function<void()> &callback);
+ bool isInteractive() const { return !m_finishedHandler; }
public slots:
void addResult(const SearchResultItem &item);
@@ -83,6 +87,7 @@ private:
private:
Internal::SearchResultWidget *m_widget;
QVariant m_userData;
+ std::function<void()> m_finishedHandler;
};
class CORE_EXPORT SearchResultWindow : public IOutputPane
diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp
index 3c6900e58f..c59fd475bd 100644
--- a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp
+++ b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp
@@ -142,7 +142,8 @@ void BuiltinModelManagerSupport::startLocalRenaming(const CursorInEditor &data,
}
void BuiltinModelManagerSupport::globalRename(const CursorInEditor &data,
- const QString &replacement)
+ const QString &replacement,
+ const std::function<void()> &callback)
{
CppModelManager *modelManager = CppModelManager::instance();
if (!modelManager)
@@ -161,7 +162,7 @@ void BuiltinModelManagerSupport::globalRename(const CursorInEditor &data,
Internal::CanonicalSymbol cs(info.doc, info.snapshot);
CPlusPlus::Symbol *canonicalSymbol = cs(cursor);
if (canonicalSymbol)
- modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
+ modelManager->renameUsages(canonicalSymbol, cs.context(), replacement, callback);
}
}
diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h
index 3518c33401..04de866d5c 100644
--- a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h
+++ b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h
@@ -38,7 +38,8 @@ private:
void startLocalRenaming(const CursorInEditor &data,
const ProjectPart *projectPart,
RenameCallback &&renameSymbolsCallback) override;
- void globalRename(const CursorInEditor &data, const QString &replacement) override;
+ void globalRename(const CursorInEditor &data, const QString &replacement,
+ const std::function<void()> &callback) override;
void findUsages(const CursorInEditor &data) const override;
void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit) override;
void checkUnused(const Utils::Link &link, Core::SearchResult *search,
diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp
index acf4e88fef..fe693bbbfc 100644
--- a/src/plugins/cppeditor/cppeditorwidget.cpp
+++ b/src/plugins/cppeditor/cppeditorwidget.cpp
@@ -619,6 +619,16 @@ void CppEditorWidget::renameUsages(const QString &replacement, QTextCursor curso
d->m_modelManager->globalRename(cursorInEditor, replacement);
}
+void CppEditorWidget::renameUsages(const Utils::FilePath &filePath, const QString &replacement,
+ QTextCursor cursor, const std::function<void ()> &callback)
+{
+ if (cursor.isNull())
+ cursor = textCursor();
+ CursorInEditor cursorInEditor{cursor, filePath, this, textDocument()};
+ QPointer<CppEditorWidget> cppEditorWidget = this;
+ d->m_modelManager->globalRename(cursorInEditor, replacement, callback);
+}
+
bool CppEditorWidget::selectBlockUp()
{
if (!behaviorSettings().m_smartSelectionChanging)
@@ -1160,7 +1170,7 @@ void CppEditorWidget::updateSemanticInfo()
void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo,
bool updateUseSelectionSynchronously)
{
- if (semanticInfo.revision != documentRevision())
+ if (semanticInfo.revision < documentRevision())
return;
d->m_lastSemanticInfo = semanticInfo;
diff --git a/src/plugins/cppeditor/cppeditorwidget.h b/src/plugins/cppeditor/cppeditorwidget.h
index 3270687201..a42d786ead 100644
--- a/src/plugins/cppeditor/cppeditorwidget.h
+++ b/src/plugins/cppeditor/cppeditorwidget.h
@@ -10,6 +10,8 @@
#include <QScopedPointer>
+#include <functional>
+
namespace TextEditor {
class IAssistProposal;
class IAssistProvider;
@@ -61,6 +63,10 @@ public:
void findUsages(QTextCursor cursor);
void renameUsages(const QString &replacement = QString(),
QTextCursor cursor = QTextCursor());
+ void renameUsages(const Utils::FilePath &filePath,
+ const QString &replacement = QString(),
+ QTextCursor cursor = QTextCursor(),
+ const std::function<void()> &callback = {});
void renameSymbolUnderCursor() override;
bool selectBlockUp() override;
diff --git a/src/plugins/cppeditor/cppfindreferences.cpp b/src/plugins/cppeditor/cppfindreferences.cpp
index f6116f62d1..940eb358e9 100644
--- a/src/plugins/cppeditor/cppfindreferences.cpp
+++ b/src/plugins/cppeditor/cppfindreferences.cpp
@@ -368,12 +368,13 @@ static void find_helper(QFutureInterface<CPlusPlus::Usage> &future,
void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
const CPlusPlus::LookupContext &context)
{
- findUsages(symbol, context, QString(), false);
+ findUsages(symbol, context, QString(), {}, false);
}
void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
const CPlusPlus::LookupContext &context,
const QString &replacement,
+ const std::function<void()> &callback,
bool replace)
{
CPlusPlus::Overview overview;
@@ -385,6 +386,8 @@ void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
SearchResultWindow::PreserveCaseDisabled,
QLatin1String("CppEditor"));
search->setTextToReplace(replacement);
+ if (callback)
+ search->makeNonInteractive(callback);
if (codeModelSettings()->categorizeFindReferences())
search->setFilter(new CppSearchResultFilter);
setupSearch(search);
@@ -408,12 +411,13 @@ void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
void CppFindReferences::renameUsages(CPlusPlus::Symbol *symbol,
const CPlusPlus::LookupContext &context,
- const QString &replacement)
+ const QString &replacement,
+ const std::function<void()> &callback)
{
if (const CPlusPlus::Identifier *id = symbol->identifier()) {
const QString textToReplace = replacement.isEmpty()
? QString::fromUtf8(id->chars(), id->size()) : replacement;
- findUsages(symbol, context, textToReplace, true);
+ findUsages(symbol, context, textToReplace, callback, true);
}
}
@@ -429,7 +433,8 @@ void CppFindReferences::findAll_helper(SearchResult *search, CPlusPlus::Symbol *
Core::EditorManager::openEditorAtSearchResult(item);
});
- SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
+ if (search->isInteractive())
+ SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
const WorkingCopy workingCopy = m_modelManager->workingCopy();
QFuture<CPlusPlus::Usage> result;
result = Utils::runAsync(m_modelManager->sharedThreadPool(), find_helper,
diff --git a/src/plugins/cppeditor/cppfindreferences.h b/src/plugins/cppeditor/cppfindreferences.h
index fc76888ad0..54f40bf4cf 100644
--- a/src/plugins/cppeditor/cppfindreferences.h
+++ b/src/plugins/cppeditor/cppfindreferences.h
@@ -14,6 +14,8 @@
#include <QPointer>
#include <QFuture>
+#include <functional>
+
QT_FORWARD_DECLARE_CLASS(QTimer)
namespace Core {
@@ -65,7 +67,8 @@ public:
public:
void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context);
void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
- const QString &replacement = QString());
+ const QString &replacement = QString(),
+ const std::function<void()> &callback = {});
void findMacroUses(const CPlusPlus::Macro &macro);
void renameMacroUses(const CPlusPlus::Macro &macro, const QString &replacement = QString());
@@ -80,7 +83,8 @@ private:
void searchAgain(Core::SearchResult *search);
void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
- const QString &replacement, bool replace);
+ const QString &replacement, const std::function<void ()> &callback,
+ bool replace);
void findMacroUses(const CPlusPlus::Macro &macro, const QString &replacement,
bool replace);
void findAll_helper(Core::SearchResult *search, CPlusPlus::Symbol *symbol,
diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp
index f8dc3173c2..9c7500a6c4 100644
--- a/src/plugins/cppeditor/cppmodelmanager.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager.cpp
@@ -6,6 +6,7 @@
#include "abstracteditorsupport.h"
#include "baseeditordocumentprocessor.h"
#include "compileroptionsbuilder.h"
+#include "cppcanonicalsymbol.h"
#include "cppcodemodelinspectordumper.h"
#include "cppcodemodelsettings.h"
#include "cppcurrentdocumentfilter.h"
@@ -325,9 +326,9 @@ void CppModelManager::startLocalRenaming(const CursorInEditor &data,
}
void CppModelManager::globalRename(const CursorInEditor &data, const QString &replacement,
- Backend backend)
+ const std::function<void()> &callback, Backend backend)
{
- instance()->modelManagerSupport(backend)->globalRename(data, replacement);
+ instance()->modelManagerSupport(backend)->globalRename(data, replacement, callback);
}
void CppModelManager::findUsages(const CursorInEditor &data, Backend backend)
@@ -1170,10 +1171,21 @@ void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context)
void CppModelManager::renameUsages(Symbol *symbol,
const LookupContext &context,
- const QString &replacement)
+ const QString &replacement,
+ const std::function<void()> &callback)
{
if (symbol->identifier())
- d->m_findReferences->renameUsages(symbol, context, replacement);
+ d->m_findReferences->renameUsages(symbol, context, replacement, callback);
+}
+
+void CppModelManager::renameUsages(const Document::Ptr &doc, const QTextCursor &cursor,
+ const Snapshot &snapshot, const QString &replacement,
+ const std::function<void ()> &callback)
+{
+ Internal::CanonicalSymbol cs(doc, snapshot);
+ CPlusPlus::Symbol *canonicalSymbol = cs(cursor);
+ if (canonicalSymbol)
+ renameUsages(canonicalSymbol, cs.context(), replacement, callback);
}
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
diff --git a/src/plugins/cppeditor/cppmodelmanager.h b/src/plugins/cppeditor/cppmodelmanager.h
index 3d3cc2eca1..2929d615e6 100644
--- a/src/plugins/cppeditor/cppmodelmanager.h
+++ b/src/plugins/cppeditor/cppmodelmanager.h
@@ -19,6 +19,7 @@
#include <QObject>
#include <QStringList>
+#include <functional>
#include <memory>
namespace Core {
@@ -149,7 +150,13 @@ public:
int position) const;
void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
- const QString &replacement = QString());
+ const QString &replacement = QString(),
+ const std::function<void()> &callback = {});
+ void renameUsages(const CPlusPlus::Document::Ptr &doc,
+ const QTextCursor &cursor,
+ const CPlusPlus::Snapshot &snapshot,
+ const QString &replacement,
+ const std::function<void()> &callback);
void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context);
void findMacroUsages(const CPlusPlus::Macro &macro);
@@ -178,6 +185,7 @@ public:
RenameCallback &&renameSymbolsCallback,
Backend backend = Backend::Best);
static void globalRename(const CursorInEditor &data, const QString &replacement,
+ const std::function<void()> &callback = {},
Backend backend = Backend::Best);
static void findUsages(const CursorInEditor &data, Backend backend = Backend::Best);
static void switchHeaderSource(bool inNextSplit, Backend backend = Backend::Best);
diff --git a/src/plugins/cppeditor/cppmodelmanagersupport.h b/src/plugins/cppeditor/cppmodelmanagersupport.h
index 387d86272e..392712d86a 100644
--- a/src/plugins/cppeditor/cppmodelmanagersupport.h
+++ b/src/plugins/cppeditor/cppmodelmanagersupport.h
@@ -11,6 +11,7 @@
#include <QSharedPointer>
#include <QString>
+#include <functional>
#include <memory>
namespace Core { class SearchResult; }
@@ -49,7 +50,8 @@ public:
virtual void startLocalRenaming(const CursorInEditor &data,
const ProjectPart *projectPart,
RenameCallback &&renameSymbolsCallback) = 0;
- virtual void globalRename(const CursorInEditor &data, const QString &replacement) = 0;
+ virtual void globalRename(const CursorInEditor &data, const QString &replacement,
+ const std::function<void()> &callback) = 0;
virtual void findUsages(const CursorInEditor &data) const = 0;
virtual void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit) = 0;
diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp
index c2dd4ed45a..db96523c36 100644
--- a/src/plugins/designer/qtcreatorintegration.cpp
+++ b/src/plugins/designer/qtcreatorintegration.cpp
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "designertr.h"
-#include "editordata.h"
#include "formeditorw.h"
#include "formwindoweditor.h"
#include "qtcreatorintegration.h"
@@ -10,7 +9,9 @@
#include <designer/cpp/formclasswizardpage.h>
#include <cppeditor/cppeditorconstants.h>
+#include <cppeditor/cppeditorwidget.h>
#include <cppeditor/cppmodelmanager.h>
+#include <cppeditor/cppsemanticinfo.h>
#include <cppeditor/cpptoolsreuse.h>
#include <cppeditor/cppworkingcopy.h>
#include <cppeditor/insertionpointlocator.h>
@@ -19,28 +20,38 @@
#include <cplusplus/Overview.h>
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/messagemanager.h>
#include <texteditor/texteditor.h>
#include <texteditor/textdocument.h>
+#include <projectexplorer/buildsystem.h>
+#include <projectexplorer/extracompiler.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/session.h>
+#include <projectexplorer/target.h>
+#include <utils/algorithm.h>
#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
+#include <utils/temporaryfile.h>
#include <QDesignerFormWindowInterface>
#include <QDesignerFormEditorInterface>
-
-#include <QMessageBox>
-
-#include <QFileInfo>
-#include <QDir>
#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QLoggingCategory>
+#include <QMessageBox>
+#include <QHash>
#include <QUrl>
+#include <memory>
+
enum { indentation = 4 };
+Q_LOGGING_CATEGORY(log, "qtc.designer", QtWarningMsg);
+
using namespace Designer::Internal;
using namespace CPlusPlus;
using namespace TextEditor;
@@ -60,8 +71,23 @@ static QString msgClassNotFound(const QString &uiClassName, const QList<Document
.arg(uiClassName, files);
}
+static void reportRenamingError(const QString &oldName, const QString &reason)
+{
+ Core::MessageManager::writeFlashing(
+ Designer::Tr::tr("Cannot rename UI symbol \"%1\" in C++ files: %2")
+ .arg(oldName, reason));
+}
+
+class QtCreatorIntegration::Private
+{
+public:
+ // See QTCREATORBUG-19141 for why this is necessary.
+ QHash<QDesignerFormWindowInterface *, QPointer<ExtraCompiler>> extraCompilers;
+ std::optional<bool> showPropertyEditorRenameWarning = false;
+};
+
QtCreatorIntegration::QtCreatorIntegration(QDesignerFormEditorInterface *core, QObject *parent)
- : QDesignerIntegration(core, parent)
+ : QDesignerIntegration(core, parent), d(new Private)
{
setResourceFileWatcherBehaviour(ReloadResourceFileSilently);
Feature f = features();
@@ -77,6 +103,44 @@ QtCreatorIntegration::QtCreatorIntegration(QDesignerFormEditorInterface *core, Q
slotSyncSettingsToDesigner();
connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
this, &QtCreatorIntegration::slotSyncSettingsToDesigner);
+
+ // The problem is as follows:
+ // - If the user edits the object name in the property editor, the objectNameChanged() signal
+ // is emitted for every keystroke (QTCREATORBUG-19141). We should not try to rename
+ // in that case, because the signals will likely come in faster than the renaming
+ // procedure takes, putting the code model in some non-deterministic state.
+ // - Unfortunately, this condition is not trivial to detect, because the propertyChanged()
+ // signal is (somewhat surprisingly) emitted *after* objectNameChanged().
+ // - We can also not simply use a queued connection for objectNameChanged(), because then
+ // the ExtraCompiler might have run before our handler, and we won't find the old
+ // object name in the source code anymore.
+ // The solution is as follows:
+ // - Upon receiving objectNameChanged(), we retrieve the corresponding ExtraCompiler,
+ // block it and store it away. Then we invoke the actual handler delayed.
+ // - Upon receiving propertyChanged(), we check whether it refers to an object name change.
+ // If it does, we unblock the ExtraCompiler and remove it from our map.
+ // - When the real handler runs, it first checks for the ExtraCompiler. If it is not found,
+ // we don't do anything. Otherwise the actual renaming procedure is run.
+ connect(this, &QtCreatorIntegration::objectNameChanged,
+ this, &QtCreatorIntegration::handleSymbolRenameStage1);
+ connect(this, &QtCreatorIntegration::propertyChanged,
+ this, [this](QDesignerFormWindowInterface *formWindow, const QString &name,
+ const QVariant &) {
+ if (name == "objectName") {
+ if (const auto extraCompiler = d->extraCompilers.find(formWindow);
+ extraCompiler != d->extraCompilers.end()) {
+ (*extraCompiler)->unblock();
+ d->extraCompilers.erase(extraCompiler);
+ if (d->showPropertyEditorRenameWarning)
+ d->showPropertyEditorRenameWarning = true;
+ }
+ }
+ });
+}
+
+QtCreatorIntegration::~QtCreatorIntegration()
+{
+ delete d;
}
void QtCreatorIntegration::slotDesignerHelpRequested(const QString &manual, const QString &document)
@@ -565,6 +629,162 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
return false;
}
+void QtCreatorIntegration::handleSymbolRenameStage1(
+ QDesignerFormWindowInterface *formWindow, QObject *object,
+ const QString &newName, const QString &oldName)
+{
+ const FilePath uiFile = FilePath::fromString(formWindow->fileName());
+ qCDebug(log) << Q_FUNC_INFO << uiFile << object << oldName << newName;
+ if (newName.isEmpty() || newName == oldName)
+ return;
+
+ // Get ExtraCompiler.
+ const Project * const project = SessionManager::projectForFile(uiFile);
+ if (!project) {
+ return reportRenamingError(oldName, Designer::Tr::tr("File \"%1\" not found in project.")
+ .arg(uiFile.toUserOutput()));
+ }
+ const Target * const target = project->activeTarget();
+ if (!target)
+ return reportRenamingError(oldName, Designer::Tr::tr("No active target."));
+ BuildSystem * const buildSystem = target->buildSystem();
+ if (!buildSystem)
+ return reportRenamingError(oldName, Designer::Tr::tr("No active build system."));
+ ExtraCompiler * const ec = buildSystem->extraCompilerForSource(uiFile);
+ if (!ec)
+ return reportRenamingError(oldName, Designer::Tr::tr("Failed to find the ui header."));
+ ec->block();
+ d->extraCompilers.insert(formWindow, ec);
+ qCDebug(log) << "\tfound extra compiler, scheduling stage 2";
+ QMetaObject::invokeMethod(this, [this, formWindow, newName, oldName] {
+ handleSymbolRenameStage2(formWindow, newName, oldName);
+ }, Qt::QueuedConnection);
+}
+
+void QtCreatorIntegration::handleSymbolRenameStage2(
+ QDesignerFormWindowInterface *formWindow, const QString &newName, const QString &oldName)
+{
+ // Retrieve and check previously stored ExtraCompiler.
+ ExtraCompiler * const ec = d->extraCompilers.take(formWindow);
+ if (!ec) {
+ qCDebug(log) << "\tchange came from property editor, ignoring";
+ if (d->showPropertyEditorRenameWarning && *d->showPropertyEditorRenameWarning) {
+ d->showPropertyEditorRenameWarning.reset();
+ reportRenamingError(oldName, Designer::Tr::tr("Renaming via the property editor "
+ "cannot be synced with C++ code; see QTCREATORBUG-19141."
+ " This message will not be repeated."));
+ }
+ return;
+ }
+
+ class ResourceHandler {
+ public:
+ ResourceHandler(ExtraCompiler *ec) : m_ec(ec) {}
+ void setEditor(BaseTextEditor *editorToClose) { m_editorToClose = editorToClose; }
+ void setTempFile(std::unique_ptr<TemporaryFile> &&tempFile) {
+ m_tempFile = std::move(tempFile);
+ }
+ ~ResourceHandler()
+ {
+ if (m_ec)
+ m_ec->unblock();
+ if (m_editorToClose)
+ Core::EditorManager::closeEditors({m_editorToClose}, false);
+ }
+ private:
+ const QPointer<ExtraCompiler> m_ec;
+ QPointer<BaseTextEditor> m_editorToClose;
+ std::unique_ptr<TemporaryFile> m_tempFile;
+ };
+ const auto resourceHandler = std::make_shared<ResourceHandler>(ec);
+
+ QTC_ASSERT(ec->targets().size() == 1, return);
+ const FilePath uiHeader = ec->targets().first();
+ qCDebug(log) << '\t' << uiHeader;
+ const QByteArray virtualContent = ec->content(uiHeader);
+ if (virtualContent.isEmpty()) {
+ qCDebug(log) << "\textra compiler unexpectedly has no contents";
+ return reportRenamingError(oldName,
+ Designer::Tr::tr("Failed to retrieve ui header contents."));
+ }
+
+ // Secretly open ui header file contents in editor.
+ // Use a temp file rather than the actual ui header path.
+ const auto openFlags = Core::EditorManager::DoNotMakeVisible
+ | Core::EditorManager::DoNotChangeCurrentEditor;
+ std::unique_ptr<TemporaryFile> tempFile
+ = std::make_unique<TemporaryFile>("XXXXXX" + uiHeader.fileName());
+ QTC_ASSERT(tempFile->open(), return);
+ qCDebug(log) << '\t' << tempFile->fileName();
+ const auto editor = qobject_cast<BaseTextEditor *>(
+ Core::EditorManager::openEditor(FilePath::fromString(tempFile->fileName()), {},
+ openFlags));
+ QTC_ASSERT(editor, return);
+ resourceHandler->setTempFile(std::move(tempFile));
+ resourceHandler->setEditor(editor);
+
+ const auto editorWidget = qobject_cast<CppEditor::CppEditorWidget *>(editor->editorWidget());
+ QTC_ASSERT(editorWidget && editorWidget->textDocument(), return);
+
+ // Parse temp file with built-in code model. Pretend it's the real ui header.
+ // In the case of clangd, this entails doing a "virtual rename" on the TextDocument,
+ // as the LanguageClient cannot be forced into taking a document and assuming a different
+ // file path.
+ const bool usesClangd = CppEditor::CppModelManager::usesClangd(editorWidget->textDocument());
+ if (usesClangd)
+ editorWidget->textDocument()->setFilePath(uiHeader);
+ editorWidget->textDocument()->setPlainText(QString::fromUtf8(virtualContent));
+ Snapshot snapshot = CppEditor::CppModelManager::instance()->snapshot();
+ snapshot.remove(uiHeader);
+ snapshot.remove(editor->textDocument()->filePath());
+ const Document::Ptr cppDoc = snapshot.preprocessedDocument(virtualContent, uiHeader);
+ cppDoc->check();
+ QTC_ASSERT(cppDoc && cppDoc->isParsed(), return);
+
+ // Locate old identifier in ui header.
+ const QByteArray oldNameBa = oldName.toUtf8();
+ const Identifier oldIdentifier(oldNameBa.constData(), oldNameBa.size());
+ QList<const Scope *> scopes{cppDoc->globalNamespace()};
+ while (!scopes.isEmpty()) {
+ const Scope * const scope = scopes.takeFirst();
+ qCDebug(log) << '\t' << scope->memberCount();
+ for (int i = 0; i < scope->memberCount(); ++i) {
+ Symbol * const symbol = scope->memberAt(i);
+ if (const Scope * const s = symbol->asScope())
+ scopes << s;
+ if (symbol->asNamespace())
+ continue;
+ qCDebug(log) << '\t' << Overview().prettyName(symbol->name());
+ if (!symbol->name()->match(&oldIdentifier))
+ continue;
+ QTextCursor cursor(editorWidget->textCursor());
+ cursor.setPosition(cppDoc->translationUnit()->getTokenPositionInDocument(
+ symbol->sourceLocation(), editorWidget->document()));
+ qCDebug(log) << '\t' << cursor.position() << cursor.blockNumber()
+ << cursor.positionInBlock();
+
+ // Trigger non-interactive renaming. The callback is destructed after invocation,
+ // closing the editor, removing the temp file and unblocking the extra compiler.
+ // For the built-in code model, we must access the model manager directly,
+ // as otherwise our file path trickery would be found out.
+ const auto callback = [resourceHandler] { };
+ if (usesClangd) {
+ qCDebug(log) << "renaming with clangd";
+ editorWidget->renameUsages(uiHeader, newName, cursor, callback);
+ } else {
+ qCDebug(log) << "renaming with built-in code model";
+ snapshot.insert(cppDoc);
+ snapshot.updateDependencyTable();
+ CppEditor::CppModelManager::instance()->renameUsages(cppDoc, cursor, snapshot,
+ newName, callback);
+ }
+ return;
+ }
+ }
+ reportRenamingError(oldName,
+ Designer::Tr::tr("Failed to locate corresponding symbol in ui header."));
+}
+
void QtCreatorIntegration::slotSyncSettingsToDesigner()
{
// Set promotion-relevant parameters on integration.
diff --git a/src/plugins/designer/qtcreatorintegration.h b/src/plugins/designer/qtcreatorintegration.h
index 4789d28bfc..c27d296ab0 100644
--- a/src/plugins/designer/qtcreatorintegration.h
+++ b/src/plugins/designer/qtcreatorintegration.h
@@ -17,6 +17,7 @@ class QtCreatorIntegration : public QDesignerIntegration
public:
explicit QtCreatorIntegration(QDesignerFormEditorInterface *core, QObject *parent = nullptr);
+ ~QtCreatorIntegration();
QWidget *containerWindow(QWidget *widget) const override;
@@ -36,6 +37,13 @@ private:
const QString &signalSignature,
const QStringList &parameterNames,
QString *errorMessage);
+ void handleSymbolRenameStage1(QDesignerFormWindowInterface *formWindow, QObject *object,
+ const QString &newName, const QString &oldName);
+ void handleSymbolRenameStage2(QDesignerFormWindowInterface *formWindow,
+ const QString &newName, const QString &oldName);
+
+ class Private;
+ Private * const d;
};
} // namespace Internal
diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp
index a70b75b547..995ca770f3 100644
--- a/src/plugins/languageclient/languageclientsymbolsupport.cpp
+++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp
@@ -295,7 +295,8 @@ void SymbolSupport::handleFindReferencesResponse(const FindReferencesRequest::Re
Core::EditorManager::openEditorAtSearchResult(item);
});
search->finishSearch(false);
- search->popup();
+ if (search->isInteractive())
+ search->popup();
}
}
@@ -365,6 +366,7 @@ bool SymbolSupport::supportsRename(TextEditor::TextDocument *document)
void SymbolSupport::renameSymbol(TextEditor::TextDocument *document,
const QTextCursor &cursor,
const QString &newSymbolName,
+ const std::function<void ()> &callback,
bool preferLowerCaseFileNames)
{
const TextDocumentPositionParams params = generateDocPosParams(document, cursor, m_client);
@@ -376,17 +378,19 @@ void SymbolSupport::renameSymbol(TextEditor::TextDocument *document,
if (!LanguageClient::supportsRename(m_client, document, prepareSupported)) {
const QString error = Tr::tr("Renaming is not supported with %1").arg(m_client->name());
createSearch(params, derivePlaceholder(oldSymbolName, newSymbolName),
- {}, {})->finishSearch(true, error);
+ {}, callback, {})->finishSearch(true, error);
} else if (prepareSupported) {
requestPrepareRename(document,
generateDocPosParams(document, cursor, m_client),
newSymbolName,
oldSymbolName,
+ callback,
preferLowerCaseFileNames);
} else {
startRenameSymbol(generateDocPosParams(document, cursor, m_client),
newSymbolName,
oldSymbolName,
+ callback,
preferLowerCaseFileNames);
}
}
@@ -395,6 +399,7 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document,
const TextDocumentPositionParams &params,
const QString &placeholder,
const QString &oldSymbolName,
+ const std::function<void()> &callback,
bool preferLowerCaseFileNames)
{
PrepareRenameRequest request(params);
@@ -402,13 +407,15 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document,
params,
placeholder,
oldSymbolName,
+ callback,
preferLowerCaseFileNames,
document = QPointer<TextEditor::TextDocument>(document)](
const PrepareRenameRequest::Response &response) {
const std::optional<PrepareRenameRequest::Response::Error> &error = response.error();
if (error.has_value()) {
m_client->log(*error);
- createSearch(params, placeholder, {}, {})->finishSearch(true, error->toString());
+ createSearch(params, placeholder, {}, callback, {})
+ ->finishSearch(true, error->toString());
}
const std::optional<PrepareRenameResult> &result = response.result();
@@ -419,6 +426,7 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document,
placeholder.isEmpty() ? placeHolderResult.placeHolder()
: placeholder,
oldSymbolName,
+ callback,
preferLowerCaseFileNames);
} else if (std::holds_alternative<Range>(*result)) {
auto range = std::get<Range>(*result);
@@ -429,9 +437,11 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document,
startRenameSymbol(params,
derivePlaceholder(reportedSymbolName, placeholder),
reportedSymbolName,
+ callback,
preferLowerCaseFileNames);
} else {
- startRenameSymbol(params, placeholder, oldSymbolName, preferLowerCaseFileNames);
+ startRenameSymbol(params, placeholder, oldSymbolName, callback,
+ preferLowerCaseFileNames);
}
}
}
@@ -449,7 +459,8 @@ void SymbolSupport::requestRename(const TextDocumentPositionParams &positionPara
handleRenameResponse(search, response);
});
m_client->sendMessage(request);
- search->popup();
+ if (search->isInteractive())
+ search->popup();
}
QList<Core::SearchResultItem> generateReplaceItems(const WorkspaceEdit &edits,
@@ -480,6 +491,7 @@ QList<Core::SearchResultItem> generateReplaceItems(const WorkspaceEdit &edits,
Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams &positionParams,
const QString &placeholder,
const QString &oldSymbolName,
+ const std::function<void()> &callback,
bool preferLowerCaseFileNames)
{
Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch(
@@ -491,6 +503,8 @@ Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams
const auto extraWidget = new ReplaceWidget;
search->setAdditionalReplaceWidget(extraWidget);
search->setTextToReplace(placeholder);
+ if (callback)
+ search->makeNonInteractive(callback);
connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) {
Core::EditorManager::openEditorAtSearchResult(item);
@@ -521,10 +535,12 @@ Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams
void SymbolSupport::startRenameSymbol(const TextDocumentPositionParams &positionParams,
const QString &placeholder,
const QString &oldSymbolName,
+ const std::function<void()> &callback,
bool preferLowerCaseFileNames)
{
requestRename(positionParams,
- createSearch(positionParams, placeholder, oldSymbolName, preferLowerCaseFileNames));
+ createSearch(positionParams, placeholder, oldSymbolName, callback,
+ preferLowerCaseFileNames));
}
void SymbolSupport::handleRenameResponse(Core::SearchResult *search,
diff --git a/src/plugins/languageclient/languageclientsymbolsupport.h b/src/plugins/languageclient/languageclientsymbolsupport.h
index 864c963b8b..a4b910a9db 100644
--- a/src/plugins/languageclient/languageclientsymbolsupport.h
+++ b/src/plugins/languageclient/languageclientsymbolsupport.h
@@ -42,7 +42,9 @@ public:
bool supportsRename(TextEditor::TextDocument *document);
void renameSymbol(TextEditor::TextDocument *document, const QTextCursor &cursor,
- const QString &newSymbolName = {}, bool preferLowerCaseFileNames = true);
+ const QString &newSymbolName = {},
+ const std::function<void()> &callback = {},
+ bool preferLowerCaseFileNames = true);
static Core::Search::TextRange convertRange(const LanguageServerProtocol::Range &range);
static QStringList getFileContents(const Utils::FilePath &filePath);
@@ -61,16 +63,17 @@ private:
void requestPrepareRename(TextEditor::TextDocument *document,
const LanguageServerProtocol::TextDocumentPositionParams &params,
const QString &placeholder,
- const QString &oldSymbolName,
+ const QString &oldSymbolName, const std::function<void()> &callback,
bool preferLowerCaseFileNames);
void requestRename(const LanguageServerProtocol::TextDocumentPositionParams &positionParams,
Core::SearchResult *search);
Core::SearchResult *createSearch(const LanguageServerProtocol::TextDocumentPositionParams &positionParams,
const QString &placeholder, const QString &oldSymbolName,
+ const std::function<void()> &callback,
bool preferLowerCaseFileNames);
void startRenameSymbol(const LanguageServerProtocol::TextDocumentPositionParams &params,
const QString &placeholder, const QString &oldSymbolName,
- bool preferLowerCaseFileNames);
+ const std::function<void()> &callback, bool preferLowerCaseFileNames);
void handleRenameResponse(Core::SearchResult *search,
const LanguageServerProtocol::RenameRequest::Response &response);
void applyRename(const QList<Core::SearchResultItem> &checkedItems, Core::SearchResult *search);
diff --git a/src/plugins/projectexplorer/buildsystem.cpp b/src/plugins/projectexplorer/buildsystem.cpp
index e0e6f93dcd..000949f3e5 100644
--- a/src/plugins/projectexplorer/buildsystem.cpp
+++ b/src/plugins/projectexplorer/buildsystem.cpp
@@ -236,6 +236,12 @@ bool BuildSystem::supportsAction(Node *, ProjectAction, const Node *) const
return false;
}
+ExtraCompiler *BuildSystem::extraCompilerForSource(const Utils::FilePath &source)
+{
+ Q_UNUSED(source);
+ return nullptr;
+}
+
MakeInstallCommand BuildSystem::makeInstallCommand(const FilePath &installRoot) const
{
QTC_ASSERT(target()->project()->hasMakeInstallEquivalent(), return {});
diff --git a/src/plugins/projectexplorer/buildsystem.h b/src/plugins/projectexplorer/buildsystem.h
index b60cb2f6e4..f43eb37f7d 100644
--- a/src/plugins/projectexplorer/buildsystem.h
+++ b/src/plugins/projectexplorer/buildsystem.h
@@ -19,6 +19,7 @@ namespace ProjectExplorer {
class BuildConfiguration;
class BuildStepList;
+class ExtraCompiler;
class Node;
struct TestCaseInfo
@@ -82,6 +83,9 @@ public:
virtual bool supportsAction(Node *context, ProjectAction action, const Node *node) const;
virtual QString name() const = 0;
+ // Owned by the build system. Use only in main thread. Can go away at any time.
+ virtual ExtraCompiler *extraCompilerForSource(const Utils::FilePath &source);
+
virtual MakeInstallCommand makeInstallCommand(const Utils::FilePath &installRoot) const;
virtual Utils::FilePaths filesGeneratedFrom(const Utils::FilePath &sourceFile) const;
diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp
index 9fb7cd3ad7..ff7c061790 100644
--- a/src/plugins/projectexplorer/extracompiler.cpp
+++ b/src/plugins/projectexplorer/extracompiler.cpp
@@ -13,10 +13,12 @@
#include <utils/asynctask.h>
#include <utils/expected.h>
+#include <utils/guard.h>
#include <utils/qtcprocess.h>
#include <QDateTime>
#include <QFutureInterface>
+#include <QLoggingCategory>
#include <QThreadPool>
#include <QTimer>
@@ -26,6 +28,7 @@ namespace ProjectExplorer {
Q_GLOBAL_STATIC(QThreadPool, s_extraCompilerThreadPool);
Q_GLOBAL_STATIC(QList<ExtraCompilerFactory *>, factories);
+Q_LOGGING_CATEGORY(log, "qtc.projectexplorer.extracompiler", QtWarningMsg);
class ExtraCompilerPrivate
{
@@ -37,6 +40,7 @@ public:
Core::IEditor *lastEditor = nullptr;
QMetaObject::Connection activeBuildConfigConnection;
QMetaObject::Connection activeEnvironmentConnection;
+ Utils::Guard lock;
bool dirty = false;
QTimer timer;
@@ -55,13 +59,7 @@ ExtraCompiler::ExtraCompiler(const Project *project, const FilePath &source,
d->contents.insert(target, QByteArray());
d->timer.setSingleShot(true);
- connect(&d->timer, &QTimer::timeout, this, [this] {
- if (d->dirty && d->lastEditor) {
- d->dirty = false;
- compileContent(d->lastEditor->document()->contents());
- }
- });
-
+ connect(&d->timer, &QTimer::timeout, this, &ExtraCompiler::compileIfDirty);
connect(BuildManager::instance(), &BuildManager::buildStateChanged,
this, &ExtraCompiler::onTargetsBuilt);
@@ -163,6 +161,16 @@ void ExtraCompiler::compileImpl(const ContentProvider &provider)
d->m_taskTree->start();
}
+void ExtraCompiler::compileIfDirty()
+{
+ qCDebug(log) << Q_FUNC_INFO;
+ if (!d->lock.isLocked() && d->dirty && d->lastEditor) {
+ qCDebug(log) << '\t' << "about to compile";
+ d->dirty = false;
+ compileContent(d->lastEditor->document()->contents());
+ }
+}
+
ExtraCompiler::ContentProvider ExtraCompiler::fromFileProvider() const
{
const auto provider = [fileName = source()] {
@@ -179,6 +187,20 @@ bool ExtraCompiler::isDirty() const
return d->dirty;
}
+void ExtraCompiler::block()
+{
+ qCDebug(log) << Q_FUNC_INFO;
+ d->lock.lock();
+}
+
+void ExtraCompiler::unblock()
+{
+ qCDebug(log) << Q_FUNC_INFO;
+ d->lock.unlock();
+ if (!d->lock.isLocked() && !d->timer.isActive())
+ d->timer.start();
+}
+
void ExtraCompiler::onTargetsBuilt(Project *project)
{
if (project != d->project || BuildManager::isBuilding(project))
@@ -278,6 +300,7 @@ Utils::FutureSynchronizer *ExtraCompiler::futureSynchronizer() const
void ExtraCompiler::setContent(const FilePath &file, const QByteArray &contents)
{
+ qCDebug(log).noquote() << Q_FUNC_INFO << contents;
auto it = d->contents.find(file);
if (it != d->contents.end()) {
if (it.value() != contents) {
diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h
index 7d595b8726..34993f3940 100644
--- a/src/plugins/projectexplorer/extracompiler.h
+++ b/src/plugins/projectexplorer/extracompiler.h
@@ -55,6 +55,8 @@ public:
Utils::Tasking::TaskItem compileFileItem();
void compileFile();
bool isDirty() const;
+ void block();
+ void unblock();
signals:
void contentsChanged(const Utils::FilePath &file);
@@ -76,6 +78,7 @@ private:
ContentProvider fromFileProvider() const;
void compileContent(const QByteArray &content);
void compileImpl(const ContentProvider &provider);
+ void compileIfDirty();
virtual Utils::Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0;
const std::unique_ptr<ExtraCompilerPrivate> d;
diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp
index ef22f02c8f..286219a9b7 100644
--- a/src/plugins/qbsprojectmanager/qbsproject.cpp
+++ b/src/plugins/qbsprojectmanager/qbsproject.cpp
@@ -586,6 +586,13 @@ void QbsBuildSystem::delayParsing()
requestDelayedParse();
}
+ExtraCompiler *QbsBuildSystem::extraCompilerForSource(const Utils::FilePath &source)
+{
+ return Utils::findOrDefault(m_extraCompilers, [source](ExtraCompiler *ec) {
+ return ec->source() == source;
+ });
+}
+
void QbsBuildSystem::parseCurrentBuildConfiguration()
{
m_parsingScheduled = false;
diff --git a/src/plugins/qbsprojectmanager/qbsproject.h b/src/plugins/qbsprojectmanager/qbsproject.h
index f33d725daa..4a21caa2a9 100644
--- a/src/plugins/qbsprojectmanager/qbsproject.h
+++ b/src/plugins/qbsprojectmanager/qbsproject.h
@@ -105,6 +105,8 @@ public:
private:
friend class QbsProject;
+ ProjectExplorer::ExtraCompiler *extraCompilerForSource(const Utils::FilePath &source) override;
+
void handleQbsParsingDone(bool success);
void changeActiveTarget(ProjectExplorer::Target *t);
void prepareForParsing();
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index a1c34a528d..d7d0a854c4 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -2080,6 +2080,21 @@ QList<ExtraCompiler *> QmakeProFile::extraCompilers() const
return m_extraCompilers;
}
+ExtraCompiler *QmakeProFile::extraCompilerForSource(const Utils::FilePath &sourceFile)
+{
+ for (ExtraCompiler * const ec : std::as_const(m_extraCompilers)) {
+ if (ec->source() == sourceFile)
+ return ec;
+ }
+ for (QmakePriFile * const priFile : std::as_const(m_children)) {
+ if (const auto proFile = dynamic_cast<QmakeProFile *>(priFile)) {
+ if (ExtraCompiler * const ec = proFile->extraCompilerForSource(sourceFile))
+ return ec;
+ }
+ }
+ return nullptr;
+}
+
void QmakeProFile::setupExtraCompiler(const FilePath &buildDir,
const FileType &fileType, ExtraCompilerFactory *factory)
{
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
index 86a3a97b49..f9700b7f54 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
@@ -301,6 +301,7 @@ public:
const Utils::FilePath &sourceFile,
const ProjectExplorer::FileType &sourceFileType) const;
QList<ProjectExplorer::ExtraCompiler *> extraCompilers() const;
+ ProjectExplorer::ExtraCompiler *extraCompilerForSource(const Utils::FilePath &sourceFile);
TargetInformation targetInformation() const;
InstallsList installsList() const;
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
index 5d021f9c56..84188df03f 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
@@ -532,6 +532,11 @@ void QmakeBuildSystem::scheduleUpdateAllNowOrLater()
scheduleUpdateAll(QmakeProFile::ParseLater);
}
+ExtraCompiler *QmakeBuildSystem::extraCompilerForSource(const Utils::FilePath &source)
+{
+ return m_rootProFile->extraCompilerForSource(source);
+}
+
QmakeBuildConfiguration *QmakeBuildSystem::qmakeBuildConfiguration() const
{
return static_cast<QmakeBuildConfiguration *>(BuildSystem::buildConfiguration());
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h
index b83468a108..edf2db0edf 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.h
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.h
@@ -163,6 +163,8 @@ public:
void scheduleUpdateAllNowOrLater();
private:
+ ProjectExplorer::ExtraCompiler *extraCompilerForSource(const Utils::FilePath &source) override;
+
void scheduleUpdateAll(QmakeProFile::AsyncUpdateDelay delay);
void scheduleUpdateAllLater() { scheduleUpdateAll(QmakeProFile::ParseLater); }