forked from qt-creator/qt-creator
Recently tons of warnings show up for presumably "problematic" singned <-> unsigned and size conversions. The Qt side uses 'int', and that's the biggest 'integration surface' for us, so instead of establishing some internal boundary between signed and unsigned areas, push that boundary out of creator core code, and use 'int' everywhere. Because it reduces friction further, also do it in libcplusplus. Change-Id: I84f3b79852c8029713e7ea6f133ffb9ef7030a70 Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
1102 lines
45 KiB
C++
1102 lines
45 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 "cppselectionchanger.h"
|
|
|
|
#include <utils/textutils.h>
|
|
#include <utils/qtcassert.h>
|
|
|
|
#include <QDebug>
|
|
#include <QString>
|
|
#include <QTextBlock>
|
|
#include <QTextDocument>
|
|
|
|
using namespace CPlusPlus;
|
|
using namespace Utils::Text;
|
|
|
|
enum {
|
|
debug = false
|
|
};
|
|
|
|
namespace CppTools {
|
|
|
|
namespace Internal {
|
|
const int kChangeSelectionNodeIndexNotSet = -1;
|
|
const int kChangeSelectionNodeIndexWholeDocoument = -2;
|
|
}
|
|
|
|
using namespace CppTools::Internal;
|
|
|
|
CppSelectionChanger::CppSelectionChanger(QObject *parent)
|
|
: QObject(parent)
|
|
, m_changeSelectionNodeIndex(kChangeSelectionNodeIndexNotSet)
|
|
, m_nodeCurrentStep(kChangeSelectionNodeIndexNotSet)
|
|
{
|
|
}
|
|
|
|
void CppSelectionChanger::onCursorPositionChanged(const QTextCursor &newCursor)
|
|
{
|
|
// Reset the text cursor to be used for initial change selection behavior, only in the case
|
|
// that the cursor is not being modified by the actual change selection methods.
|
|
if (!m_inChangeSelection) {
|
|
m_initialChangeSelectionCursor = newCursor;
|
|
setNodeIndexAndStep(NodeIndexAndStepNotSet);
|
|
if (debug)
|
|
qDebug() << "Updating change selection cursor position:" << newCursor.position();
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool hasNoSelectionAndShrinking(
|
|
CppSelectionChanger::Direction direction,
|
|
const QTextCursor &cursor)
|
|
{
|
|
if (direction == CppSelectionChanger::ShrinkSelection && !cursor.hasSelection()) {
|
|
if (debug)
|
|
qDebug() << "No selection to shrink, exiting early.";
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ensureCursorSelectionIsNotFlipped(QTextCursor &cursor)
|
|
{
|
|
if (cursor.hasSelection() && (cursor.anchor() > cursor.position()))
|
|
cursor = flippedCursor(cursor);
|
|
|
|
if (debug) {
|
|
int l, c;
|
|
convertPosition(cursor.document(), cursor.position(), &l, &c);
|
|
|
|
qDebug() << "Cursor details: " << cursor.anchor() << cursor.position()
|
|
<< " l,c:" << l << ":" << c;
|
|
}
|
|
}
|
|
|
|
bool isDocumentAvailable(const CPlusPlus::Document::Ptr doc)
|
|
{
|
|
if (!doc) {
|
|
if (debug)
|
|
qDebug() << "Document is not available.";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
QTextCursor getWholeDocumentCursor(const QTextCursor &cursor)
|
|
{
|
|
QTextCursor newWholeDocumentCursor(cursor);
|
|
newWholeDocumentCursor.setPosition(0, QTextCursor::MoveAnchor);
|
|
newWholeDocumentCursor.setPosition(cursor.document()->characterCount() - 1,
|
|
QTextCursor::KeepAnchor);
|
|
return newWholeDocumentCursor;
|
|
}
|
|
|
|
bool isWholeDocumentSelectedAndExpanding(
|
|
CppSelectionChanger::Direction direction,
|
|
const QTextCursor &cursor)
|
|
{
|
|
if (direction == CppSelectionChanger::ExpandSelection && cursor.hasSelection()) {
|
|
const QTextCursor wholeDocumentCursor = getWholeDocumentCursor(cursor);
|
|
if (wholeDocumentCursor == cursor) {
|
|
if (debug)
|
|
qDebug() << "Selection is whole document, nothing to expand, exiting early.";
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // end of anonymous namespace
|
|
|
|
int CppSelectionChanger::getTokenStartCursorPosition(
|
|
unsigned tokenIndex,
|
|
const QTextCursor &cursor) const
|
|
{
|
|
int startLine, startColumn;
|
|
m_unit->getTokenStartPosition(tokenIndex, &startLine, &startColumn);
|
|
|
|
const QTextDocument *document = cursor.document();
|
|
const int startPosition = document->findBlockByNumber(startLine - 1).position()
|
|
+ startColumn - 1;
|
|
|
|
return startPosition;
|
|
}
|
|
|
|
int CppSelectionChanger::getTokenEndCursorPosition(
|
|
unsigned tokenIndex,
|
|
const QTextCursor &cursor) const
|
|
{
|
|
int endLine, endColumn;
|
|
m_unit->getTokenEndPosition(tokenIndex, &endLine, &endColumn);
|
|
|
|
const QTextDocument *document = cursor.document();
|
|
const int endPosition = document->findBlockByNumber(endLine - 1).position()
|
|
+ endColumn - 1;
|
|
|
|
return endPosition;
|
|
}
|
|
|
|
void CppSelectionChanger::printTokenDebugInfo(
|
|
unsigned tokenIndex,
|
|
const QTextCursor &cursor,
|
|
QString prefix) const
|
|
{
|
|
int line, column;
|
|
const Token token = m_unit->tokenAt(tokenIndex);
|
|
m_unit->getTokenStartPosition(tokenIndex, &line, &column);
|
|
const int startPos = getTokenStartCursorPosition(tokenIndex, cursor);
|
|
const int endPos = getTokenEndCursorPosition(tokenIndex, cursor);
|
|
|
|
qDebug() << qSetFieldWidth(20) << prefix << qSetFieldWidth(0)
|
|
<< token.spell() << tokenIndex
|
|
<< " l, c:" << line << ":" << column
|
|
<< " offset: " << token.utf16chars() << startPos << endPos;
|
|
}
|
|
|
|
bool CppSelectionChanger::shouldSkipASTNodeBasedOnPosition(
|
|
const ASTNodePositions &positions,
|
|
const QTextCursor &cursor) const
|
|
{
|
|
bool shouldSkipNode = false;
|
|
|
|
bool isEqual = cursor.anchor() == positions.astPosStart
|
|
&& cursor.position() == positions.astPosEnd;
|
|
|
|
// New selections should include initial selection.
|
|
bool includesInitialSelection =
|
|
m_initialChangeSelectionCursor.anchor() >= positions.astPosStart &&
|
|
m_initialChangeSelectionCursor.position() <= positions.astPosEnd;
|
|
|
|
// Prefer new selections to start with initial cursor if anchor == position.
|
|
if (!m_initialChangeSelectionCursor.hasSelection()) {
|
|
includesInitialSelection =
|
|
m_initialChangeSelectionCursor.position() < positions.astPosEnd;
|
|
}
|
|
|
|
// When expanding: Skip if new selection is smaller than current cursor selection.
|
|
// When shrinking: Skip if new selection is bigger than current cursor selection.
|
|
bool isNewSelectionSmaller = positions.astPosStart > cursor.anchor()
|
|
|| positions.astPosEnd < cursor.position();
|
|
bool isNewSelectionBigger = positions.astPosStart < cursor.anchor()
|
|
|| positions.astPosEnd > cursor.position();
|
|
|
|
if (m_direction == CppSelectionChanger::ExpandSelection
|
|
&& (isNewSelectionSmaller || isEqual || !includesInitialSelection)) {
|
|
shouldSkipNode = true;
|
|
} else if (m_direction == CppSelectionChanger::ShrinkSelection
|
|
&& (isNewSelectionBigger || isEqual || !includesInitialSelection)) {
|
|
shouldSkipNode = true;
|
|
}
|
|
|
|
if (debug && shouldSkipNode) {
|
|
qDebug() << "isEqual:" << isEqual << "includesInitialSelection:" << includesInitialSelection
|
|
<< "isNewSelectionSmaller:" << isNewSelectionSmaller << "isNewSelectionBigger:"
|
|
<< isNewSelectionBigger;
|
|
}
|
|
|
|
return shouldSkipNode;
|
|
}
|
|
|
|
ASTNodePositions CppSelectionChanger::getASTPositions(AST *ast, const QTextCursor &cursor) const
|
|
{
|
|
ASTNodePositions positions(ast);
|
|
|
|
// An AST node's contents is bound by its first token start position inclusively,
|
|
// and its last token start position exclusively.
|
|
// So we are also interested in the second to last token, which is actually
|
|
// included in the bounds.
|
|
positions.firstTokenIndex = ast->firstToken();
|
|
positions.lastTokenIndex = ast->lastToken();
|
|
positions.secondToLastTokenIndex = positions.lastTokenIndex - 1;
|
|
|
|
// The AST position start is the start of the first token.
|
|
positions.astPosStart = getTokenStartCursorPosition(positions.firstTokenIndex, cursor);
|
|
|
|
// The end position depends on whether, there is only one token involved in the current AST
|
|
// node or multiple ones.
|
|
// Default we assume that there is only one token, so the end position of the AST node
|
|
// is the start of the last token.
|
|
// If there is more than one (second to last token will be different to the first token)
|
|
// use the second to last token end position as the AST node end position.
|
|
positions.astPosEnd = getTokenStartCursorPosition(positions.lastTokenIndex, cursor);
|
|
if (positions.lastTokenIndex != positions.firstTokenIndex)
|
|
positions.astPosEnd = getTokenEndCursorPosition(positions.secondToLastTokenIndex, cursor);
|
|
|
|
if (debug) {
|
|
qDebug() << "Token positions start and end:"
|
|
<< positions.astPosStart << positions.astPosEnd;
|
|
}
|
|
|
|
return positions;
|
|
}
|
|
|
|
void CppSelectionChanger::updateCursorSelection(
|
|
QTextCursor &cursorToModify,
|
|
ASTNodePositions positions)
|
|
{
|
|
m_workingCursor.setPosition(positions.astPosStart, QTextCursor::MoveAnchor);
|
|
m_workingCursor.setPosition(positions.astPosEnd, QTextCursor::KeepAnchor);
|
|
cursorToModify = m_workingCursor;
|
|
|
|
if (debug) {
|
|
printTokenDebugInfo(positions.firstTokenIndex, m_workingCursor,
|
|
QString::fromLatin1("First token:"));
|
|
printTokenDebugInfo(positions.lastTokenIndex, m_workingCursor,
|
|
QString::fromLatin1("Last token:"));
|
|
printTokenDebugInfo(positions.secondToLastTokenIndex, m_workingCursor,
|
|
QString::fromLatin1("Second to last:"));
|
|
|
|
qDebug() << "Anchor is now: " << m_workingCursor.anchor();
|
|
qDebug() << "Position is now: " << m_workingCursor.position();
|
|
}
|
|
}
|
|
|
|
int CppSelectionChanger::getFirstCurrentStepForASTNode(AST *ast) const
|
|
{
|
|
if (m_direction == ExpandSelection)
|
|
return 1;
|
|
else
|
|
return possibleASTStepCount(ast);
|
|
}
|
|
|
|
bool CppSelectionChanger::isLastPossibleStepForASTNode(AST *ast) const
|
|
{
|
|
if (m_direction == ExpandSelection)
|
|
return currentASTStep() == possibleASTStepCount(ast);
|
|
else
|
|
return currentASTStep() == 1;
|
|
}
|
|
|
|
ASTNodePositions CppSelectionChanger::getFineTunedASTPositions(AST *ast,
|
|
const QTextCursor &cursor) const
|
|
{
|
|
ASTNodePositions positions = getASTPositions(ast, cursor);
|
|
fineTuneASTNodePositions(positions);
|
|
return positions;
|
|
}
|
|
|
|
ASTNodePositions CppSelectionChanger::findRelevantASTPositionsFromCursor(
|
|
const QList<AST *> &astPath,
|
|
const QTextCursor &cursor,
|
|
int startingFromNodeIndex)
|
|
{
|
|
ASTNodePositions currentNodePositions;
|
|
const int size = astPath.size();
|
|
int currentAstIndex = m_direction == ExpandSelection ? size - 1 : 0;
|
|
|
|
// Adjust starting node index, if a valid value was passed.
|
|
if (startingFromNodeIndex != kChangeSelectionNodeIndexNotSet)
|
|
currentAstIndex = startingFromNodeIndex;
|
|
|
|
if (currentAstIndex < size && currentAstIndex >= 0) {
|
|
AST *ast = astPath.at(currentAstIndex);
|
|
m_changeSelectionNodeIndex = currentAstIndex;
|
|
m_nodeCurrentStep = getFirstCurrentStepForASTNode(ast);
|
|
currentNodePositions = getFineTunedASTPositions(ast, cursor);
|
|
|
|
if (debug && startingFromNodeIndex == kChangeSelectionNodeIndexNotSet)
|
|
qDebug() << "Setting AST index for the first time.";
|
|
}
|
|
|
|
if (!currentNodePositions.ast)
|
|
setNodeIndexAndStep(NodeIndexAndStepNotSet);
|
|
|
|
return currentNodePositions;
|
|
}
|
|
|
|
ASTNodePositions CppSelectionChanger::findRelevantASTPositionsFromCursorWhenNodeIndexNotSet(
|
|
const QList<AST *> &astPath,
|
|
const QTextCursor &cursor)
|
|
{
|
|
// Find relevant AST node from cursor, when the user expands for the first time.
|
|
return findRelevantASTPositionsFromCursor(astPath, cursor);
|
|
}
|
|
|
|
ASTNodePositions CppSelectionChanger::findRelevantASTPositionsFromCursorWhenWholeDocumentSelected(
|
|
const QList<AST *> &astPath,
|
|
const QTextCursor &cursor)
|
|
{
|
|
// Can't expand more, because whole document is selected.
|
|
if (m_direction == ExpandSelection)
|
|
return {};
|
|
|
|
// In case of shrink, select the next smaller selection.
|
|
return findRelevantASTPositionsFromCursor(astPath, cursor);
|
|
}
|
|
|
|
ASTNodePositions CppSelectionChanger::findRelevantASTPositionsFromCursorFromPreviousNodeIndex(const QList<AST *> &astPath,
|
|
const QTextCursor &cursor)
|
|
{
|
|
ASTNodePositions nodePositions;
|
|
|
|
// This is not the first expansion, use the previous node index.
|
|
nodePositions.ast = astPath.at(m_changeSelectionNodeIndex);
|
|
|
|
// We reached the last possible step for the current AST node, so we move to the
|
|
// next / previous one depending on the direction.
|
|
if (isLastPossibleStepForASTNode(nodePositions.ast)) {
|
|
int newAstIndex = m_changeSelectionNodeIndex;
|
|
if (m_direction == ExpandSelection)
|
|
--newAstIndex;
|
|
else
|
|
++newAstIndex;
|
|
|
|
if (newAstIndex < 0 || newAstIndex >= astPath.count()) {
|
|
if (debug)
|
|
qDebug() << "Skipping expansion because there is no available next AST node.";
|
|
return {};
|
|
}
|
|
|
|
// Switch to next AST and set the first step.
|
|
nodePositions = findRelevantASTPositionsFromCursor(astPath, cursor, newAstIndex);
|
|
if (!nodePositions)
|
|
return {};
|
|
|
|
if (debug)
|
|
qDebug() << "Moved to next AST node.";
|
|
} else {
|
|
// There are possible steps available for current node, so move to the next / previous
|
|
// step.
|
|
if (m_direction == ExpandSelection)
|
|
++m_nodeCurrentStep;
|
|
else
|
|
--m_nodeCurrentStep;
|
|
nodePositions = getFineTunedASTPositions(nodePositions.ast, cursor);
|
|
|
|
if (debug)
|
|
qDebug() << "Moved to next AST step.";
|
|
}
|
|
|
|
return nodePositions;
|
|
}
|
|
|
|
ASTNodePositions CppSelectionChanger::findNextASTStepPositions(const QTextCursor &cursor)
|
|
{
|
|
// Find AST node path starting from the initial change selection cursor.
|
|
// The ASTPath class, only takes into consideration the position of the cursor, but not the
|
|
// anchor. We make up for that later in the code.
|
|
QTextCursor cursorToStartFrom(m_initialChangeSelectionCursor);
|
|
|
|
ASTPath astPathFinder(m_doc);
|
|
const QList<AST *> astPath = astPathFinder(cursorToStartFrom);
|
|
|
|
#ifdef WITH_AST_PATH_DUMP
|
|
if (debug)
|
|
ASTPath::dump(astPath);
|
|
#endif
|
|
|
|
if (astPath.size() == 0)
|
|
return {};
|
|
|
|
ASTNodePositions currentNodePositions;
|
|
if (m_changeSelectionNodeIndex == kChangeSelectionNodeIndexNotSet) {
|
|
currentNodePositions = findRelevantASTPositionsFromCursorWhenNodeIndexNotSet(astPath,
|
|
cursor);
|
|
} else if (m_changeSelectionNodeIndex == kChangeSelectionNodeIndexWholeDocoument) {
|
|
currentNodePositions = findRelevantASTPositionsFromCursorWhenWholeDocumentSelected(astPath,
|
|
cursor);
|
|
} else {
|
|
currentNodePositions = findRelevantASTPositionsFromCursorFromPreviousNodeIndex(astPath,
|
|
cursor);
|
|
}
|
|
|
|
if (debug) {
|
|
qDebug() << "m_changeSelectionNodeIndex:" << m_changeSelectionNodeIndex
|
|
<< "possible step count:" << possibleASTStepCount(currentNodePositions.ast)
|
|
<< "current step:" << m_nodeCurrentStep;
|
|
}
|
|
|
|
QTC_ASSERT(m_nodeCurrentStep >= 1, return {});
|
|
|
|
return currentNodePositions;
|
|
}
|
|
|
|
void CppSelectionChanger::fineTuneForStatementPositions(unsigned firstParenTokenIndex,
|
|
unsigned lastParenTokenIndex,
|
|
ASTNodePositions &positions) const
|
|
{
|
|
Token firstParenToken = m_unit->tokenAt(firstParenTokenIndex);
|
|
Token lastParenToken = m_unit->tokenAt(lastParenTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "firstParenToken:" << firstParenToken.spell();
|
|
qDebug() << "lastParenToken:" << lastParenToken.spell();
|
|
}
|
|
|
|
int newPosStart = getTokenStartCursorPosition(firstParenTokenIndex, m_workingCursor);
|
|
int newPosEnd = getTokenEndCursorPosition(lastParenTokenIndex, m_workingCursor);
|
|
|
|
bool isOutsideParen =
|
|
m_initialChangeSelectionCursor.position() <= newPosStart;
|
|
|
|
if (currentASTStep() == 1 && !isOutsideParen) {
|
|
if (debug)
|
|
qDebug() << "Selecting Paren contents of for statement.";
|
|
positions.astPosStart = newPosStart + 1;
|
|
positions.astPosEnd = newPosEnd - 1;
|
|
}
|
|
if (currentASTStep() == 2 && !isOutsideParen) {
|
|
if (debug)
|
|
qDebug() << "Selecting Paren of for statement together with contents.";
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = newPosEnd;
|
|
}
|
|
}
|
|
|
|
void CppSelectionChanger::fineTuneASTNodePositions(ASTNodePositions &positions) const
|
|
{
|
|
AST *ast = positions.ast;
|
|
|
|
if (ast->asCompoundStatement()) {
|
|
// Allow first selecting the contents of the scope, without selecting the braces, and
|
|
// afterwards select the contents together with braces.
|
|
if (currentASTStep() == 1) {
|
|
if (debug)
|
|
qDebug() << "Selecting inner contents of compound statement.";
|
|
|
|
unsigned firstInnerTokenIndex = positions.firstTokenIndex + 1;
|
|
unsigned lastInnerTokenIndex = positions.lastTokenIndex - 2;
|
|
Token firstInnerToken = m_unit->tokenAt(firstInnerTokenIndex);
|
|
Token lastInnerToken = m_unit->tokenAt(lastInnerTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "LastInnerToken:" << lastInnerToken.spell();
|
|
qDebug() << "FirstInnerToken:" << firstInnerToken.spell();
|
|
}
|
|
|
|
// Check if compound statement is empty, then select just the blank space inside it.
|
|
int newPosStart, newPosEnd;
|
|
if (positions.secondToLastTokenIndex - positions.firstTokenIndex <= 1) {
|
|
// TODO: If the empty space has a new tab character, or spaces, and the document is
|
|
// not saved, the last semantic info is not updated, and the selection is not
|
|
// properly computed. Figure out how to work around this.
|
|
newPosStart = getTokenEndCursorPosition(positions.firstTokenIndex, m_workingCursor);
|
|
newPosEnd = getTokenStartCursorPosition(positions.secondToLastTokenIndex,
|
|
m_workingCursor);
|
|
if (debug)
|
|
qDebug() << "Selecting inner contents of compound statement which is empty.";
|
|
} else {
|
|
// Select the inner contents of the scope, without the braces.
|
|
newPosStart = getTokenStartCursorPosition(firstInnerTokenIndex, m_workingCursor);
|
|
newPosEnd = getTokenEndCursorPosition(lastInnerTokenIndex, m_workingCursor);
|
|
}
|
|
|
|
if (debug) {
|
|
qDebug() << "New" << newPosStart << newPosEnd
|
|
<< "Old" << m_workingCursor.anchor() << m_workingCursor.position();
|
|
}
|
|
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = newPosEnd;
|
|
}
|
|
// Next time, we select the braces as well. Reverse for shrinking.
|
|
// The positions already have the correct selection, so no need to set them.
|
|
} else if (CallAST *callAST = ast->asCall()) {
|
|
unsigned firstParenTokenIndex = callAST->lparen_token;
|
|
unsigned lastParenTokenIndex = callAST->rparen_token;
|
|
Token firstParenToken = m_unit->tokenAt(firstParenTokenIndex);
|
|
Token lastParenToken = m_unit->tokenAt(lastParenTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "firstParenToken:" << firstParenToken.spell();
|
|
qDebug() << "lastParenToken:" << lastParenToken.spell();
|
|
}
|
|
|
|
// Select the parenthesis of the call, and everything between.
|
|
int newPosStart = getTokenStartCursorPosition(firstParenTokenIndex, m_workingCursor);
|
|
int newPosEnd = getTokenEndCursorPosition(lastParenTokenIndex, m_workingCursor);
|
|
|
|
bool isInFunctionName =
|
|
m_initialChangeSelectionCursor.position() <= newPosStart;
|
|
|
|
// If cursor is inside the function name, select the name implicitly (because it's a
|
|
// different AST node), and then the whole call expression (so just one step).
|
|
// If cursor is inside parentheses, on first step select everything inside them,
|
|
// on second step select the everything inside parentheses including them,
|
|
// on third step select the whole call expression.
|
|
if (currentASTStep() == 1 && !isInFunctionName) {
|
|
if (debug)
|
|
qDebug() << "Selecting everything inside parentheses.";
|
|
positions.astPosStart = newPosStart + 1;
|
|
positions.astPosEnd = newPosEnd - 1;
|
|
}
|
|
if (currentASTStep() == 2 && !isInFunctionName) {
|
|
if (debug)
|
|
qDebug() << "Selecting everything inside and including "
|
|
"the parentheses of the function call.";
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = newPosEnd;
|
|
}
|
|
} else if (StringLiteralAST *stringLiteralAST = ast->asStringLiteral()) {
|
|
// Select literal without quotes on first step, and the whole literal on next step.
|
|
if (currentASTStep() == 1) {
|
|
Token firstToken = m_unit->tokenAt(stringLiteralAST->firstToken());
|
|
bool isRawLiteral = firstToken.f.kind >= T_FIRST_RAW_STRING_LITERAL
|
|
&& firstToken.f.kind <= T_RAW_UTF32_STRING_LITERAL;
|
|
if (debug && isRawLiteral)
|
|
qDebug() << "Is raw literal.";
|
|
|
|
// Start from positions that include quotes.
|
|
int newPosEnd = positions.astPosEnd;
|
|
|
|
// Decrement last position to skip last quote.
|
|
--newPosEnd;
|
|
|
|
// If raw literal also skip parenthesis.
|
|
if (isRawLiteral)
|
|
--newPosEnd;
|
|
|
|
// Start position will be the end position minus the size of the actual contents of the
|
|
// literal.
|
|
int newPosStart = newPosEnd - firstToken.string->size();
|
|
|
|
// Skip raw literal parentheses.
|
|
if (isRawLiteral)
|
|
newPosStart += 2;
|
|
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = newPosEnd;
|
|
if (debug)
|
|
qDebug() << "Selecting inner contents of string literal.";
|
|
}
|
|
} else if (NumericLiteralAST *numericLiteralAST = ast->asNumericLiteral()) {
|
|
Token firstToken = m_unit->tokenAt(numericLiteralAST->firstToken());
|
|
// If char literal, select it without quotes on first step.
|
|
if (firstToken.isCharLiteral()) {
|
|
if (currentASTStep() == 1) {
|
|
if (debug)
|
|
qDebug() << "Selecting inner contents of char literal.";
|
|
|
|
positions.astPosEnd = positions.astPosEnd - 1;
|
|
positions.astPosStart = positions.astPosEnd - firstToken.literal->size();
|
|
}
|
|
}
|
|
} else if (ForStatementAST *forStatementAST = ast->asForStatement()) {
|
|
unsigned firstParenTokenIndex = forStatementAST->lparen_token;
|
|
unsigned lastParenTokenIndex = forStatementAST->rparen_token;
|
|
fineTuneForStatementPositions(firstParenTokenIndex, lastParenTokenIndex, positions);
|
|
} else if (RangeBasedForStatementAST *rangeForStatementAST = ast->asRangeBasedForStatement()) {
|
|
unsigned firstParenTokenIndex = rangeForStatementAST->lparen_token;
|
|
unsigned lastParenTokenIndex = rangeForStatementAST->rparen_token;
|
|
fineTuneForStatementPositions(firstParenTokenIndex, lastParenTokenIndex, positions);
|
|
} else if (ClassSpecifierAST *classSpecificerAST = ast->asClassSpecifier()) {
|
|
|
|
unsigned firstBraceTokenIndex = classSpecificerAST->lbrace_token;
|
|
unsigned lastBraceTokenIndex = classSpecificerAST->rbrace_token;
|
|
unsigned classKeywordTokenIndex = classSpecificerAST->classkey_token;
|
|
|
|
Token firstBraceToken = m_unit->tokenAt(firstBraceTokenIndex);
|
|
Token lastBraceToken = m_unit->tokenAt(lastBraceTokenIndex);
|
|
Token classKeywordToken = m_unit->tokenAt(classKeywordTokenIndex);
|
|
|
|
if (debug) {
|
|
qDebug() << "firstBraceToken:" << firstBraceToken.spell();
|
|
qDebug() << "lastBraceToken:" << lastBraceToken.spell();
|
|
qDebug() << "classKeywordToken:" << classKeywordToken.spell();
|
|
|
|
}
|
|
|
|
int newPosStart = getTokenStartCursorPosition(firstBraceTokenIndex, m_workingCursor);
|
|
int newPosEnd = getTokenEndCursorPosition(lastBraceTokenIndex, m_workingCursor);
|
|
|
|
bool isOutsideBraces =
|
|
m_initialChangeSelectionCursor.position() <= newPosStart;
|
|
bool isInsideBraces = !isOutsideBraces;
|
|
|
|
int classKeywordPosStart = getTokenStartCursorPosition(classKeywordTokenIndex,
|
|
m_workingCursor);
|
|
|
|
int classKeywordPosEnd = getTokenEndCursorPosition(classKeywordTokenIndex, m_workingCursor);
|
|
|
|
bool isInClassKeyword = m_initialChangeSelectionCursor.anchor() >= classKeywordPosStart &&
|
|
m_initialChangeSelectionCursor.position() <= classKeywordPosEnd;
|
|
|
|
bool isInClassName = false;
|
|
int classNamePosEnd = newPosEnd;
|
|
NameAST *nameAST = classSpecificerAST->name;
|
|
if (nameAST) {
|
|
SimpleNameAST *classNameAST = nameAST->asSimpleName();
|
|
if (classNameAST) {
|
|
unsigned identifierTokenIndex = classNameAST->identifier_token;
|
|
Token identifierToken = m_unit->tokenAt(identifierTokenIndex);
|
|
if (debug)
|
|
qDebug() << "identifierToken:" << identifierToken.spell();
|
|
|
|
int classNamePosStart = getTokenStartCursorPosition(identifierTokenIndex,
|
|
m_workingCursor);
|
|
classNamePosEnd = getTokenEndCursorPosition(identifierTokenIndex,
|
|
m_workingCursor);
|
|
|
|
isInClassName = m_initialChangeSelectionCursor.anchor() >= classNamePosStart &&
|
|
m_initialChangeSelectionCursor.position() <= classNamePosEnd;
|
|
}
|
|
}
|
|
|
|
if (currentASTStep() == 1 && isInsideBraces) {
|
|
if (debug)
|
|
qDebug() << "Selecting everything inside braces of class statement.";
|
|
positions.astPosStart = newPosStart + 1;
|
|
positions.astPosEnd = newPosEnd - 1;
|
|
}
|
|
if (currentASTStep() == 2 && isInsideBraces) {
|
|
if (debug)
|
|
qDebug() << "Selecting braces of class statement.";
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = newPosEnd;
|
|
}
|
|
if (currentASTStep() == 1 && isInClassKeyword) {
|
|
if (debug)
|
|
qDebug() << "Selecting class keyword.";
|
|
positions.astPosStart = classKeywordPosStart;
|
|
positions.astPosEnd = classKeywordPosEnd;
|
|
}
|
|
if (currentASTStep() == 2 && isInClassKeyword) {
|
|
if (debug)
|
|
qDebug() << "Selecting class keyword and name.";
|
|
positions.astPosStart = classKeywordPosStart;
|
|
positions.astPosEnd = classNamePosEnd;
|
|
}
|
|
if (currentASTStep() == 1 && isInClassName) {
|
|
if (debug)
|
|
qDebug() << "Selecting class keyword and name.";
|
|
positions.astPosStart = classKeywordPosStart;
|
|
positions.astPosEnd = classNamePosEnd;
|
|
}
|
|
} else if (NamespaceAST *namespaceAST = ast->asNamespace()) {
|
|
unsigned namespaceTokenIndex = namespaceAST->namespace_token;
|
|
unsigned identifierTokenIndex = namespaceAST->identifier_token;
|
|
Token namespaceToken = m_unit->tokenAt(namespaceTokenIndex);
|
|
Token identifierToken = m_unit->tokenAt(identifierTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "namespace token:" << namespaceToken.spell();
|
|
qDebug() << "identifier token:" << identifierToken.spell();
|
|
}
|
|
|
|
int namespacePosStart = getTokenStartCursorPosition(namespaceTokenIndex, m_workingCursor);
|
|
int namespacePosEnd = getTokenEndCursorPosition(namespaceTokenIndex, m_workingCursor);
|
|
|
|
int identifierPosStart = getTokenStartCursorPosition(identifierTokenIndex, m_workingCursor);
|
|
int identifierPosEnd = getTokenEndCursorPosition(identifierTokenIndex, m_workingCursor);
|
|
|
|
bool isInNamespaceKeyword =
|
|
m_initialChangeSelectionCursor.position() <= namespacePosEnd;
|
|
|
|
bool isInNamespaceIdentifier =
|
|
m_initialChangeSelectionCursor.anchor() >= identifierPosStart &&
|
|
m_initialChangeSelectionCursor.position() <= identifierPosEnd;
|
|
|
|
if (currentASTStep() == 1) {
|
|
if (isInNamespaceKeyword) {
|
|
if (debug)
|
|
qDebug() << "Selecting namespace keyword.";
|
|
positions.astPosStart = namespacePosStart;
|
|
positions.astPosEnd = namespacePosEnd;
|
|
}
|
|
else if (isInNamespaceIdentifier) {
|
|
if (debug)
|
|
qDebug() << "Selecting namespace identifier.";
|
|
positions.astPosStart = identifierPosStart;
|
|
positions.astPosEnd = identifierPosEnd;
|
|
}
|
|
}
|
|
else if (currentASTStep() == 2) {
|
|
if (isInNamespaceKeyword || isInNamespaceIdentifier) {
|
|
if (debug)
|
|
qDebug() << "Selecting namespace keyword and identifier.";
|
|
positions.astPosStart = namespacePosStart;
|
|
positions.astPosEnd = identifierPosEnd;
|
|
|
|
}
|
|
}
|
|
} else if (ExpressionListParenAST *parenAST = ast->asExpressionListParen()) {
|
|
unsigned firstParenTokenIndex = parenAST->lparen_token;
|
|
unsigned lastParenTokenIndex = parenAST->rparen_token;
|
|
Token firstParenToken = m_unit->tokenAt(firstParenTokenIndex);
|
|
Token lastParenToken = m_unit->tokenAt(lastParenTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "firstParenToken:" << firstParenToken.spell();
|
|
qDebug() << "lastParenToken:" << lastParenToken.spell();
|
|
}
|
|
|
|
// Select the parentheses, and everything between.
|
|
int newPosStart = getTokenStartCursorPosition(firstParenTokenIndex, m_workingCursor);
|
|
int newPosEnd = getTokenEndCursorPosition(lastParenTokenIndex, m_workingCursor);
|
|
|
|
if (currentASTStep() == 1) {
|
|
if (debug)
|
|
qDebug() << "Selecting everything inside parentheses.";
|
|
positions.astPosStart = newPosStart + 1;
|
|
positions.astPosEnd = newPosEnd - 1;
|
|
}
|
|
if (currentASTStep() == 2) {
|
|
if (debug)
|
|
qDebug() << "Selecting everything inside including the parentheses.";
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = newPosEnd;
|
|
}
|
|
} else if (FunctionDeclaratorAST* functionDeclaratorAST = ast->asFunctionDeclarator()) {
|
|
unsigned firstParenTokenIndex = functionDeclaratorAST->lparen_token;
|
|
unsigned lastParenTokenIndex = functionDeclaratorAST->rparen_token;
|
|
Token firstParenToken = m_unit->tokenAt(firstParenTokenIndex);
|
|
Token lastParenToken = m_unit->tokenAt(lastParenTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "firstParenToken:" << firstParenToken.spell();
|
|
qDebug() << "lastParenToken:" << lastParenToken.spell();
|
|
}
|
|
|
|
int newPosStart = getTokenStartCursorPosition(firstParenTokenIndex, m_workingCursor);
|
|
int newPosEnd = getTokenEndCursorPosition(lastParenTokenIndex, m_workingCursor);
|
|
|
|
if (currentASTStep() == 1) {
|
|
if (debug)
|
|
qDebug() << "Selecting everything inside and including the parentheses.";
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = newPosEnd;
|
|
}
|
|
} else if (FunctionDefinitionAST *functionDefinitionAST = ast->asFunctionDefinition()) {
|
|
if (!functionDefinitionAST->function_body)
|
|
return;
|
|
|
|
CompoundStatementAST *compoundStatementAST =
|
|
functionDefinitionAST->function_body->asCompoundStatement();
|
|
if (!compoundStatementAST)
|
|
return;
|
|
|
|
if (!functionDefinitionAST->decl_specifier_list
|
|
|| !functionDefinitionAST->decl_specifier_list->value)
|
|
return;
|
|
|
|
SimpleSpecifierAST *simpleSpecifierAST =
|
|
functionDefinitionAST->decl_specifier_list->value->asSimpleSpecifier();
|
|
if (!simpleSpecifierAST)
|
|
return;
|
|
|
|
unsigned firstBraceTokenIndex = compoundStatementAST->lbrace_token;
|
|
unsigned specifierTokenIndex = simpleSpecifierAST->firstToken();
|
|
Token firstBraceToken = m_unit->tokenAt(firstBraceTokenIndex);
|
|
Token specifierToken = m_unit->tokenAt(specifierTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "firstBraceToken:" << firstBraceToken.spell();
|
|
qDebug() << "specifierToken:" << specifierToken.spell();
|
|
}
|
|
|
|
int firstBracePosEnd = getTokenStartCursorPosition(firstBraceTokenIndex, m_workingCursor);
|
|
|
|
bool isOutsideBraces =
|
|
m_initialChangeSelectionCursor.position() <= firstBracePosEnd;
|
|
|
|
if (currentASTStep() == 1 && isOutsideBraces) {
|
|
int newPosStart = getTokenStartCursorPosition(specifierTokenIndex, m_workingCursor);
|
|
|
|
if (debug)
|
|
qDebug() << "Selecting everything to the left of the function braces.";
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = firstBracePosEnd - 1;
|
|
}
|
|
} else if (DeclaratorAST *declaratorAST = ast->asDeclarator()) {
|
|
PostfixDeclaratorListAST *list = declaratorAST->postfix_declarator_list;
|
|
if (!list)
|
|
return;
|
|
|
|
PostfixDeclaratorAST *postfixDeclarator = list->value;
|
|
if (!postfixDeclarator)
|
|
return;
|
|
|
|
FunctionDeclaratorAST *functionDeclarator = postfixDeclarator->asFunctionDeclarator();
|
|
if (!functionDeclarator)
|
|
return;
|
|
|
|
SpecifierListAST *cv_list = functionDeclarator->cv_qualifier_list;
|
|
if (!cv_list)
|
|
return;
|
|
|
|
SpecifierAST *first_cv = cv_list->value;
|
|
if (!first_cv)
|
|
return;
|
|
|
|
unsigned firstCVTokenIndex = first_cv->firstToken();
|
|
Token firstCVToken = m_unit->tokenAt(firstCVTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "firstCVTokenIndex:" << firstCVToken.spell();
|
|
}
|
|
|
|
int cvPosStart = getTokenStartCursorPosition(firstCVTokenIndex, m_workingCursor);
|
|
bool isBeforeCVList = m_initialChangeSelectionCursor.position() < cvPosStart;
|
|
|
|
if (currentASTStep() == 1 && isBeforeCVList) {
|
|
if (debug)
|
|
qDebug() << "Selecting function declarator without CV qualifiers.";
|
|
|
|
int newPosEnd = cvPosStart;
|
|
positions.astPosEnd = newPosEnd - 1;
|
|
}
|
|
|
|
} else if (TemplateIdAST *templateIdAST = ast->asTemplateId()) {
|
|
unsigned identifierTokenIndex = templateIdAST->identifier_token;
|
|
Token identifierToken = m_unit->tokenAt(identifierTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "identifierTokenIndex:" << identifierToken.spell();
|
|
}
|
|
|
|
int newPosStart = getTokenStartCursorPosition(identifierTokenIndex, m_workingCursor);
|
|
int newPosEnd = getTokenEndCursorPosition(identifierTokenIndex, m_workingCursor);
|
|
|
|
bool isInsideIdentifier = m_initialChangeSelectionCursor.anchor() >= newPosStart &&
|
|
m_initialChangeSelectionCursor.position() <= newPosEnd;
|
|
|
|
if (currentASTStep() == 1 && isInsideIdentifier) {
|
|
if (debug)
|
|
qDebug() << "Selecting just identifier before selecting template id.";
|
|
positions.astPosStart = newPosStart;
|
|
positions.astPosEnd = newPosEnd;
|
|
}
|
|
} else if (TemplateDeclarationAST *templateDeclarationAST = ast->asTemplateDeclaration()) {
|
|
unsigned templateKeywordTokenIndex = templateDeclarationAST->template_token;
|
|
unsigned greaterTokenIndex = templateDeclarationAST->greater_token;
|
|
Token templateKeywordToken = m_unit->tokenAt(templateKeywordTokenIndex);
|
|
Token greaterToken = m_unit->tokenAt(greaterTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "templateKeywordTokenIndex:" << templateKeywordToken.spell();
|
|
qDebug() << "greaterTokenIndex:" << greaterToken.spell();
|
|
}
|
|
|
|
int templateKeywordPosStart = getTokenStartCursorPosition(templateKeywordTokenIndex,
|
|
m_workingCursor);
|
|
int templateKeywordPosEnd = getTokenEndCursorPosition(templateKeywordTokenIndex,
|
|
m_workingCursor);
|
|
|
|
int templateParametersPosEnd = getTokenEndCursorPosition(greaterTokenIndex,
|
|
m_workingCursor);
|
|
|
|
bool isInsideTemplateKeyword =
|
|
m_initialChangeSelectionCursor.anchor() >= templateKeywordPosStart &&
|
|
m_initialChangeSelectionCursor.position() <= templateKeywordPosEnd;
|
|
|
|
if (currentASTStep() == 1 && isInsideTemplateKeyword) {
|
|
if (debug)
|
|
qDebug() << "Selecting template keyword.";
|
|
positions.astPosStart = templateKeywordPosStart;
|
|
positions.astPosEnd = templateKeywordPosEnd;
|
|
}
|
|
if (currentASTStep() == 2 && isInsideTemplateKeyword) {
|
|
if (debug)
|
|
qDebug() << "Selecting template keyword and parameters.";
|
|
positions.astPosStart = templateKeywordPosStart;
|
|
positions.astPosEnd = templateParametersPosEnd;
|
|
}
|
|
} else if (LambdaExpressionAST *lambdaExpressionAST = ast->asLambdaExpression()) {
|
|
// TODO: Fix more lambda cases.
|
|
LambdaIntroducerAST *lambdaIntroducerAST = lambdaExpressionAST->lambda_introducer;
|
|
LambdaDeclaratorAST *lambdaDeclaratorAST = lambdaExpressionAST->lambda_declarator;
|
|
if (!lambdaDeclaratorAST)
|
|
return;
|
|
|
|
TrailingReturnTypeAST *trailingReturnTypeAST = lambdaDeclaratorAST->trailing_return_type;
|
|
unsigned firstSquareBracketTokenIndex = lambdaIntroducerAST->lbracket_token;
|
|
unsigned lastParenTokenIndex = lambdaDeclaratorAST->rparen_token;
|
|
|
|
Token firstSquareBracketToken = m_unit->tokenAt(firstSquareBracketTokenIndex);
|
|
Token lastParenToken = m_unit->tokenAt(lastParenTokenIndex);
|
|
if (debug) {
|
|
qDebug() << "firstSquareBracketToken:" << firstSquareBracketToken.spell();
|
|
qDebug() << "lastParenToken:" << lastParenToken.spell();
|
|
}
|
|
|
|
int firstSquareBracketPosStart = getTokenStartCursorPosition(firstSquareBracketTokenIndex,
|
|
m_workingCursor);
|
|
int lastParenPosEnd = getTokenEndCursorPosition(lastParenTokenIndex, m_workingCursor);
|
|
|
|
|
|
bool isInsideDeclarator =
|
|
m_initialChangeSelectionCursor.anchor() >= firstSquareBracketPosStart &&
|
|
m_initialChangeSelectionCursor.position() <= lastParenPosEnd;
|
|
|
|
if (currentASTStep() == 1 && isInsideDeclarator) {
|
|
if (debug)
|
|
qDebug() << "Selecting lambda capture group and arguments.";
|
|
positions.astPosStart = firstSquareBracketPosStart;
|
|
positions.astPosEnd = lastParenPosEnd;
|
|
}
|
|
if (currentASTStep() == 2 && isInsideDeclarator && trailingReturnTypeAST) {
|
|
if (debug)
|
|
qDebug() << "Selecting lambda prototype.";
|
|
|
|
unsigned lastReturnTypeTokenIndex = trailingReturnTypeAST->lastToken();
|
|
Token lastReturnTypeToken = m_unit->tokenAt(lastReturnTypeTokenIndex);
|
|
if (debug)
|
|
qDebug() << "lastReturnTypeToken:" << lastReturnTypeToken.spell();
|
|
int lastReturnTypePosEnd = getTokenEndCursorPosition(lastReturnTypeTokenIndex,
|
|
m_workingCursor);
|
|
|
|
positions.astPosStart = firstSquareBracketPosStart;
|
|
positions.astPosEnd = lastReturnTypePosEnd - 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CppSelectionChanger::performSelectionChange(QTextCursor &cursorToModify)
|
|
{
|
|
forever {
|
|
if (ASTNodePositions positions = findNextASTStepPositions(m_workingCursor)) {
|
|
if (!shouldSkipASTNodeBasedOnPosition(positions, m_workingCursor)) {
|
|
updateCursorSelection(cursorToModify, positions);
|
|
return true;
|
|
} else {
|
|
if (debug)
|
|
qDebug() << "Skipping node.";
|
|
}
|
|
} else if (m_direction == ShrinkSelection) {
|
|
// The last possible action to do, if there was no step with a smaller selection, is
|
|
// to set the cursor to the initial change selection cursor, without an anchor.
|
|
QTextCursor finalCursor(m_initialChangeSelectionCursor);
|
|
finalCursor.setPosition(finalCursor.position(), QTextCursor::MoveAnchor);
|
|
cursorToModify = finalCursor;
|
|
setNodeIndexAndStep(NodeIndexAndStepNotSet);
|
|
if (debug)
|
|
qDebug() << "Final shrink selection case.";
|
|
return true;
|
|
} else if (m_direction == ExpandSelection) {
|
|
// The last possible action to do, if there was no step with a bigger selection, is
|
|
// to set the cursor to the whole document including header inclusions.
|
|
QTextCursor finalCursor = getWholeDocumentCursor(m_initialChangeSelectionCursor);
|
|
cursorToModify = finalCursor;
|
|
setNodeIndexAndStep(NodeIndexAndStepWholeDocument);
|
|
if (debug)
|
|
qDebug() << "Final expand selection case.";
|
|
return true;
|
|
}
|
|
// Break out of the loop, because no further modification of the selection can be done.
|
|
else break;
|
|
}
|
|
|
|
// No next step found for given direction, return early without modifying the cursor.
|
|
return false;
|
|
}
|
|
|
|
void CppSelectionChanger::setNodeIndexAndStep(NodeIndexAndStepState state)
|
|
{
|
|
switch (state) {
|
|
case NodeIndexAndStepWholeDocument:
|
|
m_changeSelectionNodeIndex = kChangeSelectionNodeIndexWholeDocoument;
|
|
m_nodeCurrentStep = kChangeSelectionNodeIndexWholeDocoument;
|
|
break;
|
|
case NodeIndexAndStepNotSet:
|
|
default:
|
|
m_changeSelectionNodeIndex = kChangeSelectionNodeIndexNotSet;
|
|
m_nodeCurrentStep = kChangeSelectionNodeIndexNotSet;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool CppSelectionChanger::changeSelection(
|
|
Direction direction,
|
|
QTextCursor &cursorToModify,
|
|
const CPlusPlus::Document::Ptr doc)
|
|
{
|
|
m_workingCursor = cursorToModify;
|
|
|
|
if (hasNoSelectionAndShrinking(direction, m_workingCursor))
|
|
return false;
|
|
|
|
if (isWholeDocumentSelectedAndExpanding(direction, m_workingCursor))
|
|
return false;
|
|
|
|
if (!isDocumentAvailable(doc)) {
|
|
return false;
|
|
}
|
|
|
|
ensureCursorSelectionIsNotFlipped(m_workingCursor);
|
|
|
|
m_doc = doc;
|
|
m_unit = m_doc->translationUnit();
|
|
m_direction = direction;
|
|
|
|
return performSelectionChange(cursorToModify);
|
|
}
|
|
|
|
void CppSelectionChanger::startChangeSelection()
|
|
{
|
|
// Stop cursorPositionChanged signal handler from setting the initial
|
|
// change selection cursor, when the cursor is being changed as a result of the change
|
|
// selection operation.
|
|
m_inChangeSelection = true;
|
|
}
|
|
|
|
void CppSelectionChanger::stopChangeSelection()
|
|
{
|
|
m_inChangeSelection = false;
|
|
}
|
|
|
|
int CppSelectionChanger::possibleASTStepCount(CPlusPlus::AST *ast) const
|
|
{
|
|
// Different AST nodes, have a different number of steps though which they can go.
|
|
// For example in a string literal, we first want to select the literal contents on the first
|
|
// step, and then the quotes + the literal content in the second step.
|
|
if (!ast)
|
|
return 1;
|
|
if (ast->asCompoundStatement())
|
|
return 2;
|
|
if (ast->asCall())
|
|
return 3;
|
|
if (ast->asStringLiteral())
|
|
return 2;
|
|
if (NumericLiteralAST* numericLiteralAST = ast->asNumericLiteral()) {
|
|
Token firstToken = m_unit->tokenAt(numericLiteralAST->firstToken());
|
|
if (firstToken.isCharLiteral())
|
|
return 2;
|
|
return 1;
|
|
}
|
|
if (ast->asForStatement())
|
|
return 3;
|
|
if (ast->asRangeBasedForStatement())
|
|
return 3;
|
|
if (ast->asClassSpecifier())
|
|
return 3;
|
|
if (ast->asNamespace())
|
|
return 3;
|
|
if (ast->asExpressionListParen())
|
|
return 2;
|
|
if (ast->asFunctionDeclarator())
|
|
return 1;
|
|
if (ast->asFunctionDefinition())
|
|
return 2;
|
|
if (ast->asTemplateId())
|
|
return 2;
|
|
if (ast->asDeclarator())
|
|
return 2;
|
|
if (ast->asTemplateDeclaration())
|
|
return 3;
|
|
if (ast->asLambdaExpression())
|
|
return 3;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CppSelectionChanger::currentASTStep() const
|
|
{
|
|
return m_nodeCurrentStep;
|
|
}
|
|
|
|
} // namespace CppTools
|