Files
qt-creator/src/plugins/cppeditor/cppuseselectionsupdater.cpp
Nikolai Kosjar 6b2d6e7965 CppEditor: Cancel waiting for use selections if revision changed
Not sure whether this can happen at all, but if it can, we should not
make the UI unusable.

Change-Id: I1a40630d0c1f057e20a33b2c755e1fd5aea0b003
Reviewed-by: David Schulz <david.schulz@qt.io>
2017-07-24 13:24:31 +00:00

204 lines
7.0 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "cppuseselectionsupdater.h"
#include "cppeditor.h"
#include "cppeditordocument.h"
#include <cpptools/cpptoolsreuse.h>
#include <texteditor/convenience.h>
#include <QTextBlock>
#include <QTextCursor>
#include <utils/qtcassert.h>
enum { updateUseSelectionsInternalInMs = 500 };
namespace CppEditor {
namespace Internal {
CppUseSelectionsUpdater::CppUseSelectionsUpdater(TextEditor::TextEditorWidget *editorWidget)
: m_editorWidget(editorWidget)
, m_runnerRevision(-1)
{
m_timer.setSingleShot(true);
m_timer.setInterval(updateUseSelectionsInternalInMs);
connect(&m_timer, &QTimer::timeout, this, [this]() { update(); });
}
CppUseSelectionsUpdater::~CppUseSelectionsUpdater()
{
if (m_runnerWatcher)
m_runnerWatcher->cancel();
}
void CppUseSelectionsUpdater::scheduleUpdate()
{
m_timer.start();
}
void CppUseSelectionsUpdater::abortSchedule()
{
m_timer.stop();
}
void CppUseSelectionsUpdater::update(CallType callType)
{
auto *cppEditorWidget = qobject_cast<CppEditorWidget *>(m_editorWidget);
QTC_ASSERT(cppEditorWidget, return);
auto *cppEditorDocument = qobject_cast<CppEditorDocument *>(cppEditorWidget->textDocument());
QTC_ASSERT(cppEditorDocument, return);
CppTools::CursorInfoParams params;
params.semanticInfo = cppEditorWidget->semanticInfo();
params.textCursor = TextEditor::Convenience::wordStartCursor(cppEditorWidget->textCursor());
if (callType == Asynchronous) {
if (isSameIdentifierAsBefore(params.textCursor))
return;
if (m_runnerWatcher)
m_runnerWatcher->cancel();
m_runnerWatcher.reset(new QFutureWatcher<CursorInfo>);
connect(m_runnerWatcher.data(), &QFutureWatcherBase::finished,
this, &CppUseSelectionsUpdater::onFindUsesFinished);
m_runnerRevision = m_editorWidget->document()->revision();
m_runnerWordStartPosition = params.textCursor.position();
m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params));
} else { // synchronous case
const int startRevision = cppEditorDocument->document()->revision();
QFuture<CursorInfo> future = cppEditorDocument->cursorInfo(params);
// QFuture::waitForFinished seems to block completely, not even
// allowing to process events from QLocalSocket.
while (!future.isFinished()) {
if (future.isCanceled())
return;
QTC_ASSERT(startRevision == cppEditorDocument->document()->revision(), return);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
processResults(future.result());
}
}
bool CppUseSelectionsUpdater::isSameIdentifierAsBefore(const QTextCursor &cursorAtWordStart) const
{
return m_runnerRevision != -1
&& m_runnerRevision == m_editorWidget->document()->revision()
&& m_runnerWordStartPosition == cursorAtWordStart.position();
}
void CppUseSelectionsUpdater::processResults(const CursorInfo &result)
{
ExtraSelections localVariableSelections;
if (!result.useRanges.isEmpty() || !currentUseSelections().isEmpty()) {
ExtraSelections selections = updateUseSelections(result.useRanges);
if (result.areUseRangesForLocalVariable)
localVariableSelections = selections;
}
updateUnusedSelections(result.unusedVariablesRanges);
emit selectionsForVariableUnderCursorUpdated(localVariableSelections);
emit finished(result.localUses);
}
void CppUseSelectionsUpdater::onFindUsesFinished()
{
QTC_ASSERT(m_runnerWatcher, return);
if (m_runnerWatcher->isCanceled())
return;
if (m_runnerRevision != m_editorWidget->document()->revision())
return;
if (m_runnerWordStartPosition
!= TextEditor::Convenience::wordStartCursor(m_editorWidget->textCursor()).position()) {
return;
}
processResults(m_runnerWatcher->result());
m_runnerWatcher.reset();
}
CppUseSelectionsUpdater::ExtraSelections
CppUseSelectionsUpdater::toExtraSelections(const CursorInfo::Ranges &ranges,
TextEditor::TextStyle style)
{
CppUseSelectionsUpdater::ExtraSelections selections;
selections.reserve(ranges.size());
for (const CursorInfo::Range &range : ranges) {
QTextDocument *document = m_editorWidget->document();
const int position
= document->findBlockByNumber(static_cast<int>(range.line) - 1).position()
+ static_cast<int>(range.column) - 1;
const int anchor = position + static_cast<int>(range.length);
QTextEdit::ExtraSelection sel;
sel.format = m_editorWidget->textDocument()->fontSettings().toTextCharFormat(style);
sel.cursor = QTextCursor(document);
sel.cursor.setPosition(anchor);
sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
selections.append(sel);
}
return selections;
}
CppUseSelectionsUpdater::ExtraSelections
CppUseSelectionsUpdater::currentUseSelections() const
{
return m_editorWidget->extraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection);
}
CppUseSelectionsUpdater::ExtraSelections
CppUseSelectionsUpdater::updateUseSelections(const CursorInfo::Ranges &ranges)
{
const ExtraSelections selections = toExtraSelections(ranges, TextEditor::C_OCCURRENCES);
m_editorWidget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection,
selections);
return selections;
}
void CppUseSelectionsUpdater::updateUnusedSelections(const CursorInfo::Ranges &ranges)
{
const ExtraSelections selections = toExtraSelections(ranges, TextEditor::C_OCCURRENCES_UNUSED);
m_editorWidget->setExtraSelections(TextEditor::TextEditorWidget::UnusedSymbolSelection,
selections);
}
} // namespace Internal
} // namespace CppEditor