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 <ivan.donchevskii@qt.io>
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Nikolai Kosjar
2017-09-15 15:16:33 +02:00
parent b21742b464
commit 40ba7ac16f
3 changed files with 72 additions and 19 deletions

View File

@@ -80,8 +80,9 @@
#include <cplusplus/ASTPath.h>
#include <cplusplus/FastPreprocessor.h>
#include <cplusplus/MatchingText.h>
#include <utils/textutils.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
#include <utils/textutils.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -93,6 +94,7 @@
#include <QTextEdit>
#include <QTimer>
#include <QToolButton>
#include <QWidgetAction>
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"

View File

@@ -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<CppEditorWidget *>(m_editorWidget);
QTC_ASSERT(cppEditorWidget, return);
QTC_ASSERT(cppEditorWidget, return RunnerInfo::FailedToStart);
auto *cppEditorDocument = qobject_cast<CppEditorDocument *>(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<CursorInfo> 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;
}

View File

@@ -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<QTextEdit::ExtraSelection> &);
private: