From 40ba7ac16ffbf44800f59e0e2459382f07824e5f Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Fri, 15 Sep 2017 15:16:33 +0200 Subject: [PATCH] CppEditor: Avoid blocking context menu invocation Show the menu always immediately. If the needed use selections are not up to date for the refactoring actions, then trigger an async run and if that one finishes, then update the refactoring menu. In the meanwhile, show a place holder menu item showing on-going progress (Utils::ProgressIndicator). Change-Id: Iae7ab37738d79c20aeb1ccda2b1781091e90fdc3 Reviewed-by: Ivan Donchevskii Reviewed-by: Tim Jenssen --- src/plugins/cppeditor/cppeditorwidget.cpp | 54 ++++++++++++++++--- .../cppeditor/cppuseselectionsupdater.cpp | 32 +++++++---- .../cppeditor/cppuseselectionsupdater.h | 5 +- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index 37f152fe58a..c14bb541bef 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -80,8 +80,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -93,6 +94,7 @@ #include #include #include +#include enum { UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200 }; @@ -176,9 +178,11 @@ void CppEditorWidget::finalizeInitialization() &CppLocalRenaming::updateSelectionsForVariableUnderCursor); connect(&d->m_useSelectionsUpdater, &CppUseSelectionsUpdater::finished, this, - [this] (SemanticInfo::LocalUseMap localUses) { - d->m_lastSemanticInfo.localUsesUpdated = true; - d->m_lastSemanticInfo.localUses = localUses; + [this] (SemanticInfo::LocalUseMap localUses, bool success) { + if (success) { + d->m_lastSemanticInfo.localUsesUpdated = true; + d->m_lastSemanticInfo.localUses = localUses; + } }); connect(document(), &QTextDocument::contentsChange, @@ -750,6 +754,20 @@ static void addRefactoringActions(QMenu *menu, AssistInterface *iface) } } +class ProgressIndicatorMenuItem : public QWidgetAction +{ + Q_OBJECT + +public: + ProgressIndicatorMenuItem(QObject *parent) : QWidgetAction(parent) {} + +protected: + QWidget *createWidget(QWidget *parent = nullptr) override + { + return new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Small, parent); + } +}; + QMenu *CppEditorWidget::createRefactorMenu(QWidget *parent) const { auto *menu = new QMenu(tr("&Refactor"), parent); @@ -759,8 +777,30 @@ QMenu *CppEditorWidget::createRefactorMenu(QWidget *parent) const // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource())); if (isSemanticInfoValidExceptLocalUses()) { - d->m_useSelectionsUpdater.update(CppUseSelectionsUpdater::CallType::Synchronous); - addRefactoringActions(menu, createAssistInterface(QuickFix, ExplicitlyInvoked)); + d->m_useSelectionsUpdater.abortSchedule(); + + const CppUseSelectionsUpdater::RunnerInfo runnerInfo = d->m_useSelectionsUpdater.update(); + switch (runnerInfo) { + case CppUseSelectionsUpdater::RunnerInfo::AlreadyUpToDate: + addRefactoringActions(menu, createAssistInterface(QuickFix, ExplicitlyInvoked)); + break; + case CppUseSelectionsUpdater::RunnerInfo::Started: { + // Update the refactor menu once we get the results. + auto *progressIndicatorMenuItem = new ProgressIndicatorMenuItem(menu); + menu->addAction(progressIndicatorMenuItem); + + connect(&d->m_useSelectionsUpdater, &CppUseSelectionsUpdater::finished, + menu, [=] (SemanticInfo::LocalUseMap, bool success) { + QTC_CHECK(success); + menu->removeAction(progressIndicatorMenuItem); + addRefactoringActions(menu, createAssistInterface(QuickFix, ExplicitlyInvoked)); + }); + break; + } + case CppUseSelectionsUpdater::RunnerInfo::FailedToStart: + case CppUseSelectionsUpdater::RunnerInfo::Invalid: + QTC_CHECK(false && "Unexpected CppUseSelectionsUpdater runner result"); + } } return menu; @@ -1041,3 +1081,5 @@ void CppEditorWidget::invokeTextEditorWidgetAssist(TextEditor::AssistKind assist } // namespace Internal } // namespace CppEditor + +#include "cppeditorwidget.moc" diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp index 5b8d1d27dfe..da7a7a1d92a 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp +++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp @@ -66,13 +66,13 @@ void CppUseSelectionsUpdater::abortSchedule() m_timer.stop(); } -void CppUseSelectionsUpdater::update(CallType callType) +CppUseSelectionsUpdater::RunnerInfo CppUseSelectionsUpdater::update(CallType callType) { auto *cppEditorWidget = qobject_cast(m_editorWidget); - QTC_ASSERT(cppEditorWidget, return); + QTC_ASSERT(cppEditorWidget, return RunnerInfo::FailedToStart); auto *cppEditorDocument = qobject_cast(cppEditorWidget->textDocument()); - QTC_ASSERT(cppEditorDocument, return); + QTC_ASSERT(cppEditorDocument, return RunnerInfo::FailedToStart); CppTools::CursorInfoParams params; params.semanticInfo = cppEditorWidget->semanticInfo(); @@ -80,7 +80,7 @@ void CppUseSelectionsUpdater::update(CallType callType) if (callType == CallType::Asynchronous) { if (isSameIdentifierAsBefore(params.textCursor)) - return; + return RunnerInfo::AlreadyUpToDate; if (m_runnerWatcher) m_runnerWatcher->cancel(); @@ -93,25 +93,28 @@ void CppUseSelectionsUpdater::update(CallType callType) m_runnerWordStartPosition = params.textCursor.position(); m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params)); + return RunnerInfo::Started; } else { // synchronous case abortSchedule(); const int startRevision = cppEditorDocument->document()->revision(); QFuture future = cppEditorDocument->cursorInfo(params); if (future.isCanceled()) - return; + return RunnerInfo::Invalid; // QFuture::waitForFinished seems to block completely, not even // allowing to process events from QLocalSocket. while (!future.isFinished()) { if (future.isCanceled()) - return; + return RunnerInfo::Invalid; - QTC_ASSERT(startRevision == cppEditorDocument->document()->revision(), return); + QTC_ASSERT(startRevision == cppEditorDocument->document()->revision(), + return RunnerInfo::Invalid); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } processResults(future.result()); + return RunnerInfo::Invalid; } } @@ -134,18 +137,25 @@ void CppUseSelectionsUpdater::processResults(const CursorInfo &result) updateUnusedSelections(result.unusedVariablesRanges); emit selectionsForVariableUnderCursorUpdated(localVariableSelections); - emit finished(result.localUses); + emit finished(result.localUses, true); } void CppUseSelectionsUpdater::onFindUsesFinished() { - QTC_ASSERT(m_runnerWatcher, return); - if (m_runnerWatcher->isCanceled()) + QTC_ASSERT(m_runnerWatcher, + emit finished(CppTools::SemanticInfo::LocalUseMap(), false); return); + + if (m_runnerWatcher->isCanceled()) { + emit finished(CppTools::SemanticInfo::LocalUseMap(), false); return; - if (m_runnerRevision != m_editorWidget->document()->revision()) + } + if (m_runnerRevision != m_editorWidget->document()->revision()) { + emit finished(CppTools::SemanticInfo::LocalUseMap(), false); return; + } if (m_runnerWordStartPosition != Utils::Text::wordStartCursor(m_editorWidget->textCursor()).position()) { + emit finished(CppTools::SemanticInfo::LocalUseMap(), false); return; } diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.h b/src/plugins/cppeditor/cppuseselectionsupdater.h index 7e10eeb680c..ec18b222f45 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.h +++ b/src/plugins/cppeditor/cppuseselectionsupdater.h @@ -50,10 +50,11 @@ public: void abortSchedule(); enum class CallType { Synchronous, Asynchronous }; - void update(CallType callType = CallType::Asynchronous); + enum class RunnerInfo { AlreadyUpToDate, Started, FailedToStart, Invalid }; // For async case. + RunnerInfo update(CallType callType = CallType::Asynchronous); signals: - void finished(CppTools::SemanticInfo::LocalUseMap localUses); + void finished(CppTools::SemanticInfo::LocalUseMap localUses, bool success); void selectionsForVariableUnderCursorUpdated(const QList &); private: