// parent is either a FunctionDefinitionAST or a SimpleDeclarationAST // line and column are 1-based static bool findDeclOrDef(const Document::Ptr &doc, int line, int column, DeclarationAST **parent, DeclaratorAST **decl, FunctionDeclaratorAST **funcDecl) { QList<AST *> path = ASTPath(doc)(line, column); // for function definitions, simply scan for FunctionDefinitionAST not preceded // by CompoundStatement/CtorInitializer // for function declarations, look for SimpleDeclarations with a single Declarator // with a FunctionDeclarator postfix FunctionDefinitionAST *funcDef = 0; SimpleDeclarationAST *simpleDecl = 0; *decl = 0; for (int i = path.size() - 1; i > 0; --i) { AST *ast = path.at(i); if (ast->asCompoundStatement() || ast->asCtorInitializer()) break; if ((funcDef = ast->asFunctionDefinition()) != 0) { *parent = funcDef; *decl = funcDef->declarator; break; } if ((simpleDecl = ast->asSimpleDeclaration()) != 0) { *parent = simpleDecl; if (!simpleDecl->declarator_list || !simpleDecl->declarator_list->value) break; *decl = simpleDecl->declarator_list->value; break; } } if (!*parent || !*decl) return false; if (!(*decl)->postfix_declarator_list || !(*decl)->postfix_declarator_list->value) return false; *funcDecl = (*decl)->postfix_declarator_list->value->asFunctionDeclarator(); return *funcDecl; }
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 - 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."; positions.astPosEnd = positions.astPosEnd - 1; positions.astPosStart = positions.astPosEnd - int(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; } } }