| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  | /****************************************************************************
 | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** 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"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-21 12:35:24 +02:00
										 |  |  | #include <utils/textutils.h>
 | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  | #include <utils/qtcassert.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <QDebug>
 | 
					
						
							|  |  |  | #include <QString>
 | 
					
						
							|  |  |  | #include <QTextBlock>
 | 
					
						
							|  |  |  | #include <QTextDocument>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using namespace CPlusPlus; | 
					
						
							| 
									
										
										
										
											2017-09-21 12:35:24 +02:00
										 |  |  | using namespace Utils::Text; | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     unsigned startLine, startColumn; | 
					
						
							|  |  |  |     m_unit->getTokenStartPosition(tokenIndex, &startLine, &startColumn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const QTextDocument *document = cursor.document(); | 
					
						
							|  |  |  |     const int startPosition = | 
					
						
							|  |  |  |             document->findBlockByNumber(static_cast<int>(startLine) - 1).position() | 
					
						
							|  |  |  |             + static_cast<int>(startColumn) - 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return startPosition; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int CppSelectionChanger::getTokenEndCursorPosition( | 
					
						
							|  |  |  |         unsigned tokenIndex, | 
					
						
							|  |  |  |         const QTextCursor &cursor) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     unsigned endLine, endColumn; | 
					
						
							|  |  |  |     m_unit->getTokenEndPosition(tokenIndex, &endLine, &endColumn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const QTextDocument *document = cursor.document(); | 
					
						
							|  |  |  |     const int endPosition = | 
					
						
							|  |  |  |             document->findBlockByNumber(static_cast<int>(endLine) - 1).position() | 
					
						
							|  |  |  |             + static_cast<int>(endColumn) - 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return endPosition; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CppSelectionChanger::printTokenDebugInfo( | 
					
						
							|  |  |  |         unsigned tokenIndex, | 
					
						
							|  |  |  |         const QTextCursor &cursor, | 
					
						
							|  |  |  |         QString prefix) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     unsigned 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( | 
					
						
							| 
									
										
										
										
											2019-01-14 01:40:53 +01:00
										 |  |  |         const QList<AST *> &astPath, | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  |         const QTextCursor &cursor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Find relevant AST node from cursor, when the user expands for the first time.
 | 
					
						
							|  |  |  |     return findRelevantASTPositionsFromCursor(astPath, cursor); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ASTNodePositions CppSelectionChanger::findRelevantASTPositionsFromCursorWhenWholeDocumentSelected( | 
					
						
							| 
									
										
										
										
											2019-01-14 01:40:53 +01:00
										 |  |  |         const QList<AST *> &astPath, | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  |         const QTextCursor &cursor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Can't expand more, because whole document is selected.
 | 
					
						
							|  |  |  |     if (m_direction == ExpandSelection) | 
					
						
							| 
									
										
										
										
											2019-01-14 01:40:53 +01:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // In case of shrink, select the next smaller selection.
 | 
					
						
							|  |  |  |     return findRelevantASTPositionsFromCursor(astPath, cursor); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 01:40:53 +01:00
										 |  |  | ASTNodePositions CppSelectionChanger::findRelevantASTPositionsFromCursorFromPreviousNodeIndex(const QList<AST *> &astPath, | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  |         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."; | 
					
						
							| 
									
										
										
										
											2019-01-14 01:40:53 +01:00
										 |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Switch to next AST and set the first step.
 | 
					
						
							|  |  |  |         nodePositions = findRelevantASTPositionsFromCursor(astPath, cursor, newAstIndex); | 
					
						
							|  |  |  |         if (!nodePositions) | 
					
						
							| 
									
										
										
										
											2019-01-14 01:40:53 +01:00
										 |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         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) | 
					
						
							| 
									
										
										
										
											2019-01-14 01:40:53 +01:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 01:40:53 +01:00
										 |  |  |     QTC_ASSERT(m_nodeCurrentStep >= 1, return {}); | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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 newPosStart = positions.astPosStart; | 
					
						
							|  |  |  |             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.
 | 
					
						
							|  |  |  |             newPosStart = newPosEnd - static_cast<int>(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."; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 int newPosStart = positions.astPosStart; | 
					
						
							|  |  |  |                 int newPosEnd = positions.astPosEnd; | 
					
						
							|  |  |  |                 newPosEnd = newPosEnd - 1; | 
					
						
							|  |  |  |                 newPosStart = newPosEnd - static_cast<int>(firstToken.literal->size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 positions.astPosStart = newPosStart; | 
					
						
							|  |  |  |                 positions.astPosEnd = newPosEnd; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } 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; | 
					
						
							| 
									
										
										
										
											2018-09-20 12:04:21 +02:00
										 |  |  |         if (!lambdaDeclaratorAST) | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 19:59:00 +01:00
										 |  |  |         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
 |