From 0d909c353c54ecf2fee2d13b7d2fe19eee01591d Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 16 Jan 2023 17:03:26 +0100 Subject: [PATCH] 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: Reviewed-by: David Schulz --- src/plugins/clangcodemodel/clangdclient.cpp | 19 +- src/plugins/clangcodemodel/clangdclient.h | 3 +- .../clangcodemodel/clangdfindreferences.cpp | 7 +- .../clangcodemodel/clangdfindreferences.h | 4 +- .../clangmodelmanagersupport.cpp | 9 +- .../clangcodemodel/clangmodelmanagersupport.h | 3 +- .../clangcodemodel/test/clangdtests.cpp | 2 +- .../cmakeprojectmanager/cmakebuildsystem.cpp | 7 + .../cmakeprojectmanager/cmakebuildsystem.h | 1 + .../coreplugin/find/searchresultwidget.cpp | 16 +- .../coreplugin/find/searchresultwidget.h | 2 + .../coreplugin/find/searchresultwindow.cpp | 13 + .../coreplugin/find/searchresultwindow.h | 5 + .../cppbuiltinmodelmanagersupport.cpp | 5 +- .../cppeditor/cppbuiltinmodelmanagersupport.h | 3 +- src/plugins/cppeditor/cppeditorwidget.cpp | 12 +- src/plugins/cppeditor/cppeditorwidget.h | 6 + src/plugins/cppeditor/cppfindreferences.cpp | 13 +- src/plugins/cppeditor/cppfindreferences.h | 8 +- src/plugins/cppeditor/cppmodelmanager.cpp | 20 +- src/plugins/cppeditor/cppmodelmanager.h | 10 +- .../cppeditor/cppmodelmanagersupport.h | 4 +- src/plugins/designer/qtcreatorintegration.cpp | 234 +++++++++++++++++- src/plugins/designer/qtcreatorintegration.h | 8 + .../languageclientsymbolsupport.cpp | 28 ++- .../languageclientsymbolsupport.h | 9 +- src/plugins/projectexplorer/buildsystem.cpp | 6 + src/plugins/projectexplorer/buildsystem.h | 4 + src/plugins/projectexplorer/extracompiler.cpp | 37 ++- src/plugins/projectexplorer/extracompiler.h | 3 + src/plugins/qbsprojectmanager/qbsproject.cpp | 7 + src/plugins/qbsprojectmanager/qbsproject.h | 2 + .../qmakeprojectmanager/qmakeparsernodes.cpp | 15 ++ .../qmakeprojectmanager/qmakeparsernodes.h | 1 + .../qmakeprojectmanager/qmakeproject.cpp | 5 + .../qmakeprojectmanager/qmakeproject.h | 2 + 36 files changed, 470 insertions(+), 63 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 6735b96aeb5..6d2d2fb95dc 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 &replacement, - bool categorize); + const std::function &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 &replacement) + const std::optional &replacement, + const std::function &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 &replacement, bool categorize) + const std::optional &replacement, const std::function &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 ff64a9c3f54..772920b2776 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 &replacement); + const std::optional &replacement, + const std::function &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 2458183164b..f2102325f06 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 &replacement, bool categorize) + const std::optional &replacement, const std::function &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 requestId = client->symbolSupport().findUsages( document, cursor, [self = QPointer(this)](const QList &locations) { diff --git a/src/plugins/clangcodemodel/clangdfindreferences.h b/src/plugins/clangcodemodel/clangdfindreferences.h index ce6d4726303..d904db31f41 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 &replacement, bool categorize); + const std::optional &replacement, + const std::function &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 e9742195f8e..64a8dac7bdf 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 &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 983d8e89eab..a901b4407e2 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 &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 affeb8db5f1..c2517c13579 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 37cc9ff29f2..daef1d30daf 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 e9b1acedc73..ec257ea09c4 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -122,6 +122,7 @@ signals: private: QList> 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 0d590743b5c..bb16aaf23da 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 f761dde0e1d..f10bd5dd673 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 29c1953a084..3b2ea6e8ae9 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 &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 e8758e657ce..da1bcf67e41 100644 --- a/src/plugins/coreplugin/find/searchresultwindow.h +++ b/src/plugins/coreplugin/find/searchresultwindow.h @@ -12,6 +12,8 @@ #include #include +#include + 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 &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 m_finishedHandler; }; class CORE_EXPORT SearchResultWindow : public IOutputPane diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp index 3c6900e58f8..c59fd475bda 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 &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 3518c33401e..04de866d5ce 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 &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 acf4e88fef3..fe693bbbfc9 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 &callback) +{ + if (cursor.isNull()) + cursor = textCursor(); + CursorInEditor cursorInEditor{cursor, filePath, this, textDocument()}; + QPointer 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 32706872011..a42d786ead9 100644 --- a/src/plugins/cppeditor/cppeditorwidget.h +++ b/src/plugins/cppeditor/cppeditorwidget.h @@ -10,6 +10,8 @@ #include +#include + 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 &callback = {}); void renameSymbolUnderCursor() override; bool selectBlockUp() override; diff --git a/src/plugins/cppeditor/cppfindreferences.cpp b/src/plugins/cppeditor/cppfindreferences.cpp index f6116f62d1a..940eb358e93 100644 --- a/src/plugins/cppeditor/cppfindreferences.cpp +++ b/src/plugins/cppeditor/cppfindreferences.cpp @@ -368,12 +368,13 @@ static void find_helper(QFutureInterface &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 &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 &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 result; result = Utils::runAsync(m_modelManager->sharedThreadPool(), find_helper, diff --git a/src/plugins/cppeditor/cppfindreferences.h b/src/plugins/cppeditor/cppfindreferences.h index fc76888ad07..54f40bf4cfe 100644 --- a/src/plugins/cppeditor/cppfindreferences.h +++ b/src/plugins/cppeditor/cppfindreferences.h @@ -14,6 +14,8 @@ #include #include +#include + 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 &callback = {}); void findMacroUses(const CPlusPlus::Macro ¯o); void renameMacroUses(const CPlusPlus::Macro ¯o, 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 &callback, + bool replace); void findMacroUses(const CPlusPlus::Macro ¯o, 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 f8dc3173c20..9c7500a6c45 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 &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 &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 &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 ¯o) diff --git a/src/plugins/cppeditor/cppmodelmanager.h b/src/plugins/cppeditor/cppmodelmanager.h index 3d3cc2eca12..2929d615e62 100644 --- a/src/plugins/cppeditor/cppmodelmanager.h +++ b/src/plugins/cppeditor/cppmodelmanager.h @@ -19,6 +19,7 @@ #include #include +#include #include 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 &callback = {}); + void renameUsages(const CPlusPlus::Document::Ptr &doc, + const QTextCursor &cursor, + const CPlusPlus::Snapshot &snapshot, + const QString &replacement, + const std::function &callback); void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context); void findMacroUsages(const CPlusPlus::Macro ¯o); @@ -178,6 +185,7 @@ public: RenameCallback &&renameSymbolsCallback, Backend backend = Backend::Best); static void globalRename(const CursorInEditor &data, const QString &replacement, + const std::function &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 387d86272e0..392712d86ae 100644 --- a/src/plugins/cppeditor/cppmodelmanagersupport.h +++ b/src/plugins/cppeditor/cppmodelmanagersupport.h @@ -11,6 +11,7 @@ #include #include +#include #include 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 &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 c2dd4ed45ab..db96523c360 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 #include +#include #include +#include #include #include #include @@ -19,28 +20,38 @@ #include #include #include +#include #include #include +#include +#include #include #include #include +#include +#include #include #include #include +#include #include #include - -#include - -#include -#include #include +#include +#include +#include +#include +#include #include +#include + 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> extraCompilers; + std::optional 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 &&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 m_ec; + QPointer m_editorToClose; + std::unique_ptr m_tempFile; + }; + const auto resourceHandler = std::make_shared(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 tempFile + = std::make_unique("XXXXXX" + uiHeader.fileName()); + QTC_ASSERT(tempFile->open(), return); + qCDebug(log) << '\t' << tempFile->fileName(); + const auto editor = qobject_cast( + 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(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 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 4789d28bfc2..c27d296ab0b 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 ¶meterNames, 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 a70b75b5474..995ca770f3d 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 &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 ¶ms, const QString &placeholder, const QString &oldSymbolName, + const std::function &callback, bool preferLowerCaseFileNames) { PrepareRenameRequest request(params); @@ -402,13 +407,15 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document, params, placeholder, oldSymbolName, + callback, preferLowerCaseFileNames, document = QPointer(document)]( const PrepareRenameRequest::Response &response) { const std::optional &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 &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(*result)) { auto range = std::get(*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 generateReplaceItems(const WorkspaceEdit &edits, @@ -480,6 +491,7 @@ QList generateReplaceItems(const WorkspaceEdit &edits, Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams &positionParams, const QString &placeholder, const QString &oldSymbolName, + const std::function &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 &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 864c963b8b9..a4b910a9dbd 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 &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 ¶ms, const QString &placeholder, - const QString &oldSymbolName, + const QString &oldSymbolName, const std::function &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 &callback, bool preferLowerCaseFileNames); void startRenameSymbol(const LanguageServerProtocol::TextDocumentPositionParams ¶ms, const QString &placeholder, const QString &oldSymbolName, - bool preferLowerCaseFileNames); + const std::function &callback, bool preferLowerCaseFileNames); void handleRenameResponse(Core::SearchResult *search, const LanguageServerProtocol::RenameRequest::Response &response); void applyRename(const QList &checkedItems, Core::SearchResult *search); diff --git a/src/plugins/projectexplorer/buildsystem.cpp b/src/plugins/projectexplorer/buildsystem.cpp index e0e6f93dcd3..000949f3e51 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 b60cb2f6e4a..f43eb37f7d6 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 9fb7cd3ad78..ff7c061790e 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -13,10 +13,12 @@ #include #include +#include #include #include #include +#include #include #include @@ -26,6 +28,7 @@ namespace ProjectExplorer { Q_GLOBAL_STATIC(QThreadPool, s_extraCompilerThreadPool); Q_GLOBAL_STATIC(QList, 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 7d595b87260..34993f39406 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 d; diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index ef22f02c8f1..286219a9b7d 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 f33d725daa5..4a21caa2a92 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 a1c34a528d6..d7d0a854c44 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp @@ -2080,6 +2080,21 @@ QList 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(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 86a3a97b49f..f9700b7f549 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 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 5d021f9c56d..84188df03f9 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(BuildSystem::buildConfiguration()); diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h index b83468a1082..edf2db0edfa 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); }