2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2010-07-26 13:06:33 +02:00
|
|
|
**
|
2013-01-28 17:12:19 +01:00
|
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
2012-10-02 09:12:39 +02:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2010-07-26 13:06:33 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2010-07-26 13:06:33 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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 Digia. For licensing terms and
|
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
2010-07-26 13:06:33 +02:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
2012-10-02 09:12:39 +02:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
2010-12-17 16:01:08 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
#include "cppquickfixes.h"
|
|
|
|
|
|
2010-07-26 13:06:33 +02:00
|
|
|
#include "cppeditor.h"
|
2013-04-11 18:01:40 +02:00
|
|
|
#include "cppfunctiondecldeflink.h"
|
2011-04-15 16:19:23 +02:00
|
|
|
#include "cppquickfixassistant.h"
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-01-17 17:23:43 +01:00
|
|
|
#include <cpptools/cppclassesfilter.h>
|
2013-01-15 14:26:24 +01:00
|
|
|
#include <cpptools/cppcodestylesettings.h>
|
2013-01-17 17:23:43 +01:00
|
|
|
#include <cpptools/cpppointerdeclarationformatter.h>
|
|
|
|
|
#include <cpptools/cpptoolsconstants.h>
|
2013-04-11 18:01:40 +02:00
|
|
|
#include <cpptools/cpptoolsreuse.h>
|
|
|
|
|
#include <cpptools/insertionpointlocator.h>
|
2012-01-20 14:43:21 +01:00
|
|
|
#include <cpptools/symbolfinder.h>
|
2013-03-27 18:54:03 +01:00
|
|
|
|
2013-04-15 14:23:33 +02:00
|
|
|
#include <cplusplus/ASTPath.h>
|
2013-04-11 18:01:40 +02:00
|
|
|
#include <cplusplus/CPlusPlusForwardDeclarations.h>
|
2013-03-27 18:54:03 +01:00
|
|
|
#include <cplusplus/CppRewriter.h>
|
|
|
|
|
#include <cplusplus/DependencyTable.h>
|
|
|
|
|
#include <cplusplus/TypeOfExpression.h>
|
|
|
|
|
|
2011-11-25 12:05:58 +01:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
2013-04-11 18:01:40 +02:00
|
|
|
|
2011-12-14 14:23:25 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QApplication>
|
2013-04-11 18:01:40 +02:00
|
|
|
#include <QDir>
|
2013-01-17 17:23:43 +01:00
|
|
|
#include <QFileInfo>
|
2013-04-11 18:01:40 +02:00
|
|
|
#include <QInputDialog>
|
|
|
|
|
#include <QMessageBox>
|
2013-02-07 12:16:41 +01:00
|
|
|
#include <QSharedPointer>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QTextBlock>
|
|
|
|
|
#include <QTextCursor>
|
2011-10-25 14:06:39 +02:00
|
|
|
|
2013-02-15 08:26:12 +01:00
|
|
|
#include <cctype>
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
using namespace CPlusPlus;
|
2010-07-26 13:06:33 +02:00
|
|
|
using namespace CppEditor;
|
|
|
|
|
using namespace CppEditor::Internal;
|
2013-04-11 18:01:40 +02:00
|
|
|
using namespace CppTools;
|
|
|
|
|
using namespace TextEditor;
|
|
|
|
|
using namespace Utils;
|
2013-02-07 12:16:41 +01:00
|
|
|
|
|
|
|
|
void CppEditor::Internal::registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
|
|
|
|
|
{
|
|
|
|
|
plugIn->addAutoReleasedObject(new AddIncludeForUndefinedIdentifier);
|
|
|
|
|
plugIn->addAutoReleasedObject(new AddIncludeForForwardDeclaration);
|
|
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new FlipLogicalOperands);
|
|
|
|
|
plugIn->addAutoReleasedObject(new InverseLogicalComparison);
|
|
|
|
|
plugIn->addAutoReleasedObject(new RewriteLogicalAnd);
|
|
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new ConvertToCamelCase);
|
|
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new ConvertCStringToNSString);
|
|
|
|
|
plugIn->addAutoReleasedObject(new ConvertNumericLiteral);
|
|
|
|
|
plugIn->addAutoReleasedObject(new TranslateStringLiteral);
|
|
|
|
|
plugIn->addAutoReleasedObject(new WrapStringLiteral);
|
|
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new MoveDeclarationOutOfIf);
|
|
|
|
|
plugIn->addAutoReleasedObject(new MoveDeclarationOutOfWhile);
|
|
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new SplitIfStatement);
|
|
|
|
|
plugIn->addAutoReleasedObject(new SplitSimpleDeclaration);
|
|
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new AddLocalDeclaration);
|
|
|
|
|
plugIn->addAutoReleasedObject(new AddBracesToIf);
|
|
|
|
|
plugIn->addAutoReleasedObject(new RearrangeParamDeclarationList);
|
|
|
|
|
plugIn->addAutoReleasedObject(new ReformatPointerDeclaration);
|
|
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new CompleteSwitchCaseStatement);
|
|
|
|
|
plugIn->addAutoReleasedObject(new InsertQtPropertyMembers);
|
|
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges);
|
|
|
|
|
plugIn->addAutoReleasedObject(new ExtractFunction);
|
|
|
|
|
plugIn->addAutoReleasedObject(new GenerateGetterSetter);
|
|
|
|
|
plugIn->addAutoReleasedObject(new InsertDeclFromDef);
|
|
|
|
|
plugIn->addAutoReleasedObject(new InsertDefFromDecl);
|
2013-04-15 14:23:33 +02:00
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
|
|
|
|
|
plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
|
2013-04-17 21:53:20 +02:00
|
|
|
|
|
|
|
|
plugIn->addAutoReleasedObject(new AssignToLocalVariable);
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
// In the following anonymous namespace all functions are collected, which could be of interest for
|
|
|
|
|
// different quick fixes.
|
|
|
|
|
namespace {
|
|
|
|
|
|
2013-05-03 11:02:30 +02:00
|
|
|
enum DefPos {
|
|
|
|
|
DefPosInsideClass,
|
|
|
|
|
DefPosOutsideClass,
|
|
|
|
|
DefPosImplementationFile
|
|
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
inline bool isQtStringLiteral(const QByteArray &id)
|
2011-09-30 10:45:11 +02:00
|
|
|
{
|
|
|
|
|
return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
inline bool isQtStringTranslation(const QByteArray &id)
|
2011-12-14 14:23:25 +01:00
|
|
|
{
|
|
|
|
|
return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
Class *isMemberFunction(const LookupContext &context, Function *function)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(function, return 0);
|
|
|
|
|
|
|
|
|
|
Scope *enclosingScope = function->enclosingScope();
|
|
|
|
|
while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
|
|
|
|
|
enclosingScope = enclosingScope->enclosingScope();
|
|
|
|
|
QTC_ASSERT(enclosingScope != 0, return 0);
|
|
|
|
|
|
|
|
|
|
const Name *functionName = function->name();
|
|
|
|
|
if (! functionName)
|
|
|
|
|
return 0; // anonymous function names are not valid c++
|
|
|
|
|
|
|
|
|
|
if (! functionName->isQualifiedNameId())
|
|
|
|
|
return 0; // trying to add a declaration for a global function
|
|
|
|
|
|
|
|
|
|
const QualifiedNameId *q = functionName->asQualifiedNameId();
|
|
|
|
|
if (!q->base())
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
|
|
|
|
|
foreach (Symbol *s, binding->symbols()) {
|
|
|
|
|
if (Class *matchingClass = s->asClass())
|
|
|
|
|
return matchingClass;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class InverseLogicalComparisonOp: public CppQuickFixOperation
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2013-02-07 12:16:41 +01:00
|
|
|
InverseLogicalComparisonOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
BinaryExpressionAST *binary, Kind invertToken)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, binary(binary), nested(0), negation(0)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
Token tok;
|
|
|
|
|
tok.f.kind = invertToken;
|
|
|
|
|
replacement = QLatin1String(tok.spell());
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// check for enclosing nested expression
|
|
|
|
|
if (priority - 1 >= 0)
|
|
|
|
|
nested = interface->path()[priority - 1]->asNestedExpression();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// check for ! before parentheses
|
|
|
|
|
if (nested && priority - 2 >= 0) {
|
|
|
|
|
negation = interface->path()[priority - 2]->asUnaryExpression();
|
|
|
|
|
if (negation && ! interface->currentFile()->tokenAt(negation->unary_op_token).is(T_EXCLAIM))
|
|
|
|
|
negation = 0;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
QString description() const
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
ChangeSet changes;
|
|
|
|
|
if (negation) {
|
|
|
|
|
// can't remove parentheses since that might break precedence
|
|
|
|
|
changes.remove(currentFile->range(negation->unary_op_token));
|
|
|
|
|
} else if (nested) {
|
|
|
|
|
changes.insert(currentFile->startOf(nested), QLatin1String("!"));
|
|
|
|
|
} else {
|
|
|
|
|
changes.insert(currentFile->startOf(binary), QLatin1String("!("));
|
|
|
|
|
changes.insert(currentFile->endOf(binary), QLatin1String(")"));
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
changes.replace(currentFile->range(binary->binary_op_token), replacement);
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
private:
|
|
|
|
|
BinaryExpressionAST *binary;
|
|
|
|
|
NestedExpressionAST *nested;
|
|
|
|
|
UnaryExpressionAST *negation;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
QString replacement;
|
2010-07-26 13:06:33 +02:00
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void InverseLogicalComparison::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
int index = path.size() - 1;
|
|
|
|
|
BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
|
|
|
|
|
if (! binary)
|
|
|
|
|
return;
|
|
|
|
|
if (! interface->isCursorOn(binary->binary_op_token))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Kind invertToken;
|
|
|
|
|
switch (file->tokenAt(binary->binary_op_token).kind()) {
|
|
|
|
|
case T_LESS_EQUAL:
|
|
|
|
|
invertToken = T_GREATER;
|
|
|
|
|
break;
|
|
|
|
|
case T_LESS:
|
|
|
|
|
invertToken = T_GREATER_EQUAL;
|
|
|
|
|
break;
|
|
|
|
|
case T_GREATER:
|
|
|
|
|
invertToken = T_LESS_EQUAL;
|
|
|
|
|
break;
|
|
|
|
|
case T_GREATER_EQUAL:
|
|
|
|
|
invertToken = T_LESS;
|
|
|
|
|
break;
|
|
|
|
|
case T_EQUAL_EQUAL:
|
|
|
|
|
invertToken = T_EXCLAIM_EQUAL;
|
|
|
|
|
break;
|
|
|
|
|
case T_EXCLAIM_EQUAL:
|
|
|
|
|
invertToken = T_EQUAL_EQUAL;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
result.append(CppQuickFixOperation::Ptr(
|
|
|
|
|
new InverseLogicalComparisonOp(interface, index, binary, invertToken)));
|
|
|
|
|
}
|
2011-01-07 16:55:34 +01:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class FlipLogicalOperandsOp: public CppQuickFixOperation
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2013-02-07 12:16:41 +01:00
|
|
|
FlipLogicalOperandsOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
BinaryExpressionAST *binary, QString replacement)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
, binary(binary)
|
|
|
|
|
, replacement(replacement)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
setPriority(priority);
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
QString description() const
|
|
|
|
|
{
|
|
|
|
|
if (replacement.isEmpty())
|
|
|
|
|
return QApplication::translate("CppTools::QuickFix", "Swap Operands");
|
|
|
|
|
else
|
|
|
|
|
return QApplication::translate("CppTools::QuickFix", "Rewrite Using %1").arg(replacement);
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ChangeSet changes;
|
2013-04-11 18:01:40 +02:00
|
|
|
changes.flip(currentFile->range(binary->left_expression),
|
|
|
|
|
currentFile->range(binary->right_expression));
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! replacement.isEmpty())
|
|
|
|
|
changes.replace(currentFile->range(binary->binary_op_token), replacement);
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->apply();
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2013-02-07 12:16:41 +01:00
|
|
|
BinaryExpressionAST *binary;
|
|
|
|
|
QString replacement;
|
|
|
|
|
};
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void FlipLogicalOperands::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int index = path.size() - 1;
|
|
|
|
|
BinaryExpressionAST *binary = path.at(index)->asBinaryExpression();
|
|
|
|
|
if (! binary)
|
|
|
|
|
return;
|
|
|
|
|
if (! interface->isCursorOn(binary->binary_op_token))
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
Kind flipToken;
|
|
|
|
|
switch (file->tokenAt(binary->binary_op_token).kind()) {
|
|
|
|
|
case T_LESS_EQUAL:
|
|
|
|
|
flipToken = T_GREATER_EQUAL;
|
|
|
|
|
break;
|
|
|
|
|
case T_LESS:
|
|
|
|
|
flipToken = T_GREATER;
|
|
|
|
|
break;
|
|
|
|
|
case T_GREATER:
|
|
|
|
|
flipToken = T_LESS;
|
|
|
|
|
break;
|
|
|
|
|
case T_GREATER_EQUAL:
|
|
|
|
|
flipToken = T_LESS_EQUAL;
|
|
|
|
|
break;
|
|
|
|
|
case T_EQUAL_EQUAL:
|
|
|
|
|
case T_EXCLAIM_EQUAL:
|
|
|
|
|
case T_AMPER_AMPER:
|
|
|
|
|
case T_PIPE_PIPE:
|
|
|
|
|
flipToken = T_EOF_SYMBOL;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
QString replacement;
|
|
|
|
|
if (flipToken != T_EOF_SYMBOL) {
|
|
|
|
|
Token tok;
|
|
|
|
|
tok.f.kind = flipToken;
|
|
|
|
|
replacement = QLatin1String(tok.spell());
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new FlipLogicalOperandsOp(interface, index, binary, replacement)));
|
|
|
|
|
}
|
2011-01-07 16:55:34 +01:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class RewriteLogicalAndOp: public CppQuickFixOperation
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2013-02-07 12:16:41 +01:00
|
|
|
QSharedPointer<ASTPatternBuilder> mk;
|
|
|
|
|
UnaryExpressionAST *left;
|
|
|
|
|
UnaryExpressionAST *right;
|
|
|
|
|
BinaryExpressionAST *pattern;
|
|
|
|
|
|
|
|
|
|
RewriteLogicalAndOp(const CppQuickFixInterface &interface)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
, mk(new ASTPatternBuilder)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
left = mk->UnaryExpression();
|
|
|
|
|
right = mk->UnaryExpression();
|
|
|
|
|
pattern = mk->BinaryExpression(left, right);
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
ChangeSet changes;
|
|
|
|
|
changes.replace(currentFile->range(pattern->binary_op_token), QLatin1String("||"));
|
|
|
|
|
changes.remove(currentFile->range(left->unary_op_token));
|
|
|
|
|
changes.remove(currentFile->range(right->unary_op_token));
|
|
|
|
|
const int start = currentFile->startOf(pattern);
|
|
|
|
|
const int end = currentFile->endOf(pattern);
|
|
|
|
|
changes.insert(start, QLatin1String("!("));
|
|
|
|
|
changes.insert(end, QLatin1String(")"));
|
|
|
|
|
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->appendIndentRange(currentFile->range(pattern));
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
|
|
|
|
};
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void RewriteLogicalAnd::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
BinaryExpressionAST *expression = 0;
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int index = path.size() - 1;
|
|
|
|
|
for (; index != -1; --index) {
|
|
|
|
|
expression = path.at(index)->asBinaryExpression();
|
|
|
|
|
if (expression)
|
|
|
|
|
break;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! expression)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! interface->isCursorOn(expression->binary_op_token))
|
|
|
|
|
return;
|
2012-10-10 23:27:16 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
QSharedPointer<RewriteLogicalAndOp> op(new RewriteLogicalAndOp(interface));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (expression->match(op->pattern, &matcher) &&
|
|
|
|
|
file->tokenAt(op->pattern->binary_op_token).is(T_AMPER_AMPER) &&
|
|
|
|
|
file->tokenAt(op->left->unary_op_token).is(T_EXCLAIM) &&
|
|
|
|
|
file->tokenAt(op->right->unary_op_token).is(T_EXCLAIM)) {
|
2013-04-11 18:01:40 +02:00
|
|
|
op->setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Rewrite Condition Using ||"));
|
2013-02-07 12:16:41 +01:00
|
|
|
op->setPriority(index);
|
|
|
|
|
result.append(op);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
bool SplitSimpleDeclaration::checkDeclaration(SimpleDeclarationAST *declaration)
|
|
|
|
|
{
|
|
|
|
|
if (! declaration->semicolon_token)
|
|
|
|
|
return false;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! declaration->decl_specifier_list)
|
|
|
|
|
return false;
|
2011-01-07 14:49:00 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
for (SpecifierListAST *it = declaration->decl_specifier_list; it; it = it->next) {
|
|
|
|
|
SpecifierAST *specifier = it->value;
|
2011-01-07 16:55:34 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (specifier->asEnumSpecifier() != 0)
|
2010-07-26 13:06:33 +02:00
|
|
|
return false;
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
else if (specifier->asClassSpecifier() != 0)
|
2010-07-26 13:06:33 +02:00
|
|
|
return false;
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! declaration->declarator_list)
|
|
|
|
|
return false;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
else if (! declaration->declarator_list->next)
|
|
|
|
|
return false;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class SplitSimpleDeclarationOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SplitSimpleDeclarationOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
SimpleDeclarationAST *decl)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, declaration(decl)
|
|
|
|
|
{
|
|
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Split Declaration"));
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ChangeSet changes;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
SpecifierListAST *specifiers = declaration->decl_specifier_list;
|
|
|
|
|
int declSpecifiersStart = currentFile->startOf(specifiers->firstToken());
|
|
|
|
|
int declSpecifiersEnd = currentFile->endOf(specifiers->lastToken() - 1);
|
|
|
|
|
int insertPos = currentFile->endOf(declaration->semicolon_token);
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
DeclaratorAST *prevDeclarator = declaration->declarator_list->value;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
for (DeclaratorListAST *it = declaration->declarator_list->next; it; it = it->next) {
|
|
|
|
|
DeclaratorAST *declarator = it->value;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
changes.insert(insertPos, QLatin1String("\n"));
|
|
|
|
|
changes.copy(declSpecifiersStart, declSpecifiersEnd, insertPos);
|
|
|
|
|
changes.insert(insertPos, QLatin1String(" "));
|
|
|
|
|
changes.move(currentFile->range(declarator), insertPos);
|
|
|
|
|
changes.insert(insertPos, QLatin1String(";"));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int prevDeclEnd = currentFile->endOf(prevDeclarator);
|
|
|
|
|
changes.remove(prevDeclEnd, currentFile->startOf(declarator));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
prevDeclarator = declarator;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->appendIndentRange(currentFile->range(declaration));
|
|
|
|
|
currentFile->apply();
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2013-02-07 12:16:41 +01:00
|
|
|
SimpleDeclarationAST *declaration;
|
|
|
|
|
};
|
2012-10-10 23:27:16 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void SplitSimpleDeclaration::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
CoreDeclaratorAST *core_declarator = 0;
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
const int cursorPosition = file->cursor().selectionStart();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
for (int index = path.size() - 1; index != -1; --index) {
|
|
|
|
|
AST *node = path.at(index);
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (CoreDeclaratorAST *coreDecl = node->asCoreDeclarator())
|
|
|
|
|
core_declarator = coreDecl;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
else if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
|
|
|
|
|
if (checkDeclaration(simpleDecl)) {
|
|
|
|
|
SimpleDeclarationAST *declaration = simpleDecl;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int startOfDeclSpecifier = file->startOf(declaration->decl_specifier_list->firstToken());
|
|
|
|
|
const int endOfDeclSpecifier = file->endOf(declaration->decl_specifier_list->lastToken() - 1);
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (cursorPosition >= startOfDeclSpecifier && cursorPosition <= endOfDeclSpecifier) {
|
|
|
|
|
// the AST node under cursor is a specifier.
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new SplitSimpleDeclarationOp(interface, index, declaration)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (core_declarator && interface->isCursorOn(core_declarator)) {
|
|
|
|
|
// got a core-declarator under the text cursor.
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new SplitSimpleDeclarationOp(interface, index, declaration)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class AddBracesToIfOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
AddBracesToIfOp(const CppQuickFixInterface &interface, int priority, StatementAST *statement)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, _statement(statement)
|
|
|
|
|
{
|
2013-04-11 18:01:40 +02:00
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix", "Add Curly Braces"));
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
ChangeSet changes;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int start = currentFile->endOf(_statement->firstToken() - 1);
|
|
|
|
|
changes.insert(start, QLatin1String(" {"));
|
2011-01-07 14:49:00 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int end = currentFile->endOf(_statement->lastToken() - 1);
|
|
|
|
|
changes.insert(end, QLatin1String("\n}"));
|
2011-01-07 16:55:34 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
currentFile->setChangeSet(changes);
|
2013-04-11 18:01:40 +02:00
|
|
|
currentFile->appendIndentRange(ChangeSet::Range(start, end));
|
2013-02-07 12:16:41 +01:00
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
StatementAST *_statement;
|
|
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void AddBracesToIf::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
const QList<AST *> &path = interface->path();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// show when we're on the 'if' of an if statement
|
|
|
|
|
int index = path.size() - 1;
|
|
|
|
|
IfStatementAST *ifStatement = path.at(index)->asIfStatement();
|
|
|
|
|
if (ifStatement && interface->isCursorOn(ifStatement->if_token) && ifStatement->statement
|
|
|
|
|
&& ! ifStatement->statement->asCompoundStatement()) {
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new AddBracesToIfOp(interface, index, ifStatement->statement)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// or if we're on the statement contained in the if
|
|
|
|
|
// ### This may not be such a good idea, consider nested ifs...
|
|
|
|
|
for (; index != -1; --index) {
|
2010-07-26 13:06:33 +02:00
|
|
|
IfStatementAST *ifStatement = path.at(index)->asIfStatement();
|
2013-02-07 12:16:41 +01:00
|
|
|
if (ifStatement && ifStatement->statement
|
|
|
|
|
&& interface->isCursorOn(ifStatement->statement)
|
2010-07-26 13:06:33 +02:00
|
|
|
&& ! ifStatement->statement->asCompoundStatement()) {
|
2013-02-07 12:16:41 +01:00
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new AddBracesToIfOp(interface, index, ifStatement->statement)));
|
2012-10-10 23:27:16 +02:00
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// ### This could very well be extended to the else branch
|
|
|
|
|
// and other nodes entirely.
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class MoveDeclarationOutOfIfOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MoveDeclarationOutOfIfOp(const CppQuickFixInterface &interface)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
{
|
|
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Move Declaration out of Condition"));
|
|
|
|
|
|
|
|
|
|
condition = mk.Condition();
|
|
|
|
|
pattern = mk.IfStatement(condition);
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ChangeSet changes;
|
2012-10-10 23:27:16 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
changes.copy(currentFile->range(core), currentFile->startOf(condition));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int insertPos = currentFile->startOf(pattern);
|
|
|
|
|
changes.move(currentFile->range(condition), insertPos);
|
|
|
|
|
changes.insert(insertPos, QLatin1String(";\n"));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->appendIndentRange(currentFile->range(pattern));
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ASTMatcher matcher;
|
|
|
|
|
ASTPatternBuilder mk;
|
|
|
|
|
CPPEditorWidget *editor;
|
|
|
|
|
ConditionAST *condition;
|
|
|
|
|
IfStatementAST *pattern;
|
|
|
|
|
CoreDeclaratorAST *core;
|
2010-07-26 13:06:33 +02:00
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void MoveDeclarationOutOfIf::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
typedef QSharedPointer<MoveDeclarationOutOfIfOp> Ptr;
|
|
|
|
|
Ptr op(new MoveDeclarationOutOfIfOp(interface));
|
|
|
|
|
|
|
|
|
|
int index = path.size() - 1;
|
|
|
|
|
for (; index != -1; --index) {
|
|
|
|
|
if (IfStatementAST *statement = path.at(index)->asIfStatement()) {
|
|
|
|
|
if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) {
|
|
|
|
|
DeclaratorAST *declarator = op->condition->declarator;
|
|
|
|
|
op->core = declarator->core_declarator;
|
|
|
|
|
if (! op->core)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (interface->isCursorOn(op->core)) {
|
|
|
|
|
op->setPriority(index);
|
|
|
|
|
result.append(op);
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class MoveDeclarationOutOfWhileOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MoveDeclarationOutOfWhileOp(const CppQuickFixInterface &interface)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Move Declaration out of Condition"));
|
2012-10-10 23:27:16 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
condition = mk.Condition();
|
|
|
|
|
pattern = mk.WhileStatement(condition);
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ChangeSet changes;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
changes.insert(currentFile->startOf(condition), QLatin1String("("));
|
|
|
|
|
changes.insert(currentFile->endOf(condition), QLatin1String(") != 0"));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int insertPos = currentFile->startOf(pattern);
|
|
|
|
|
const int conditionStart = currentFile->startOf(condition);
|
|
|
|
|
changes.move(conditionStart, currentFile->startOf(core), insertPos);
|
|
|
|
|
changes.copy(currentFile->range(core), insertPos);
|
|
|
|
|
changes.insert(insertPos, QLatin1String(";\n"));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->appendIndentRange(currentFile->range(pattern));
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ASTMatcher matcher;
|
|
|
|
|
ASTPatternBuilder mk;
|
|
|
|
|
CPPEditorWidget *editor;
|
|
|
|
|
ConditionAST *condition;
|
|
|
|
|
WhileStatementAST *pattern;
|
|
|
|
|
CoreDeclaratorAST *core;
|
|
|
|
|
};
|
2011-01-07 16:55:34 +01:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void MoveDeclarationOutOfWhile::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
QSharedPointer<MoveDeclarationOutOfWhileOp> op(new MoveDeclarationOutOfWhileOp(interface));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int index = path.size() - 1;
|
|
|
|
|
for (; index != -1; --index) {
|
|
|
|
|
if (WhileStatementAST *statement = path.at(index)->asWhileStatement()) {
|
|
|
|
|
if (statement->match(op->pattern, &op->matcher) && op->condition->declarator) {
|
|
|
|
|
DeclaratorAST *declarator = op->condition->declarator;
|
|
|
|
|
op->core = declarator->core_declarator;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! op->core)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! declarator->equal_token)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! declarator->initializer)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (interface->isCursorOn(op->core)) {
|
|
|
|
|
op->setPriority(index);
|
|
|
|
|
result.append(op);
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class SplitIfStatementOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SplitIfStatementOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
IfStatementAST *pattern, BinaryExpressionAST *condition)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, pattern(pattern)
|
|
|
|
|
, condition(condition)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Split if Statement"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const Token binaryToken = currentFile->tokenAt(condition->binary_op_token);
|
2012-10-10 23:27:16 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (binaryToken.is(T_AMPER_AMPER))
|
|
|
|
|
splitAndCondition(currentFile);
|
|
|
|
|
else
|
|
|
|
|
splitOrCondition(currentFile);
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void splitAndCondition(CppRefactoringFilePtr currentFile) const
|
|
|
|
|
{
|
|
|
|
|
ChangeSet changes;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int startPos = currentFile->startOf(pattern);
|
|
|
|
|
changes.insert(startPos, QLatin1String("if ("));
|
|
|
|
|
changes.move(currentFile->range(condition->left_expression), startPos);
|
|
|
|
|
changes.insert(startPos, QLatin1String(") {\n"));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int lExprEnd = currentFile->endOf(condition->left_expression);
|
|
|
|
|
changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
|
|
|
|
|
changes.insert(currentFile->endOf(pattern), QLatin1String("\n}"));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->appendIndentRange(currentFile->range(pattern));
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void splitOrCondition(CppRefactoringFilePtr currentFile) const
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
ChangeSet changes;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
StatementAST *ifTrueStatement = pattern->statement;
|
|
|
|
|
CompoundStatementAST *compoundStatement = ifTrueStatement->asCompoundStatement();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int insertPos = currentFile->endOf(ifTrueStatement);
|
|
|
|
|
if (compoundStatement)
|
|
|
|
|
changes.insert(insertPos, QLatin1String(" "));
|
|
|
|
|
else
|
|
|
|
|
changes.insert(insertPos, QLatin1String("\n"));
|
|
|
|
|
changes.insert(insertPos, QLatin1String("else if ("));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int rExprStart = currentFile->startOf(condition->right_expression);
|
|
|
|
|
changes.move(rExprStart, currentFile->startOf(pattern->rparen_token), insertPos);
|
|
|
|
|
changes.insert(insertPos, QLatin1String(")"));
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int rParenEnd = currentFile->endOf(pattern->rparen_token);
|
|
|
|
|
changes.copy(rParenEnd, currentFile->endOf(pattern->statement), insertPos);
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int lExprEnd = currentFile->endOf(condition->left_expression);
|
|
|
|
|
changes.remove(lExprEnd, currentFile->startOf(condition->right_expression));
|
|
|
|
|
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->appendIndentRange(currentFile->range(pattern));
|
|
|
|
|
currentFile->apply();
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2013-02-07 12:16:41 +01:00
|
|
|
IfStatementAST *pattern;
|
|
|
|
|
BinaryExpressionAST *condition;
|
|
|
|
|
};
|
2012-10-10 23:27:16 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void SplitIfStatement::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
IfStatementAST *pattern = 0;
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int index = path.size() - 1;
|
|
|
|
|
for (; index != -1; --index) {
|
|
|
|
|
AST *node = path.at(index);
|
|
|
|
|
if (IfStatementAST *stmt = node->asIfStatement()) {
|
|
|
|
|
pattern = stmt;
|
|
|
|
|
break;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! pattern || ! pattern->statement)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
unsigned splitKind = 0;
|
|
|
|
|
for (++index; index < path.size(); ++index) {
|
|
|
|
|
AST *node = path.at(index);
|
|
|
|
|
BinaryExpressionAST *condition = node->asBinaryExpression();
|
|
|
|
|
if (! condition)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
Token binaryToken = interface->currentFile()->tokenAt(condition->binary_op_token);
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// only accept a chain of ||s or &&s - no mixing
|
|
|
|
|
if (! splitKind) {
|
|
|
|
|
splitKind = binaryToken.kind();
|
|
|
|
|
if (splitKind != T_AMPER_AMPER && splitKind != T_PIPE_PIPE)
|
|
|
|
|
return;
|
|
|
|
|
// we can't reliably split &&s in ifs with an else branch
|
|
|
|
|
if (splitKind == T_AMPER_AMPER && pattern->else_statement)
|
|
|
|
|
return;
|
|
|
|
|
} else if (splitKind != binaryToken.kind()) {
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (interface->isCursorOn(condition->binary_op_token)) {
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new SplitIfStatementOp(interface, index, pattern, condition)));
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
2011-09-30 10:45:11 +02:00
|
|
|
|
2011-12-14 14:23:25 +01:00
|
|
|
/* Analze a string/character literal like "x", QLatin1String("x") and return the literal
|
|
|
|
|
* (StringLiteral or NumericLiteral for characters) and its type
|
|
|
|
|
* and the enclosing function (QLatin1String, tr...) */
|
|
|
|
|
ExpressionAST *WrapStringLiteral::analyze(const QList<AST *> &path,
|
2013-04-11 18:01:40 +02:00
|
|
|
const CppRefactoringFilePtr &file, Type *type,
|
2011-12-14 14:23:25 +01:00
|
|
|
QByteArray *enclosingFunction /* = 0 */,
|
|
|
|
|
CallAST **enclosingFunctionCall /* = 0 */)
|
|
|
|
|
{
|
|
|
|
|
*type = TypeNone;
|
|
|
|
|
if (enclosingFunction)
|
|
|
|
|
enclosingFunction->clear();
|
|
|
|
|
if (enclosingFunctionCall)
|
|
|
|
|
*enclosingFunctionCall = 0;
|
|
|
|
|
|
|
|
|
|
if (path.isEmpty())
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
ExpressionAST *literal = path.last()->asExpression();
|
|
|
|
|
if (literal) {
|
|
|
|
|
if (literal->asStringLiteral()) {
|
|
|
|
|
// Check for Objective C string (@"bla")
|
|
|
|
|
const QChar firstChar = file->charAt(file->startOf(literal));
|
|
|
|
|
*type = firstChar == QLatin1Char('@') ? TypeObjCString : TypeString;
|
|
|
|
|
} else if (NumericLiteralAST *numericLiteral = literal->asNumericLiteral()) {
|
|
|
|
|
// character ('c') constants are numeric.
|
|
|
|
|
if (file->tokenAt(numericLiteral->literal_token).is(T_CHAR_LITERAL))
|
|
|
|
|
*type = TypeChar;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2011-12-14 14:23:25 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2011-12-14 14:23:25 +01:00
|
|
|
if (*type != TypeNone && enclosingFunction && path.size() > 1) {
|
|
|
|
|
if (CallAST *call = path.at(path.size() - 2)->asCall()) {
|
|
|
|
|
if (call->base_expression) {
|
|
|
|
|
if (IdExpressionAST *idExpr = call->base_expression->asIdExpression()) {
|
|
|
|
|
if (SimpleNameAST *functionName = idExpr->name->asSimpleName()) {
|
|
|
|
|
*enclosingFunction = file->tokenAt(functionName->identifier_token).identifier->chars();
|
|
|
|
|
if (enclosingFunctionCall)
|
|
|
|
|
*enclosingFunctionCall = call;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-12-14 14:23:25 +01:00
|
|
|
}
|
|
|
|
|
return literal;
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
/// Operation performs the operations of type ActionFlags passed in as actions.
|
|
|
|
|
class WrapStringLiteralOp : public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
typedef WrapStringLiteral Factory;
|
|
|
|
|
|
|
|
|
|
WrapStringLiteralOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
unsigned actions, const QString &description, ExpressionAST *literal,
|
|
|
|
|
const QString &translationContext = QString())
|
|
|
|
|
: CppQuickFixOperation(interface, priority), m_actions(actions), m_literal(literal),
|
|
|
|
|
m_translationContext(translationContext)
|
|
|
|
|
{
|
|
|
|
|
setDescription(description);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
ChangeSet changes;
|
|
|
|
|
|
|
|
|
|
const int startPos = currentFile->startOf(m_literal);
|
|
|
|
|
const int endPos = currentFile->endOf(m_literal);
|
|
|
|
|
|
|
|
|
|
// kill leading '@'. No need to adapt endPos, that is done by ChangeSet
|
|
|
|
|
if (m_actions & Factory::RemoveObjectiveCAction)
|
|
|
|
|
changes.remove(startPos, startPos + 1);
|
|
|
|
|
|
|
|
|
|
// Fix quotes
|
|
|
|
|
if (m_actions & (Factory::SingleQuoteAction | Factory::DoubleQuoteAction)) {
|
2013-04-11 18:01:40 +02:00
|
|
|
const QString newQuote((m_actions & Factory::SingleQuoteAction)
|
|
|
|
|
? QLatin1Char('\'') : QLatin1Char('"'));
|
2013-02-07 12:16:41 +01:00
|
|
|
changes.replace(startPos, startPos + 1, newQuote);
|
|
|
|
|
changes.replace(endPos - 1, endPos, newQuote);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert single character strings into character constants
|
|
|
|
|
if (m_actions & Factory::ConvertEscapeSequencesToCharAction) {
|
|
|
|
|
StringLiteralAST *stringLiteral = m_literal->asStringLiteral();
|
|
|
|
|
QTC_ASSERT(stringLiteral, return ;);
|
|
|
|
|
const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token).identifier->chars());
|
|
|
|
|
const QByteArray newContents = Factory::stringToCharEscapeSequences(oldContents);
|
|
|
|
|
QTC_ASSERT(!newContents.isEmpty(), return ;);
|
|
|
|
|
if (oldContents != newContents)
|
|
|
|
|
changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert character constants into strings constants
|
|
|
|
|
if (m_actions & Factory::ConvertEscapeSequencesToStringAction) {
|
|
|
|
|
NumericLiteralAST *charLiteral = m_literal->asNumericLiteral(); // char 'c' constants are numerical.
|
|
|
|
|
QTC_ASSERT(charLiteral, return ;);
|
|
|
|
|
const QByteArray oldContents(currentFile->tokenAt(charLiteral->literal_token).identifier->chars());
|
|
|
|
|
const QByteArray newContents = Factory::charToStringEscapeSequences(oldContents);
|
|
|
|
|
QTC_ASSERT(!newContents.isEmpty(), return ;);
|
|
|
|
|
if (oldContents != newContents)
|
|
|
|
|
changes.replace(startPos + 1, endPos -1, QString::fromLatin1(newContents));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enclose in literal or translation function, macro.
|
|
|
|
|
if (m_actions & (Factory::EncloseActionMask | Factory::TranslationMask)) {
|
|
|
|
|
changes.insert(endPos, QString(QLatin1Char(')')));
|
|
|
|
|
QString leading = Factory::replacement(m_actions);
|
|
|
|
|
leading += QLatin1Char('(');
|
2013-04-11 18:01:40 +02:00
|
|
|
if (m_actions
|
|
|
|
|
& (Factory::TranslateQCoreApplicationAction | Factory::TranslateNoopAction)) {
|
2013-02-07 12:16:41 +01:00
|
|
|
leading += QLatin1Char('"');
|
|
|
|
|
leading += m_translationContext;
|
|
|
|
|
leading += QLatin1String("\", ");
|
|
|
|
|
}
|
|
|
|
|
changes.insert(startPos, leading);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const unsigned m_actions;
|
|
|
|
|
ExpressionAST *m_literal;
|
|
|
|
|
const QString m_translationContext;
|
|
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2012-10-10 23:27:16 +02:00
|
|
|
void WrapStringLiteral::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
2011-12-14 14:23:25 +01:00
|
|
|
{
|
|
|
|
|
typedef CppQuickFixOperation::Ptr OperationPtr;
|
|
|
|
|
|
|
|
|
|
Type type = TypeNone;
|
|
|
|
|
QByteArray enclosingFunction;
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
2013-02-07 12:16:41 +01:00
|
|
|
ExpressionAST *literal = analyze(path, file, &type, &enclosingFunction);
|
2011-12-14 14:23:25 +01:00
|
|
|
if (!literal || type == TypeNone)
|
2012-10-10 23:27:16 +02:00
|
|
|
return;
|
2011-12-14 14:23:25 +01:00
|
|
|
if ((type == TypeChar && enclosingFunction == "QLatin1Char")
|
|
|
|
|
|| isQtStringLiteral(enclosingFunction)
|
|
|
|
|
|| isQtStringTranslation(enclosingFunction))
|
2012-10-10 23:27:16 +02:00
|
|
|
return;
|
2011-09-30 10:45:11 +02:00
|
|
|
|
2011-12-14 14:23:25 +01:00
|
|
|
const int priority = path.size() - 1; // very high priority
|
|
|
|
|
if (type == TypeChar) {
|
|
|
|
|
unsigned actions = EncloseInQLatin1CharAction;
|
2013-02-07 12:16:41 +01:00
|
|
|
QString description = msgQtStringLiteralDescription(replacement(actions));
|
|
|
|
|
result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
|
|
|
|
|
description, literal));
|
2011-12-14 14:23:25 +01:00
|
|
|
if (NumericLiteralAST *charLiteral = literal->asNumericLiteral()) {
|
|
|
|
|
const QByteArray contents(file->tokenAt(charLiteral->literal_token).identifier->chars());
|
|
|
|
|
if (!charToStringEscapeSequences(contents).isEmpty()) {
|
|
|
|
|
actions = DoubleQuoteAction | ConvertEscapeSequencesToStringAction;
|
|
|
|
|
description = QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Convert to String Literal");
|
2013-02-07 12:16:41 +01:00
|
|
|
result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
|
|
|
|
|
description, literal));
|
2011-12-14 14:23:25 +01:00
|
|
|
}
|
2011-09-30 10:45:11 +02:00
|
|
|
}
|
2011-12-14 14:23:25 +01:00
|
|
|
} else {
|
|
|
|
|
const unsigned objectiveCActions = type == TypeObjCString ?
|
|
|
|
|
unsigned(RemoveObjectiveCAction) : 0u;
|
|
|
|
|
unsigned actions = 0;
|
|
|
|
|
if (StringLiteralAST *stringLiteral = literal->asStringLiteral()) {
|
|
|
|
|
const QByteArray contents(file->tokenAt(stringLiteral->literal_token).identifier->chars());
|
|
|
|
|
if (!stringToCharEscapeSequences(contents).isEmpty()) {
|
|
|
|
|
actions = EncloseInQLatin1CharAction | SingleQuoteAction
|
|
|
|
|
| ConvertEscapeSequencesToCharAction | objectiveCActions;
|
|
|
|
|
QString description = QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Convert to Character Literal and Enclose in QLatin1Char(...)");
|
2013-04-11 18:01:40 +02:00
|
|
|
result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
|
|
|
|
|
description, literal));
|
2011-12-14 14:23:25 +01:00
|
|
|
actions &= ~EncloseInQLatin1CharAction;
|
|
|
|
|
description = QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Convert to Character Literal");
|
2013-04-11 18:01:40 +02:00
|
|
|
result << OperationPtr(new WrapStringLiteralOp(interface, priority, actions,
|
|
|
|
|
description, literal));
|
2011-12-14 14:23:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
actions = EncloseInQLatin1StringAction | objectiveCActions;
|
2013-02-07 12:16:41 +01:00
|
|
|
result << OperationPtr(
|
|
|
|
|
new WrapStringLiteralOp(interface, priority, actions,
|
|
|
|
|
msgQtStringLiteralDescription(replacement(actions), 4),
|
|
|
|
|
literal));
|
2011-12-14 14:23:25 +01:00
|
|
|
actions = EncloseInQStringLiteralAction | objectiveCActions;
|
2013-02-07 12:16:41 +01:00
|
|
|
result << OperationPtr(
|
|
|
|
|
new WrapStringLiteralOp(interface, priority, actions,
|
|
|
|
|
msgQtStringLiteralDescription(replacement(actions), 5), literal));
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2011-12-14 14:23:25 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2011-12-14 14:23:25 +01:00
|
|
|
QString WrapStringLiteral::replacement(unsigned actions)
|
|
|
|
|
{
|
|
|
|
|
if (actions & EncloseInQLatin1CharAction)
|
|
|
|
|
return QLatin1String("QLatin1Char");
|
|
|
|
|
if (actions & EncloseInQLatin1StringAction)
|
|
|
|
|
return QLatin1String("QLatin1String");
|
|
|
|
|
if (actions & EncloseInQStringLiteralAction)
|
|
|
|
|
return QLatin1String("QStringLiteral");
|
|
|
|
|
if (actions & TranslateTrAction)
|
|
|
|
|
return QLatin1String("tr");
|
|
|
|
|
if (actions & TranslateQCoreApplicationAction)
|
|
|
|
|
return QLatin1String("QCoreApplication::translate");
|
|
|
|
|
if (actions & TranslateNoopAction)
|
|
|
|
|
return QLatin1String("QT_TRANSLATE_NOOP");
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2011-12-14 14:23:25 +01:00
|
|
|
/* Convert single-character string literals into character literals with some
|
|
|
|
|
* special cases "a" --> 'a', "'" --> '\'', "\n" --> '\n', "\"" --> '"'. */
|
|
|
|
|
QByteArray WrapStringLiteral::stringToCharEscapeSequences(const QByteArray &content)
|
|
|
|
|
{
|
|
|
|
|
if (content.size() == 1)
|
|
|
|
|
return content.at(0) == '\'' ? QByteArray("\\'") : content;
|
|
|
|
|
if (content.size() == 2 && content.at(0) == '\\')
|
|
|
|
|
return content == "\\\"" ? QByteArray(1, '"') : content;
|
|
|
|
|
return QByteArray();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2011-12-14 14:23:25 +01:00
|
|
|
/* Convert character literal into a string literal with some special cases
|
|
|
|
|
* 'a' -> "a", '\n' -> "\n", '\'' --> "'", '"' --> "\"". */
|
|
|
|
|
QByteArray WrapStringLiteral::charToStringEscapeSequences(const QByteArray &content)
|
|
|
|
|
{
|
|
|
|
|
if (content.size() == 1)
|
|
|
|
|
return content.at(0) == '"' ? QByteArray("\\\"") : content;
|
|
|
|
|
if (content.size() == 2)
|
|
|
|
|
return content == "\\'" ? QByteArray("'") : content;
|
|
|
|
|
return QByteArray();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
inline QString WrapStringLiteral::msgQtStringLiteralDescription(const QString &replacement,
|
|
|
|
|
int qtVersion)
|
|
|
|
|
{
|
|
|
|
|
return QApplication::translate("CppTools::QuickFix", "Enclose in %1(...) (Qt %2)")
|
|
|
|
|
.arg(replacement).arg(qtVersion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline QString WrapStringLiteral::msgQtStringLiteralDescription(const QString &replacement)
|
|
|
|
|
{
|
|
|
|
|
return QApplication::translate("CppTools::QuickFix", "Enclose in %1(...)").arg(replacement);
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void TranslateStringLiteral::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
2011-12-14 14:23:25 +01:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
// Initialize
|
|
|
|
|
WrapStringLiteral::Type type = WrapStringLiteral::TypeNone;
|
|
|
|
|
QByteArray enclosingFunction;
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction);
|
|
|
|
|
if (!literal || type != WrapStringLiteral::TypeString
|
|
|
|
|
|| isQtStringLiteral(enclosingFunction) || isQtStringTranslation(enclosingFunction))
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
QString trContext;
|
|
|
|
|
|
|
|
|
|
QSharedPointer<Control> control = interface->context().control();
|
|
|
|
|
const Name *trName = control->identifier("tr");
|
|
|
|
|
|
|
|
|
|
// Check whether we are in a method:
|
|
|
|
|
const QString description = QApplication::translate("CppTools::QuickFix", "Mark as Translatable");
|
|
|
|
|
for (int i = path.size() - 1; i >= 0; --i) {
|
|
|
|
|
if (FunctionDefinitionAST *definition = path.at(i)->asFunctionDefinition()) {
|
|
|
|
|
Function *function = definition->symbol;
|
|
|
|
|
ClassOrNamespace *b = interface->context().lookupType(function);
|
|
|
|
|
if (b) {
|
|
|
|
|
// Do we have a tr method?
|
|
|
|
|
foreach (const LookupItem &r, b->find(trName)) {
|
|
|
|
|
Symbol *s = r.declaration();
|
|
|
|
|
if (s->type()->isFunctionType()) {
|
|
|
|
|
// no context required for tr
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new WrapStringLiteralOp(interface, path.size() - 1,
|
|
|
|
|
WrapStringLiteral::TranslateTrAction,
|
|
|
|
|
description, literal)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// We need to do a QCA::translate, so we need a context.
|
|
|
|
|
// Use fully qualified class name:
|
|
|
|
|
Overview oo;
|
|
|
|
|
foreach (const Name *n, LookupContext::path(function)) {
|
|
|
|
|
if (!trContext.isEmpty())
|
|
|
|
|
trContext.append(QLatin1String("::"));
|
|
|
|
|
trContext.append(oo.prettyName(n));
|
|
|
|
|
}
|
|
|
|
|
// ... or global if none available!
|
|
|
|
|
if (trContext.isEmpty())
|
|
|
|
|
trContext = QLatin1String("GLOBAL");
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new WrapStringLiteralOp(interface, path.size() - 1,
|
|
|
|
|
WrapStringLiteral::TranslateQCoreApplicationAction,
|
|
|
|
|
description, literal, trContext)));
|
|
|
|
|
return;
|
2011-12-14 14:23:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// We need to use Q_TRANSLATE_NOOP
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new WrapStringLiteralOp(interface, path.size() - 1,
|
|
|
|
|
WrapStringLiteral::TranslateNoopAction,
|
|
|
|
|
description, literal, trContext)));
|
2011-12-14 14:23:25 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class ConvertCStringToNSStringOp: public CppQuickFixOperation
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2013-02-07 12:16:41 +01:00
|
|
|
ConvertCStringToNSStringOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
StringLiteralAST *stringLiteral, CallAST *qlatin1Call)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, stringLiteral(stringLiteral)
|
|
|
|
|
, qlatin1Call(qlatin1Call)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Convert to Objective-C String Literal"));
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
2010-08-13 12:49:11 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ChangeSet changes;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (qlatin1Call) {
|
2013-04-11 18:01:40 +02:00
|
|
|
changes.replace(currentFile->startOf(qlatin1Call), currentFile->startOf(stringLiteral),
|
|
|
|
|
QLatin1String("@"));
|
2013-02-07 12:16:41 +01:00
|
|
|
changes.remove(currentFile->endOf(stringLiteral), currentFile->endOf(qlatin1Call));
|
|
|
|
|
} else {
|
|
|
|
|
changes.insert(currentFile->startOf(stringLiteral), QLatin1String("@"));
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->apply();
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2013-02-07 12:16:41 +01:00
|
|
|
StringLiteralAST *stringLiteral;
|
|
|
|
|
CallAST *qlatin1Call;
|
|
|
|
|
};
|
2012-10-10 23:27:16 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void ConvertCStringToNSString::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-26 23:37:50 +04:00
|
|
|
if (!interface->editor()->isObjCEnabled())
|
2013-02-07 12:16:41 +01:00
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
WrapStringLiteral::Type type = WrapStringLiteral::TypeNone;
|
|
|
|
|
QByteArray enclosingFunction;
|
|
|
|
|
CallAST *qlatin1Call;
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
2013-04-11 18:01:40 +02:00
|
|
|
ExpressionAST *literal = WrapStringLiteral::analyze(path, file, &type, &enclosingFunction,
|
|
|
|
|
&qlatin1Call);
|
2013-02-07 12:16:41 +01:00
|
|
|
if (!literal || type != WrapStringLiteral::TypeString)
|
|
|
|
|
return;
|
|
|
|
|
if (!isQtStringLiteral(enclosingFunction))
|
|
|
|
|
qlatin1Call = 0;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new ConvertCStringToNSStringOp(interface, path.size() - 1, literal->asStringLiteral(),
|
|
|
|
|
qlatin1Call)));
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class ConvertNumericLiteralOp: public CppQuickFixOperation
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2013-02-07 12:16:41 +01:00
|
|
|
ConvertNumericLiteralOp(const CppQuickFixInterface &interface, int start, int end,
|
|
|
|
|
const QString &replacement)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
, start(start)
|
|
|
|
|
, end(end)
|
|
|
|
|
, replacement(replacement)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void perform()
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ChangeSet changes;
|
|
|
|
|
changes.replace(start, end, replacement);
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
private:
|
|
|
|
|
int start, end;
|
|
|
|
|
QString replacement;
|
|
|
|
|
};
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void ConvertNumericLiteral::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (path.isEmpty())
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
NumericLiteralAST *literal = path.last()->asNumericLiteral();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! literal)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
Token token = file->tokenAt(literal->asNumericLiteral()->literal_token);
|
|
|
|
|
if (!token.is(T_NUMERIC_LITERAL))
|
|
|
|
|
return;
|
|
|
|
|
const NumericLiteral *numeric = token.number;
|
|
|
|
|
if (numeric->isDouble() || numeric->isFloat())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// remove trailing L or U and stuff
|
|
|
|
|
const char * const spell = numeric->chars();
|
|
|
|
|
int numberLength = numeric->size();
|
|
|
|
|
while (numberLength > 0 && !std::isxdigit(spell[numberLength - 1]))
|
|
|
|
|
--numberLength;
|
|
|
|
|
if (numberLength < 1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// convert to number
|
|
|
|
|
bool valid;
|
|
|
|
|
ulong value = QString::fromUtf8(spell).left(numberLength).toULong(&valid, 0);
|
|
|
|
|
if (!valid) // e.g. octal with digit > 7
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const int priority = path.size() - 1; // very high priority
|
|
|
|
|
const int start = file->startOf(literal);
|
|
|
|
|
const char * const str = numeric->chars();
|
|
|
|
|
|
|
|
|
|
if (!numeric->isHex()) {
|
|
|
|
|
/*
|
|
|
|
|
Convert integer literal to hex representation.
|
|
|
|
|
Replace
|
|
|
|
|
32
|
|
|
|
|
040
|
|
|
|
|
With
|
|
|
|
|
0x20
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
QString replacement;
|
|
|
|
|
replacement.sprintf("0x%lX", value);
|
|
|
|
|
QuickFixOperation::Ptr op(
|
|
|
|
|
new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement));
|
|
|
|
|
op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Hexadecimal"));
|
|
|
|
|
op->setPriority(priority);
|
|
|
|
|
result.append(op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value != 0) {
|
|
|
|
|
if (!(numberLength > 1 && str[0] == '0' && str[1] != 'x' && str[1] != 'X')) {
|
2010-07-26 13:06:33 +02:00
|
|
|
/*
|
2013-02-07 12:16:41 +01:00
|
|
|
Convert integer literal to octal representation.
|
2010-07-26 13:06:33 +02:00
|
|
|
Replace
|
|
|
|
|
32
|
|
|
|
|
0x20
|
2013-02-07 12:16:41 +01:00
|
|
|
With
|
|
|
|
|
040
|
2010-07-26 13:06:33 +02:00
|
|
|
*/
|
|
|
|
|
QString replacement;
|
2013-02-07 12:16:41 +01:00
|
|
|
replacement.sprintf("0%lo", value);
|
|
|
|
|
QuickFixOperation::Ptr op(
|
|
|
|
|
new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement));
|
|
|
|
|
op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Octal"));
|
2010-07-26 13:06:33 +02:00
|
|
|
op->setPriority(priority);
|
|
|
|
|
result.append(op);
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (value != 0 || numeric->isHex()) {
|
|
|
|
|
if (!(numberLength > 1 && str[0] != '0')) {
|
|
|
|
|
/*
|
|
|
|
|
Convert integer literal to decimal representation.
|
|
|
|
|
Replace
|
|
|
|
|
0x20
|
|
|
|
|
040
|
|
|
|
|
With
|
|
|
|
|
32
|
|
|
|
|
*/
|
|
|
|
|
QString replacement;
|
|
|
|
|
replacement.sprintf("%lu", value);
|
|
|
|
|
QuickFixOperation::Ptr op(
|
|
|
|
|
new ConvertNumericLiteralOp(interface, start, start + numberLength, replacement));
|
|
|
|
|
op->setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Decimal"));
|
|
|
|
|
op->setPriority(priority);
|
|
|
|
|
result.append(op);
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class AddIncludeForForwardDeclarationOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
AddIncludeForForwardDeclarationOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
Symbol *fwdClass)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, fwdClass(fwdClass)
|
|
|
|
|
{
|
|
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"#include Header File"));
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
QTC_ASSERT(fwdClass != 0, return);
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
SymbolFinder symbolFinder;
|
2013-02-07 12:16:41 +01:00
|
|
|
if (Class *k = symbolFinder.findMatchingClassDeclaration(fwdClass, snapshot())) {
|
|
|
|
|
const QString headerFile = QString::fromUtf8(k->fileName(), k->fileNameLength());
|
|
|
|
|
|
|
|
|
|
// collect the fwd headers
|
|
|
|
|
Snapshot fwdHeaders;
|
|
|
|
|
fwdHeaders.insert(snapshot().document(headerFile));
|
|
|
|
|
foreach (Document::Ptr doc, snapshot()) {
|
|
|
|
|
QFileInfo headerFileInfo(doc->fileName());
|
|
|
|
|
if (doc->globalSymbolCount() == 0 && doc->includes().size() == 1)
|
|
|
|
|
fwdHeaders.insert(doc);
|
|
|
|
|
else if (headerFileInfo.suffix().isEmpty())
|
|
|
|
|
fwdHeaders.insert(doc);
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
DependencyTable dep;
|
|
|
|
|
dep.build(fwdHeaders);
|
|
|
|
|
QStringList candidates = dep.dependencyTable().value(headerFile);
|
|
|
|
|
|
|
|
|
|
const QString className = QString::fromUtf8(k->identifier()->chars());
|
|
|
|
|
|
|
|
|
|
QString best;
|
|
|
|
|
foreach (const QString &c, candidates) {
|
|
|
|
|
QFileInfo headerFileInfo(c);
|
|
|
|
|
if (headerFileInfo.fileName() == className) {
|
|
|
|
|
best = c;
|
|
|
|
|
break;
|
|
|
|
|
} else if (headerFileInfo.fileName().at(0).isUpper()) {
|
|
|
|
|
best = c;
|
|
|
|
|
// and continue
|
|
|
|
|
} else if (! best.isEmpty()) {
|
|
|
|
|
if (c.count(QLatin1Char('/')) < best.count(QLatin1Char('/')))
|
|
|
|
|
best = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (best.isEmpty())
|
|
|
|
|
best = headerFile;
|
2011-01-07 16:55:34 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
int pos = currentFile->startOf(1);
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
unsigned currentLine = currentFile->cursor().blockNumber() + 1;
|
|
|
|
|
unsigned bestLine = 0;
|
2013-04-11 18:01:40 +02:00
|
|
|
foreach (const Document::Include &incl,
|
|
|
|
|
assistInterface()->semanticInfo().doc->includes()) {
|
2013-02-07 12:16:41 +01:00
|
|
|
if (incl.line() < currentLine)
|
|
|
|
|
bestLine = incl.line();
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
|
|
|
|
|
if (bestLine)
|
|
|
|
|
pos = currentFile->document()->findBlockByNumber(bestLine).position();
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
ChangeSet changes;
|
2013-02-07 12:16:41 +01:00
|
|
|
changes.insert(pos, QLatin1String("#include <")
|
|
|
|
|
+ QFileInfo(best).fileName() + QLatin1String(">\n"));
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->apply();
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-10 23:27:16 +02:00
|
|
|
static Symbol *checkName(const CppQuickFixInterface &interface, NameAST *ast)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2011-04-15 16:19:23 +02:00
|
|
|
if (ast && interface->isCursorOn(ast)) {
|
2010-07-26 13:06:33 +02:00
|
|
|
if (const Name *name = ast->name) {
|
|
|
|
|
unsigned line, column;
|
2011-04-15 16:19:23 +02:00
|
|
|
interface->semanticInfo().doc->translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
|
2010-07-26 13:06:33 +02:00
|
|
|
|
|
|
|
|
Symbol *fwdClass = 0;
|
|
|
|
|
|
2011-04-15 16:19:23 +02:00
|
|
|
foreach (const LookupItem &r,
|
2013-04-11 18:01:40 +02:00
|
|
|
interface->context().lookup(name, interface->semanticInfo().doc->scopeAt(line, column))) {
|
2010-07-26 13:06:33 +02:00
|
|
|
if (! r.declaration())
|
|
|
|
|
continue;
|
|
|
|
|
else if (ForwardClassDeclaration *fwd = r.declaration()->asForwardClassDeclaration())
|
|
|
|
|
fwdClass = fwd;
|
|
|
|
|
else if (r.declaration()->isClass())
|
|
|
|
|
return 0; // nothing to do.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fwdClass;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2013-02-07 12:16:41 +01:00
|
|
|
Symbol *fwdClass;
|
|
|
|
|
};
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void AddIncludeForForwardDeclaration::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
for (int index = path.size() - 1; index != -1; --index) {
|
|
|
|
|
AST *ast = path.at(index);
|
|
|
|
|
if (NamedTypeSpecifierAST *namedTy = ast->asNamedTypeSpecifier()) {
|
2013-04-11 18:01:40 +02:00
|
|
|
if (Symbol *fwdClass = AddIncludeForForwardDeclarationOp::checkName(interface,
|
|
|
|
|
namedTy->name)) {
|
2013-02-07 12:16:41 +01:00
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new AddIncludeForForwardDeclarationOp(interface, index, fwdClass)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else if (ElaboratedTypeSpecifierAST *eTy = ast->asElaboratedTypeSpecifier()) {
|
2013-04-11 18:01:40 +02:00
|
|
|
if (Symbol *fwdClass = AddIncludeForForwardDeclarationOp::checkName(interface,
|
|
|
|
|
eTy->name)) {
|
2013-02-07 12:16:41 +01:00
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new AddIncludeForForwardDeclarationOp(interface, index, fwdClass)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class AddLocalDeclarationOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
AddLocalDeclarationOp(const CppQuickFixInterface &interface,
|
|
|
|
|
int priority,
|
|
|
|
|
const BinaryExpressionAST *binaryAST,
|
|
|
|
|
const SimpleNameAST *simpleNameAST)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, binaryAST(binaryAST)
|
|
|
|
|
, simpleNameAST(simpleNameAST)
|
|
|
|
|
{
|
|
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix", "Add Local Declaration"));
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
|
|
|
|
typeOfExpression.init(assistInterface()->semanticInfo().doc,
|
|
|
|
|
snapshot(), assistInterface()->context().bindings());
|
|
|
|
|
Scope *scope = currentFile->scopeAt(binaryAST->firstToken());
|
|
|
|
|
const QList<LookupItem> result =
|
|
|
|
|
typeOfExpression(currentFile->textOf(binaryAST->right_expression).toUtf8(),
|
|
|
|
|
scope,
|
|
|
|
|
TypeOfExpression::Preprocess);
|
|
|
|
|
|
|
|
|
|
if (! result.isEmpty()) {
|
|
|
|
|
SubstitutionEnvironment env;
|
|
|
|
|
env.setContext(assistInterface()->context());
|
|
|
|
|
env.switchScope(result.first().scope());
|
|
|
|
|
ClassOrNamespace *con = typeOfExpression.context().lookupType(scope);
|
|
|
|
|
if (!con)
|
|
|
|
|
con = typeOfExpression.context().globalNamespace();
|
|
|
|
|
UseMinimalNames q(con);
|
|
|
|
|
env.enter(&q);
|
|
|
|
|
|
|
|
|
|
Control *control = assistInterface()->context().control().data();
|
|
|
|
|
FullySpecifiedType tn = rewriteType(result.first().type(), &env, control);
|
|
|
|
|
|
|
|
|
|
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
|
|
|
QString ty = oo.prettyType(tn, simpleNameAST->name);
|
|
|
|
|
if (! ty.isEmpty()) {
|
2013-04-11 18:01:40 +02:00
|
|
|
ChangeSet changes;
|
2013-02-07 12:16:41 +01:00
|
|
|
changes.replace(currentFile->startOf(binaryAST),
|
|
|
|
|
currentFile->endOf(simpleNameAST),
|
|
|
|
|
ty);
|
2011-08-17 11:35:57 +02:00
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->apply();
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
private:
|
|
|
|
|
const BinaryExpressionAST *binaryAST;
|
|
|
|
|
const SimpleNameAST *simpleNameAST;
|
2010-07-26 13:06:33 +02:00
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void AddLocalDeclaration::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
|
|
|
|
|
for (int index = path.size() - 1; index != -1; --index) {
|
|
|
|
|
if (BinaryExpressionAST *binary = path.at(index)->asBinaryExpression()) {
|
2013-04-11 18:01:40 +02:00
|
|
|
if (binary->left_expression && binary->right_expression
|
|
|
|
|
&& file->tokenAt(binary->binary_op_token).is(T_EQUAL)) {
|
2013-02-07 12:16:41 +01:00
|
|
|
IdExpressionAST *idExpr = binary->left_expression->asIdExpression();
|
2013-04-11 18:01:40 +02:00
|
|
|
if (interface->isCursorOn(binary->left_expression) && idExpr
|
|
|
|
|
&& idExpr->name->asSimpleName() != 0) {
|
2013-02-07 12:16:41 +01:00
|
|
|
SimpleNameAST *nameAST = idExpr->name->asSimpleName();
|
|
|
|
|
const QList<LookupItem> results = interface->context().lookup(nameAST->name, file->scopeAt(nameAST->firstToken()));
|
|
|
|
|
Declaration *decl = 0;
|
|
|
|
|
foreach (const LookupItem &r, results) {
|
|
|
|
|
if (! r.declaration())
|
|
|
|
|
continue;
|
|
|
|
|
else if (Declaration *d = r.declaration()->asDeclaration()) {
|
|
|
|
|
if (! d->type()->isFunctionType()) {
|
|
|
|
|
decl = d;
|
|
|
|
|
break;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (! decl) {
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new AddLocalDeclarationOp(interface, index, binary, nameAST)));
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class ConvertToCamelCaseOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ConvertToCamelCaseOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
const QString &newName)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, m_name(newName)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-04-11 18:01:40 +02:00
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix", "Convert to Camel Case"));
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < m_name.length(); ++i) {
|
|
|
|
|
QCharRef c = m_name[i];
|
|
|
|
|
if (c.isUpper()) {
|
|
|
|
|
c = c.toLower();
|
|
|
|
|
} else if (i < m_name.length() - 1
|
|
|
|
|
&& isConvertibleUnderscore(m_name, i)) {
|
|
|
|
|
m_name.remove(i, 1);
|
|
|
|
|
m_name[i] = m_name.at(i).toUpper();
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
2013-04-11 18:01:40 +02:00
|
|
|
static_cast<CPPEditorWidget*>(assistInterface()->editor())->renameUsagesNow(m_name);
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isConvertibleUnderscore(const QString &name, int pos)
|
|
|
|
|
{
|
|
|
|
|
return name.at(pos) == QLatin1Char('_') && name.at(pos+1).isLetter()
|
|
|
|
|
&& !(pos == 1 && name.at(0) == QLatin1Char('m'));
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
private:
|
|
|
|
|
QString m_name;
|
2010-07-26 13:06:33 +02:00
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void ConvertToCamelCase::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
const QList<AST *> &path = interface->path();
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (path.isEmpty())
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
AST * const ast = path.last();
|
|
|
|
|
const Name *name = 0;
|
|
|
|
|
if (const NameAST * const nameAst = ast->asName()) {
|
|
|
|
|
if (nameAst->name && nameAst->name->asNameId())
|
|
|
|
|
name = nameAst->name;
|
|
|
|
|
} else if (const NamespaceAST * const namespaceAst = ast->asNamespace()) {
|
|
|
|
|
name = namespaceAst->symbol->name();
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (!name)
|
|
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
QString newName = QString::fromUtf8(name->identifier()->chars());
|
|
|
|
|
if (newName.length() < 3)
|
|
|
|
|
return;
|
|
|
|
|
for (int i = 1; i < newName.length() - 1; ++i) {
|
|
|
|
|
if (ConvertToCamelCaseOp::isConvertibleUnderscore(newName, i)) {
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new ConvertToCamelCaseOp(interface, path.size() - 1, newName)));
|
2012-10-10 23:27:16 +02:00
|
|
|
return;
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class AddIncludeForUndefinedIdentifierOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
AddIncludeForUndefinedIdentifierOp(const CppQuickFixInterface &interface, int priority,
|
|
|
|
|
const QString &include)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, m_include(include)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Add #include %1").arg(m_include));
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr file = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
QList<Document::Include> includes = file->cppDocument()->includes();
|
2013-04-07 18:46:51 +02:00
|
|
|
if (includes.isEmpty()) {
|
|
|
|
|
// No includes, find possible first/multi line comment
|
|
|
|
|
int insertPos = 0;
|
|
|
|
|
QTextBlock block = file->document()->firstBlock();
|
|
|
|
|
while (block.isValid()) {
|
|
|
|
|
const QString trimmedText = block.text().trimmed();
|
|
|
|
|
|
|
|
|
|
// Only skip the first comment!
|
|
|
|
|
if (trimmedText.startsWith(QLatin1String("/*"))) {
|
|
|
|
|
do {
|
|
|
|
|
const int pos = block.text().indexOf(QLatin1String("*/"));
|
|
|
|
|
if (pos > -1) {
|
|
|
|
|
insertPos = block.position() + pos + 2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
block = block.next();
|
|
|
|
|
} while (block.isValid());
|
|
|
|
|
break;
|
|
|
|
|
} else if (trimmedText.startsWith(QLatin1String("//"))) {
|
|
|
|
|
block = block.next();
|
|
|
|
|
while (block.isValid()) {
|
|
|
|
|
if (!block.text().trimmed().startsWith(QLatin1String("//"))) {
|
|
|
|
|
insertPos = block.position() - 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
|
2013-04-07 18:46:51 +02:00
|
|
|
if (!trimmedText.isEmpty())
|
|
|
|
|
break;
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChangeSet changes;
|
|
|
|
|
if (insertPos != 0)
|
|
|
|
|
changes.insert(insertPos, QLatin1String("\n\n#include ") + m_include);
|
|
|
|
|
else
|
|
|
|
|
changes.insert(insertPos, QString::fromLatin1("#include %1\n\n").arg(m_include));
|
|
|
|
|
file->setChangeSet(changes);
|
|
|
|
|
file->apply();
|
|
|
|
|
} else {
|
|
|
|
|
// find location of last include in file
|
|
|
|
|
unsigned lastIncludeLine = 0;
|
|
|
|
|
foreach (const Document::Include &include, includes) {
|
|
|
|
|
if (include.line() > lastIncludeLine)
|
|
|
|
|
lastIncludeLine = include.line();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int insertPos = qMax(0, file->position(lastIncludeLine + 1, 1) - 1);
|
|
|
|
|
ChangeSet changes;
|
|
|
|
|
changes.insert(insertPos, QLatin1String("\n#include ") + m_include);
|
|
|
|
|
file->setChangeSet(changes);
|
|
|
|
|
file->apply();
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
private:
|
|
|
|
|
QString m_include;
|
2010-07-26 13:06:33 +02:00
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
2011-11-25 12:05:58 +01:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
CppClassesFilter *classesFilter = ExtensionSystem::PluginManager::getObject<CppClassesFilter>();
|
|
|
|
|
if (!classesFilter)
|
|
|
|
|
return;
|
2011-11-25 12:05:58 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const QList<AST *> &path = interface->path();
|
2011-11-25 12:05:58 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (path.isEmpty())
|
|
|
|
|
return;
|
2011-11-25 12:05:58 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// find the largest enclosing Name
|
|
|
|
|
const NameAST *enclosingName = 0;
|
|
|
|
|
const SimpleNameAST *innermostName = 0;
|
|
|
|
|
for (int i = path.size() - 1; i >= 0; --i) {
|
|
|
|
|
if (NameAST *nameAst = path.at(i)->asName()) {
|
|
|
|
|
enclosingName = nameAst;
|
|
|
|
|
if (!innermostName) {
|
|
|
|
|
innermostName = nameAst->asSimpleName();
|
|
|
|
|
if (!innermostName)
|
|
|
|
|
return;
|
2011-11-25 12:05:58 +01:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
} else {
|
|
|
|
|
break;
|
2011-11-25 12:05:58 +01:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
|
|
|
|
if (!enclosingName || !enclosingName->name)
|
|
|
|
|
return;
|
2011-11-25 12:05:58 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// find the enclosing scope
|
|
|
|
|
unsigned line, column;
|
|
|
|
|
const Document::Ptr &doc = interface->semanticInfo().doc;
|
|
|
|
|
doc->translationUnit()->getTokenStartPosition(enclosingName->firstToken(), &line, &column);
|
|
|
|
|
Scope *scope = doc->scopeAt(line, column);
|
|
|
|
|
if (!scope)
|
|
|
|
|
return;
|
2011-11-25 12:05:58 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// check if the name resolves to something
|
|
|
|
|
QList<LookupItem> existingResults = interface->context().lookup(enclosingName->name, scope);
|
|
|
|
|
if (!existingResults.isEmpty())
|
|
|
|
|
return;
|
2011-11-25 12:05:58 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
const QString &className = Overview().prettyName(innermostName->name);
|
|
|
|
|
if (className.isEmpty())
|
|
|
|
|
return;
|
2011-11-25 12:05:58 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// find the include paths
|
|
|
|
|
QStringList includePaths;
|
|
|
|
|
CppModelManagerInterface *modelManager = CppModelManagerInterface::instance();
|
|
|
|
|
QList<CppModelManagerInterface::ProjectInfo> projectInfos = modelManager->projectInfos();
|
|
|
|
|
bool inProject = false;
|
|
|
|
|
foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) {
|
2013-03-04 01:30:46 +04:00
|
|
|
foreach (ProjectPart::Ptr part, info.projectParts()) {
|
|
|
|
|
foreach (const ProjectFile &file, part->files) {
|
|
|
|
|
if (file.path == doc->fileName()) {
|
|
|
|
|
inProject = true;
|
|
|
|
|
includePaths += part->includePaths;
|
|
|
|
|
}
|
2011-11-25 12:05:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
|
|
|
|
if (!inProject) {
|
|
|
|
|
// better use all include paths than none
|
|
|
|
|
foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) {
|
2013-03-04 01:30:46 +04:00
|
|
|
foreach (ProjectPart::Ptr part, info.projectParts())
|
2013-02-07 12:16:41 +01:00
|
|
|
includePaths += part->includePaths;
|
2011-11-25 12:05:58 +01:00
|
|
|
}
|
2013-02-07 12:16:41 +01:00
|
|
|
}
|
2011-11-25 12:05:58 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// find a include file through the locator
|
|
|
|
|
QFutureInterface<Locator::FilterEntry> dummyInterface;
|
|
|
|
|
QList<Locator::FilterEntry> matches = classesFilter->matchesFor(dummyInterface, className);
|
|
|
|
|
bool classExists = false;
|
|
|
|
|
foreach (const Locator::FilterEntry &entry, matches) {
|
|
|
|
|
const ModelItemInfo info = entry.internalData.value<ModelItemInfo>();
|
|
|
|
|
if (info.symbolName != className)
|
|
|
|
|
continue;
|
|
|
|
|
classExists = true;
|
|
|
|
|
const QString &fileName = info.fileName;
|
|
|
|
|
const QFileInfo fileInfo(fileName);
|
|
|
|
|
|
|
|
|
|
// find the shortest way to include fileName given the includePaths
|
|
|
|
|
QString shortestInclude;
|
|
|
|
|
|
|
|
|
|
if (fileInfo.path() == QFileInfo(doc->fileName()).path()) {
|
|
|
|
|
shortestInclude = QLatin1Char('"') + fileInfo.fileName() + QLatin1Char('"');
|
|
|
|
|
} else {
|
|
|
|
|
foreach (const QString &includePath, includePaths) {
|
|
|
|
|
if (!fileName.startsWith(includePath))
|
|
|
|
|
continue;
|
|
|
|
|
QString relativePath = fileName.mid(includePath.size());
|
|
|
|
|
if (!relativePath.isEmpty() && relativePath.at(0) == QLatin1Char('/'))
|
|
|
|
|
relativePath = relativePath.mid(1);
|
|
|
|
|
if (shortestInclude.isEmpty() || relativePath.size() + 2 < shortestInclude.size())
|
|
|
|
|
shortestInclude = QLatin1Char('<') + relativePath + QLatin1Char('>');
|
2011-11-25 12:05:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (!shortestInclude.isEmpty()) {
|
|
|
|
|
result += CppQuickFixOperation::Ptr(
|
|
|
|
|
new AddIncludeForUndefinedIdentifierOp(interface, 0, shortestInclude));
|
2011-11-25 12:05:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
// for QSomething, propose a <QSomething> include -- if such a class was in the locator
|
|
|
|
|
if (classExists
|
|
|
|
|
&& className.size() > 2
|
|
|
|
|
&& className.at(0) == QLatin1Char('Q')
|
|
|
|
|
&& className.at(1).isUpper()) {
|
|
|
|
|
const QString include = QLatin1Char('<') + className + QLatin1Char('>');
|
|
|
|
|
result += CppQuickFixOperation::Ptr(
|
|
|
|
|
new AddIncludeForUndefinedIdentifierOp(interface, 1, include));
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-04 14:41:30 +01:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class RearrangeParamDeclarationListOp: public CppQuickFixOperation
|
2012-02-04 14:41:30 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2013-02-07 12:16:41 +01:00
|
|
|
enum Target { TargetPrevious, TargetNext };
|
2012-02-04 14:41:30 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
RearrangeParamDeclarationListOp(const CppQuickFixInterface &interface, AST *currentParam,
|
|
|
|
|
AST *targetParam, Target target)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
, m_currentParam(currentParam)
|
|
|
|
|
, m_targetParam(targetParam)
|
2012-02-04 14:41:30 +01:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
QString targetString;
|
|
|
|
|
if (target == TargetPrevious)
|
|
|
|
|
targetString = QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Switch with Previous Parameter");
|
|
|
|
|
else
|
|
|
|
|
targetString = QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Switch with Next Parameter");
|
|
|
|
|
setDescription(targetString);
|
|
|
|
|
}
|
2012-02-04 14:41:30 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
int targetEndPos = currentFile->endOf(m_targetParam);
|
|
|
|
|
ChangeSet changes;
|
|
|
|
|
changes.flip(currentFile->startOf(m_currentParam), currentFile->endOf(m_currentParam),
|
|
|
|
|
currentFile->startOf(m_targetParam), targetEndPos);
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->setOpenEditor(false, targetEndPos);
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
2012-02-04 14:41:30 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
private:
|
|
|
|
|
AST *m_currentParam;
|
|
|
|
|
AST *m_targetParam;
|
|
|
|
|
};
|
2012-02-04 14:41:30 +01:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void RearrangeParamDeclarationList::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> path = interface->path();
|
2012-02-04 14:41:30 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ParameterDeclarationAST *paramDecl = 0;
|
|
|
|
|
int index = path.size() - 1;
|
|
|
|
|
for (; index != -1; --index) {
|
|
|
|
|
paramDecl = path.at(index)->asParameterDeclaration();
|
|
|
|
|
if (paramDecl)
|
|
|
|
|
break;
|
2012-02-04 14:41:30 +01:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (index < 1)
|
|
|
|
|
return;
|
2012-02-04 14:41:30 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ParameterDeclarationClauseAST *paramDeclClause = path.at(index-1)->asParameterDeclarationClause();
|
|
|
|
|
QTC_ASSERT(paramDeclClause && paramDeclClause->parameter_declaration_list, return);
|
2012-10-10 23:27:16 +02:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
ParameterDeclarationListAST *paramListNode = paramDeclClause->parameter_declaration_list;
|
|
|
|
|
ParameterDeclarationListAST *prevParamListNode = 0;
|
|
|
|
|
while (paramListNode) {
|
|
|
|
|
if (paramDecl == paramListNode->value)
|
|
|
|
|
break;
|
|
|
|
|
prevParamListNode = paramListNode;
|
|
|
|
|
paramListNode = paramListNode->next;
|
|
|
|
|
}
|
2012-02-04 14:41:30 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
if (!paramListNode)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (prevParamListNode)
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(
|
|
|
|
|
new RearrangeParamDeclarationListOp(interface, paramListNode->value,
|
|
|
|
|
prevParamListNode->value, RearrangeParamDeclarationListOp::TargetPrevious)));
|
|
|
|
|
if (paramListNode->next)
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(
|
|
|
|
|
new RearrangeParamDeclarationListOp(interface, paramListNode->value,
|
|
|
|
|
paramListNode->next->value, RearrangeParamDeclarationListOp::TargetNext)));
|
|
|
|
|
}
|
2011-12-21 18:24:14 +01:00
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
class ReformatPointerDeclarationOp: public CppQuickFixOperation
|
2013-01-17 17:23:43 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2013-04-11 18:01:40 +02:00
|
|
|
ReformatPointerDeclarationOp(const CppQuickFixInterface &interface, const ChangeSet change)
|
2013-02-07 12:16:41 +01:00
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
, m_change(change)
|
2013-01-17 17:23:43 +01:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
QString description;
|
|
|
|
|
if (m_change.operationList().size() == 1) {
|
|
|
|
|
description = QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Reformat to \"%1\"").arg(m_change.operationList().first().text);
|
|
|
|
|
} else { // > 1
|
|
|
|
|
description = QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Reformat Pointers or References");
|
|
|
|
|
}
|
|
|
|
|
setDescription(description);
|
2013-01-17 17:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void perform()
|
2013-01-17 17:23:43 +01:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
currentFile->setChangeSet(m_change);
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
2013-01-17 17:23:43 +01:00
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
private:
|
2013-04-11 18:01:40 +02:00
|
|
|
ChangeSet m_change;
|
2013-01-17 17:23:43 +01:00
|
|
|
};
|
|
|
|
|
|
2013-02-12 11:57:01 +01:00
|
|
|
/// Filter the results of ASTPath.
|
|
|
|
|
/// The resulting list contains the supported AST types only once.
|
|
|
|
|
/// For this, the results of ASTPath are iterated in reverse order.
|
|
|
|
|
class ReformatPointerDeclarationASTPathResultsFilter
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ReformatPointerDeclarationASTPathResultsFilter()
|
|
|
|
|
: m_hasSimpleDeclaration(false)
|
|
|
|
|
, m_hasFunctionDefinition(false)
|
|
|
|
|
, m_hasParameterDeclaration(false)
|
|
|
|
|
, m_hasIfStatement(false)
|
|
|
|
|
, m_hasWhileStatement(false)
|
|
|
|
|
, m_hasForStatement(false)
|
|
|
|
|
, m_hasForeachStatement(false) {}
|
|
|
|
|
|
|
|
|
|
QList<AST*> filter(const QList<AST*> &astPathList)
|
|
|
|
|
{
|
|
|
|
|
QList<AST*> filtered;
|
|
|
|
|
|
|
|
|
|
for (int i = astPathList.size() - 1; i >= 0; --i) {
|
|
|
|
|
AST *ast = astPathList.at(i);
|
|
|
|
|
|
|
|
|
|
if (! m_hasSimpleDeclaration && ast->asSimpleDeclaration()) {
|
|
|
|
|
m_hasSimpleDeclaration = true;
|
|
|
|
|
filtered.append(ast);
|
|
|
|
|
} else if (! m_hasFunctionDefinition && ast->asFunctionDefinition()) {
|
|
|
|
|
m_hasFunctionDefinition = true;
|
|
|
|
|
filtered.append(ast);
|
|
|
|
|
} else if (! m_hasParameterDeclaration && ast->asParameterDeclaration()) {
|
|
|
|
|
m_hasParameterDeclaration = true;
|
|
|
|
|
filtered.append(ast);
|
|
|
|
|
} else if (! m_hasIfStatement && ast->asIfStatement()) {
|
|
|
|
|
m_hasIfStatement = true;
|
|
|
|
|
filtered.append(ast);
|
|
|
|
|
} else if (! m_hasWhileStatement && ast->asWhileStatement()) {
|
|
|
|
|
m_hasWhileStatement = true;
|
|
|
|
|
filtered.append(ast);
|
|
|
|
|
} else if (! m_hasForStatement && ast->asForStatement()) {
|
|
|
|
|
m_hasForStatement = true;
|
|
|
|
|
filtered.append(ast);
|
|
|
|
|
} else if (! m_hasForeachStatement && ast->asForeachStatement()) {
|
|
|
|
|
m_hasForeachStatement = true;
|
|
|
|
|
filtered.append(ast);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filtered;
|
|
|
|
|
}
|
2013-04-11 18:01:40 +02:00
|
|
|
|
2013-02-12 11:57:01 +01:00
|
|
|
private:
|
|
|
|
|
bool m_hasSimpleDeclaration;
|
|
|
|
|
bool m_hasFunctionDefinition;
|
|
|
|
|
bool m_hasParameterDeclaration;
|
|
|
|
|
bool m_hasIfStatement;
|
|
|
|
|
bool m_hasWhileStatement;
|
|
|
|
|
bool m_hasForStatement;
|
|
|
|
|
bool m_hasForeachStatement;
|
|
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2013-02-07 12:16:41 +01:00
|
|
|
void ReformatPointerDeclaration::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
2010-07-26 13:06:33 +02:00
|
|
|
{
|
2013-02-07 12:16:41 +01:00
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
|
|
|
|
|
Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
|
|
|
overview.showArgumentNames = true;
|
|
|
|
|
overview.showReturnTypes = true;
|
|
|
|
|
|
|
|
|
|
const QTextCursor cursor = file->cursor();
|
2013-04-11 18:01:40 +02:00
|
|
|
ChangeSet change;
|
2013-02-07 12:16:41 +01:00
|
|
|
PointerDeclarationFormatter formatter(file, overview,
|
|
|
|
|
PointerDeclarationFormatter::RespectCursor);
|
|
|
|
|
|
|
|
|
|
if (cursor.hasSelection()) {
|
|
|
|
|
// This will no work always as expected since this method is only called if
|
|
|
|
|
// interface-path() is not empty. If the user selects the whole document via
|
|
|
|
|
// ctrl-a and there is an empty line in the end, then the cursor is not on
|
|
|
|
|
// any AST and therefore no quick fix will be triggered.
|
|
|
|
|
change = formatter.format(file->cppDocument()->translationUnit()->ast());
|
|
|
|
|
if (! change.isEmpty()) {
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new ReformatPointerDeclarationOp(interface, change)));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2013-02-12 11:57:01 +01:00
|
|
|
const QList<AST *> suitableASTs
|
|
|
|
|
= ReformatPointerDeclarationASTPathResultsFilter().filter(path);
|
|
|
|
|
foreach (AST *ast, suitableASTs) {
|
2013-02-07 12:16:41 +01:00
|
|
|
change = formatter.format(ast);
|
|
|
|
|
if (! change.isEmpty()) {
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new ReformatPointerDeclarationOp(interface, change)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-26 13:06:33 +02:00
|
|
|
}
|
2013-04-11 18:01:40 +02:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class CaseStatementCollector : public ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CaseStatementCollector(Document::Ptr document, const Snapshot &snapshot,
|
|
|
|
|
Scope *scope)
|
|
|
|
|
: ASTVisitor(document->translationUnit()),
|
|
|
|
|
document(document),
|
|
|
|
|
scope(scope)
|
|
|
|
|
{
|
|
|
|
|
typeOfExpression.init(document, snapshot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList operator ()(AST *ast)
|
|
|
|
|
{
|
|
|
|
|
values.clear();
|
|
|
|
|
foundCaseStatementLevel = false;
|
|
|
|
|
accept(ast);
|
|
|
|
|
return values;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool preVisit(AST *ast) {
|
|
|
|
|
if (CaseStatementAST *cs = ast->asCaseStatement()) {
|
|
|
|
|
foundCaseStatementLevel = true;
|
|
|
|
|
if (ExpressionAST *expression = cs->expression->asIdExpression()) {
|
|
|
|
|
QList<LookupItem> candidates = typeOfExpression(expression, document, scope);
|
|
|
|
|
if (!candidates .isEmpty() && candidates.first().declaration()) {
|
|
|
|
|
Symbol *decl = candidates.first().declaration();
|
|
|
|
|
values << prettyPrint.prettyName(LookupContext::fullyQualifiedName(decl));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
} else if (foundCaseStatementLevel) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Overview prettyPrint;
|
|
|
|
|
bool foundCaseStatementLevel;
|
|
|
|
|
QStringList values;
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
|
|
|
|
Document::Ptr document;
|
|
|
|
|
Scope *scope;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CompleteSwitchCaseStatementOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CompleteSwitchCaseStatementOp(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
|
|
|
|
|
int priority, CompoundStatementAST *compoundStatement, const QStringList &values)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, compoundStatement(compoundStatement)
|
|
|
|
|
, values(values)
|
|
|
|
|
{
|
|
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix",
|
|
|
|
|
"Complete Switch Statement"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
ChangeSet changes;
|
|
|
|
|
int start = currentFile->endOf(compoundStatement->lbrace_token);
|
|
|
|
|
changes.insert(start, QLatin1String("\ncase ")
|
|
|
|
|
+ values.join(QLatin1String(":\nbreak;\ncase "))
|
|
|
|
|
+ QLatin1String(":\nbreak;"));
|
|
|
|
|
currentFile->setChangeSet(changes);
|
|
|
|
|
currentFile->appendIndentRange(currentFile->range(compoundStatement));
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompoundStatementAST *compoundStatement;
|
|
|
|
|
QStringList values;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Enum *findEnum(const QList<LookupItem> &results, const LookupContext &ctxt)
|
|
|
|
|
{
|
|
|
|
|
foreach (const LookupItem &result, results) {
|
|
|
|
|
const FullySpecifiedType fst = result.type();
|
|
|
|
|
|
|
|
|
|
Type *type = result.declaration() ? result.declaration()->type().type()
|
|
|
|
|
: fst.type();
|
|
|
|
|
|
|
|
|
|
if (!type)
|
|
|
|
|
continue;
|
|
|
|
|
if (Enum *e = type->asEnumType())
|
|
|
|
|
return e;
|
|
|
|
|
if (const NamedType *namedType = type->asNamedType()) {
|
|
|
|
|
const QList<LookupItem> candidates =
|
|
|
|
|
ctxt.lookup(namedType->name(), result.scope());
|
|
|
|
|
return findEnum(candidates, ctxt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Enum *conditionEnum(const CppQuickFixInterface &interface, SwitchStatementAST *statement)
|
|
|
|
|
{
|
|
|
|
|
Block *block = statement->symbol;
|
|
|
|
|
Scope *scope = interface->semanticInfo().doc->scopeAt(block->line(), block->column());
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
|
|
|
|
typeOfExpression.init(interface->semanticInfo().doc, interface->snapshot());
|
|
|
|
|
const QList<LookupItem> results = typeOfExpression(statement->condition,
|
|
|
|
|
interface->semanticInfo().doc,
|
|
|
|
|
scope);
|
|
|
|
|
|
|
|
|
|
return findEnum(results, typeOfExpression.context());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void CompleteSwitchCaseStatement::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
|
|
|
|
|
if (path.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// look for switch statement
|
|
|
|
|
for (int depth = path.size() - 1; depth >= 0; --depth) {
|
|
|
|
|
AST *ast = path.at(depth);
|
|
|
|
|
SwitchStatementAST *switchStatement = ast->asSwitchStatement();
|
|
|
|
|
if (switchStatement) {
|
|
|
|
|
if (!interface->isCursorOn(switchStatement->switch_token) || !switchStatement->statement)
|
|
|
|
|
return;
|
|
|
|
|
CompoundStatementAST *compoundStatement = switchStatement->statement->asCompoundStatement();
|
|
|
|
|
if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;"
|
|
|
|
|
return;
|
|
|
|
|
// look if the condition's type is an enum
|
|
|
|
|
if (Enum *e = conditionEnum(interface, switchStatement)) {
|
|
|
|
|
// check the possible enum values
|
|
|
|
|
QStringList values;
|
|
|
|
|
Overview prettyPrint;
|
|
|
|
|
for (unsigned i = 0; i < e->memberCount(); ++i) {
|
|
|
|
|
if (Declaration *decl = e->memberAt(i)->asDeclaration())
|
|
|
|
|
values << prettyPrint.prettyName(LookupContext::fullyQualifiedName(decl));
|
|
|
|
|
}
|
|
|
|
|
// Get the used values
|
|
|
|
|
Block *block = switchStatement->symbol;
|
|
|
|
|
CaseStatementCollector caseValues(interface->semanticInfo().doc, interface->snapshot(),
|
|
|
|
|
interface->semanticInfo().doc->scopeAt(block->line(), block->column()));
|
|
|
|
|
QStringList usedValues = caseValues(switchStatement);
|
|
|
|
|
// save the values that would be added
|
|
|
|
|
foreach (const QString &usedValue, usedValues)
|
|
|
|
|
values.removeAll(usedValue);
|
|
|
|
|
if (!values.isEmpty())
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(new CompleteSwitchCaseStatementOp(interface, depth, compoundStatement, values)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class InsertDeclOperation: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
InsertDeclOperation(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
|
|
|
|
|
const QString &targetFileName, const Class *targetSymbol,
|
|
|
|
|
InsertionPointLocator::AccessSpec xsSpec, const QString &decl)
|
|
|
|
|
: CppQuickFixOperation(interface, 0)
|
|
|
|
|
, m_targetFileName(targetFileName)
|
|
|
|
|
, m_targetSymbol(targetSymbol)
|
|
|
|
|
, m_xsSpec(xsSpec)
|
|
|
|
|
, m_decl(decl)
|
|
|
|
|
{
|
|
|
|
|
QString type;
|
|
|
|
|
switch (xsSpec) {
|
|
|
|
|
case InsertionPointLocator::Public: type = QLatin1String("public"); break;
|
|
|
|
|
case InsertionPointLocator::Protected: type = QLatin1String("protected"); break;
|
|
|
|
|
case InsertionPointLocator::Private: type = QLatin1String("private"); break;
|
|
|
|
|
case InsertionPointLocator::PublicSlot: type = QLatin1String("public slot"); break;
|
|
|
|
|
case InsertionPointLocator::ProtectedSlot: type = QLatin1String("protected slot"); break;
|
|
|
|
|
case InsertionPointLocator::PrivateSlot: type = QLatin1String("private slot"); break;
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::InsertDeclOperation",
|
|
|
|
|
"Add %1 Declaration").arg(type));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
|
|
|
|
|
InsertionPointLocator locator(refactoring);
|
|
|
|
|
const InsertionLocation loc = locator.methodDeclarationInClass(
|
|
|
|
|
m_targetFileName, m_targetSymbol, m_xsSpec);
|
|
|
|
|
QTC_ASSERT(loc.isValid(), return);
|
|
|
|
|
|
|
|
|
|
CppRefactoringFilePtr targetFile = refactoring.file(m_targetFileName);
|
|
|
|
|
int targetPosition1 = targetFile->position(loc.line(), loc.column());
|
|
|
|
|
int targetPosition2 = qMax(0, targetFile->position(loc.line(), 1) - 1);
|
|
|
|
|
|
|
|
|
|
ChangeSet target;
|
|
|
|
|
target.insert(targetPosition1, loc.prefix() + m_decl);
|
|
|
|
|
targetFile->setChangeSet(target);
|
|
|
|
|
targetFile->appendIndentRange(ChangeSet::Range(targetPosition2, targetPosition1));
|
|
|
|
|
targetFile->setOpenEditor(true, targetPosition1);
|
|
|
|
|
targetFile->apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString generateDeclaration(Function *function);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QString m_targetFileName;
|
|
|
|
|
const Class *m_targetSymbol;
|
|
|
|
|
InsertionPointLocator::AccessSpec m_xsSpec;
|
|
|
|
|
QString m_decl;
|
|
|
|
|
};
|
|
|
|
|
|
2013-04-10 19:37:25 +04:00
|
|
|
class DeclOperationFactory
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DeclOperationFactory(const CppQuickFixInterface &interface, const QString &fileName,
|
|
|
|
|
const Class *matchingClass, const QString &decl)
|
|
|
|
|
: m_interface(interface)
|
|
|
|
|
, m_fileName(fileName)
|
|
|
|
|
, m_matchingClass(matchingClass)
|
|
|
|
|
, m_decl(decl)
|
|
|
|
|
{}
|
|
|
|
|
TextEditor::QuickFixOperation::Ptr
|
|
|
|
|
operator()(InsertionPointLocator::AccessSpec xsSpec)
|
|
|
|
|
{
|
|
|
|
|
return TextEditor::QuickFixOperation::Ptr(
|
|
|
|
|
new InsertDeclOperation(m_interface, m_fileName, m_matchingClass, xsSpec, m_decl));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const CppQuickFixInterface &m_interface;
|
|
|
|
|
const QString &m_fileName;
|
|
|
|
|
const Class *m_matchingClass;
|
|
|
|
|
const QString &m_decl;
|
|
|
|
|
};
|
|
|
|
|
|
2013-04-11 18:01:40 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void InsertDeclFromDef::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
|
|
|
|
|
FunctionDefinitionAST *funDef = 0;
|
|
|
|
|
int idx = 0;
|
|
|
|
|
for (; idx < path.size(); ++idx) {
|
|
|
|
|
AST *node = path.at(idx);
|
|
|
|
|
if (idx > 1) {
|
|
|
|
|
if (DeclaratorIdAST *declId = node->asDeclaratorId()) {
|
|
|
|
|
if (file->isCursorOn(declId)) {
|
|
|
|
|
if (FunctionDefinitionAST *candidate = path.at(idx - 2)->asFunctionDefinition()) {
|
|
|
|
|
if (funDef)
|
|
|
|
|
return;
|
|
|
|
|
funDef = candidate;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node->asClassSpecifier())
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!funDef || !funDef->symbol)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Function *fun = funDef->symbol;
|
|
|
|
|
if (Class *matchingClass = isMemberFunction(interface->context(), fun)) {
|
|
|
|
|
const QualifiedNameId *qName = fun->name()->asQualifiedNameId();
|
|
|
|
|
for (Symbol *s = matchingClass->find(qName->identifier()); s; s = s->next()) {
|
|
|
|
|
if (!s->name()
|
|
|
|
|
|| !qName->identifier()->isEqualTo(s->identifier())
|
|
|
|
|
|| !s->type()->isFunctionType())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (s->type().isEqualTo(fun->type())) {
|
|
|
|
|
// Declaration exists.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
QString fileName = QString::fromUtf8(matchingClass->fileName(),
|
|
|
|
|
matchingClass->fileNameLength());
|
|
|
|
|
const QString decl = InsertDeclOperation::generateDeclaration(fun);
|
2013-04-10 19:37:25 +04:00
|
|
|
|
|
|
|
|
// Add several possible insertion locations for declaration
|
|
|
|
|
DeclOperationFactory operation(interface, fileName, matchingClass, decl);
|
|
|
|
|
|
|
|
|
|
result.append(operation(InsertionPointLocator::Public));
|
|
|
|
|
result.append(operation(InsertionPointLocator::PublicSlot));
|
|
|
|
|
result.append(operation(InsertionPointLocator::Protected));
|
|
|
|
|
result.append(operation(InsertionPointLocator::ProtectedSlot));
|
|
|
|
|
result.append(operation(InsertionPointLocator::Private));
|
|
|
|
|
result.append(operation(InsertionPointLocator::PrivateSlot));
|
2013-04-11 18:01:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString InsertDeclOperation::generateDeclaration(Function *function)
|
|
|
|
|
{
|
|
|
|
|
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
|
|
|
oo.showFunctionSignatures = true;
|
|
|
|
|
oo.showReturnTypes = true;
|
|
|
|
|
oo.showArgumentNames = true;
|
|
|
|
|
|
|
|
|
|
QString decl;
|
|
|
|
|
decl += oo.prettyType(function->type(), function->unqualifiedName());
|
|
|
|
|
decl += QLatin1String(";\n");
|
|
|
|
|
|
|
|
|
|
return decl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class InsertDefOperation: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
InsertDefOperation(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
|
2013-05-03 11:02:30 +02:00
|
|
|
Declaration *decl, const InsertionLocation &loc, const DefPos defpos,
|
|
|
|
|
bool freeFunction = false)
|
2013-04-11 18:01:40 +02:00
|
|
|
: CppQuickFixOperation(interface, 0)
|
|
|
|
|
, m_decl(decl)
|
|
|
|
|
, m_loc(loc)
|
2013-05-03 11:02:30 +02:00
|
|
|
, m_defpos(defpos)
|
2013-04-11 18:01:40 +02:00
|
|
|
{
|
2013-05-03 11:02:30 +02:00
|
|
|
if (m_defpos == DefPosImplementationFile) {
|
|
|
|
|
const QString declFile = QString::fromUtf8(decl->fileName(), decl->fileNameLength());
|
|
|
|
|
const QDir dir = QFileInfo(declFile).dir();
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::InsertDefOperation",
|
|
|
|
|
"Add Definition in %1")
|
|
|
|
|
.arg(dir.relativeFilePath(m_loc.fileName())));
|
|
|
|
|
} else if (freeFunction) {
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::InsertDefOperation",
|
|
|
|
|
"Add Definition Here"));
|
|
|
|
|
} else if (m_defpos == DefPosInsideClass) {
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::InsertDefOperation",
|
|
|
|
|
"Add Definition Inside Class"));
|
|
|
|
|
} else if (m_defpos == DefPosOutsideClass) {
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::InsertDefOperation",
|
|
|
|
|
"Add Definition Outside Class"));
|
|
|
|
|
}
|
2013-04-11 18:01:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_loc.isValid(), return);
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr targetFile = refactoring.file(m_loc.fileName());
|
|
|
|
|
|
|
|
|
|
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
|
|
|
oo.showFunctionSignatures = true;
|
|
|
|
|
oo.showReturnTypes = true;
|
|
|
|
|
oo.showArgumentNames = true;
|
|
|
|
|
|
2013-05-03 11:02:30 +02:00
|
|
|
if (m_defpos == DefPosInsideClass) {
|
|
|
|
|
const int targetPos = targetFile->position(m_loc.line(), m_loc.column());
|
|
|
|
|
ChangeSet target;
|
|
|
|
|
target.replace(targetPos - 1, targetPos, QLatin1String(" {\n\n}")); // replace ';'
|
|
|
|
|
targetFile->setChangeSet(target);
|
|
|
|
|
targetFile->appendIndentRange(ChangeSet::Range(targetPos, targetPos + 4));
|
|
|
|
|
targetFile->setOpenEditor(true, targetPos);
|
|
|
|
|
targetFile->apply();
|
|
|
|
|
|
|
|
|
|
// Move cursor inside definition
|
|
|
|
|
QTextCursor c = targetFile->cursor();
|
|
|
|
|
c.setPosition(targetPos);
|
|
|
|
|
c.movePosition(QTextCursor::Down);
|
|
|
|
|
c.movePosition(QTextCursor::EndOfLine);
|
|
|
|
|
assistInterface()->editor()->setTextCursor(c);
|
|
|
|
|
} else {
|
|
|
|
|
// make target lookup context
|
|
|
|
|
Document::Ptr targetDoc = targetFile->cppDocument();
|
|
|
|
|
Scope *targetScope = targetDoc->scopeAt(m_loc.line(), m_loc.column());
|
|
|
|
|
LookupContext targetContext(targetDoc, assistInterface()->snapshot());
|
|
|
|
|
ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
|
|
|
|
|
if (!targetCoN)
|
|
|
|
|
targetCoN = targetContext.globalNamespace();
|
|
|
|
|
|
|
|
|
|
// setup rewriting to get minimally qualified names
|
|
|
|
|
SubstitutionEnvironment env;
|
|
|
|
|
env.setContext(assistInterface()->context());
|
|
|
|
|
env.switchScope(m_decl->enclosingScope());
|
|
|
|
|
UseMinimalNames q(targetCoN);
|
|
|
|
|
env.enter(&q);
|
|
|
|
|
Control *control = assistInterface()->context().control().data();
|
2013-04-11 18:01:40 +02:00
|
|
|
|
2013-05-03 11:02:30 +02:00
|
|
|
// rewrite the function type
|
|
|
|
|
const FullySpecifiedType tn = rewriteType(m_decl->type(), &env, control);
|
|
|
|
|
|
|
|
|
|
// rewrite the function name
|
|
|
|
|
const QString name = oo.prettyName(LookupContext::minimalName(m_decl, targetCoN,
|
|
|
|
|
control));
|
|
|
|
|
|
|
|
|
|
const QString defText = oo.prettyType(tn, name) + QLatin1String("\n{\n\n}");
|
|
|
|
|
|
|
|
|
|
const int targetPos = targetFile->position(m_loc.line(), m_loc.column());
|
|
|
|
|
const int targetPos2 = qMax(0, targetFile->position(m_loc.line(), 1) - 1);
|
|
|
|
|
|
|
|
|
|
ChangeSet target;
|
|
|
|
|
target.insert(targetPos, m_loc.prefix() + defText + m_loc.suffix());
|
|
|
|
|
targetFile->setChangeSet(target);
|
|
|
|
|
targetFile->appendIndentRange(ChangeSet::Range(targetPos2, targetPos));
|
|
|
|
|
targetFile->setOpenEditor(true, targetPos);
|
|
|
|
|
targetFile->apply();
|
|
|
|
|
|
|
|
|
|
// Move cursor inside definition
|
|
|
|
|
QTextCursor c = targetFile->cursor();
|
|
|
|
|
c.setPosition(targetPos);
|
|
|
|
|
c.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor,
|
|
|
|
|
m_loc.prefix().count(QLatin1String("\n")) + 2);
|
|
|
|
|
c.movePosition(QTextCursor::EndOfLine);
|
|
|
|
|
if (m_defpos == DefPosImplementationFile) {
|
|
|
|
|
if (BaseTextEditorWidget *editor = refactoring.editorForFile(m_loc.fileName()))
|
|
|
|
|
editor->setTextCursor(c);
|
|
|
|
|
} else {
|
|
|
|
|
assistInterface()->editor()->setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-11 18:01:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Declaration *m_decl;
|
2013-05-03 11:02:30 +02:00
|
|
|
const InsertionLocation m_loc;
|
|
|
|
|
const DefPos m_defpos;
|
2013-04-11 18:01:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
|
|
|
|
|
int idx = path.size() - 1;
|
|
|
|
|
for (; idx >= 0; --idx) {
|
|
|
|
|
AST *node = path.at(idx);
|
|
|
|
|
if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
|
|
|
|
|
if (simpleDecl->symbols && ! simpleDecl->symbols->next) {
|
|
|
|
|
if (Symbol *symbol = simpleDecl->symbols->value) {
|
|
|
|
|
if (Declaration *decl = symbol->asDeclaration()) {
|
|
|
|
|
if (Function *func = decl->type()->asFunctionType()) {
|
|
|
|
|
if (func->isSignal())
|
|
|
|
|
return;
|
2013-05-03 11:02:30 +02:00
|
|
|
|
|
|
|
|
InsertDefOperation *op = 0;
|
|
|
|
|
bool isHeaderFile = false;
|
|
|
|
|
const QString cppFileName = correspondingHeaderOrSource(
|
|
|
|
|
interface->fileName(), &isHeaderFile);
|
|
|
|
|
|
|
|
|
|
// Insert Position: Implementation File
|
|
|
|
|
if (isHeaderFile && !cppFileName.isEmpty()) {
|
|
|
|
|
CppRefactoringChanges refactoring(interface->snapshot());
|
|
|
|
|
InsertionPointLocator locator(refactoring);
|
|
|
|
|
foreach (const InsertionLocation &loc,
|
|
|
|
|
locator.methodDefinition(decl)) {
|
|
|
|
|
if (loc.isValid()) {
|
|
|
|
|
op = new InsertDefOperation(interface, decl, loc,
|
|
|
|
|
DefPosImplementationFile);
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(op));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dealing with a free function
|
|
|
|
|
const CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
unsigned line, column;
|
|
|
|
|
if (func->enclosingClass() == 0) {
|
|
|
|
|
file->lineAndColumn(file->endOf(simpleDecl), &line, &column);
|
|
|
|
|
InsertionLocation loc(interface->fileName(), QLatin1String(""),
|
|
|
|
|
QLatin1String(""), line, column);
|
|
|
|
|
op = new InsertDefOperation(interface, decl, loc,
|
|
|
|
|
DefPosInsideClass, true);
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(op));
|
|
|
|
|
return;
|
2013-04-11 18:01:40 +02:00
|
|
|
}
|
2013-05-03 11:02:30 +02:00
|
|
|
|
|
|
|
|
// Insert Position: Outside Class
|
|
|
|
|
--idx;
|
|
|
|
|
for (; idx >= 0; --idx) {
|
|
|
|
|
AST *node = path.at(idx);
|
|
|
|
|
if (ClassSpecifierAST *ast = node->asClassSpecifier()) {
|
|
|
|
|
file->lineAndColumn(file->endOf(ast), &line, &column);
|
|
|
|
|
InsertionLocation loc(interface->fileName(),
|
|
|
|
|
QLatin1String("\n\n"), QLatin1String(""),
|
|
|
|
|
line, column + 1); // include ';'
|
|
|
|
|
op = new InsertDefOperation(interface, decl, loc,
|
|
|
|
|
DefPosOutsideClass);
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(op));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Insert Position: Inside Class
|
|
|
|
|
file->lineAndColumn(file->endOf(simpleDecl), &line, &column);
|
|
|
|
|
InsertionLocation loc(interface->fileName(), QLatin1String(""),
|
|
|
|
|
QLatin1String(""), line, column);
|
|
|
|
|
op = new InsertDefOperation(interface, decl, loc, DefPosInsideClass);
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(op));
|
2013-04-11 18:01:40 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class GenerateGetterSetterOperation : public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
GenerateGetterSetterOperation(const QSharedPointer<const CppQuickFixAssistInterface> &interface)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
, m_variableName(0)
|
|
|
|
|
, m_declaratorId(0)
|
|
|
|
|
, m_declarator(0)
|
|
|
|
|
, m_variableDecl(0)
|
|
|
|
|
, m_classSpecifier(0)
|
|
|
|
|
, m_classDecl(0)
|
|
|
|
|
, m_offerQuickFix(true)
|
|
|
|
|
{
|
|
|
|
|
setDescription(QuickFixFactory::tr("Create Getter and Setter Member Functions"));
|
|
|
|
|
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
// We expect something like
|
|
|
|
|
// [0] TranslationUnitAST
|
|
|
|
|
// [1] NamespaceAST
|
|
|
|
|
// [2] LinkageBodyAST
|
|
|
|
|
// [3] SimpleDeclarationAST
|
|
|
|
|
// [4] ClassSpecifierAST
|
|
|
|
|
// [5] SimpleDeclarationAST
|
|
|
|
|
// [6] DeclaratorAST
|
|
|
|
|
// [7] DeclaratorIdAST
|
|
|
|
|
// [8] SimpleNameAST
|
|
|
|
|
|
|
|
|
|
const int n = path.size();
|
|
|
|
|
if (n < 6)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int i = 1;
|
|
|
|
|
m_variableName = path.at(n - i++)->asSimpleName();
|
|
|
|
|
m_declaratorId = path.at(n - i++)->asDeclaratorId();
|
|
|
|
|
// DeclaratorAST might be preceded by PointerAST, e.g. for the case
|
|
|
|
|
// "class C { char *@s; };", where '@' denotes the text cursor position.
|
|
|
|
|
if (!(m_declarator = path.at(n - i++)->asDeclarator())) {
|
|
|
|
|
--i;
|
|
|
|
|
if (path.at(n - i++)->asPointer()) {
|
|
|
|
|
if (n < 7)
|
|
|
|
|
return;
|
|
|
|
|
m_declarator = path.at(n - i++)->asDeclarator();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_variableDecl = path.at(n - i++)->asSimpleDeclaration();
|
|
|
|
|
m_classSpecifier = path.at(n - i++)->asClassSpecifier();
|
|
|
|
|
m_classDecl = path.at(n - i++)->asSimpleDeclaration();
|
|
|
|
|
|
|
|
|
|
if (!isValid())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Do not get triggered on member functions and arrays
|
|
|
|
|
if (m_declarator->postfix_declarator_list) {
|
|
|
|
|
m_offerQuickFix = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Construct getter and setter names
|
|
|
|
|
const Name *variableName = m_variableName->name;
|
|
|
|
|
if (!variableName) {
|
|
|
|
|
m_offerQuickFix = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const Identifier *variableId = variableName->identifier();
|
|
|
|
|
if (!variableId) {
|
|
|
|
|
m_offerQuickFix = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_variableString = QString::fromLatin1(variableId->chars(), variableId->size());
|
|
|
|
|
|
|
|
|
|
m_baseName = m_variableString;
|
|
|
|
|
if (m_baseName.startsWith(QLatin1String("m_")))
|
|
|
|
|
m_baseName.remove(0, 2);
|
|
|
|
|
else if (m_baseName.startsWith(QLatin1Char('_')))
|
|
|
|
|
m_baseName.remove(0, 1);
|
|
|
|
|
else if (m_baseName.endsWith(QLatin1Char('_')))
|
|
|
|
|
m_baseName.chop(1);
|
|
|
|
|
|
|
|
|
|
m_getterName = m_baseName != m_variableString
|
|
|
|
|
? QString::fromLatin1("%1").arg(m_baseName)
|
|
|
|
|
: QString::fromLatin1("get%1%2")
|
|
|
|
|
.arg(m_baseName.left(1).toUpper()).arg(m_baseName.mid(1));
|
|
|
|
|
m_setterName = QString::fromLatin1("set%1%2")
|
|
|
|
|
.arg(m_baseName.left(1).toUpper()).arg(m_baseName.mid(1));
|
|
|
|
|
|
|
|
|
|
// Check if the class has already a getter or setter.
|
|
|
|
|
// This is only a simple check which should suffice not triggering the
|
|
|
|
|
// same quick fix again. Limitations:
|
|
|
|
|
// 1) It only checks in the current class, but not in base classes.
|
|
|
|
|
// 2) It compares only names instead of types/signatures.
|
|
|
|
|
if (Class *klass = m_classSpecifier->symbol) {
|
|
|
|
|
for (unsigned i = 0; i < klass->memberCount(); ++i) {
|
|
|
|
|
Symbol *symbol = klass->memberAt(i);
|
|
|
|
|
if (const Name *symbolName = symbol->name()) {
|
|
|
|
|
if (const Identifier *id = symbolName->identifier()) {
|
|
|
|
|
const QString memberName = QString::fromLatin1(id->chars(), id->size());
|
|
|
|
|
if (memberName == m_getterName || memberName == m_setterName) {
|
|
|
|
|
m_offerQuickFix = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // for
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isValid() const
|
|
|
|
|
{
|
|
|
|
|
return m_variableName
|
|
|
|
|
&& m_declaratorId
|
|
|
|
|
&& m_declarator
|
|
|
|
|
&& m_variableDecl
|
|
|
|
|
&& m_classSpecifier
|
|
|
|
|
&& m_classDecl
|
|
|
|
|
&& m_offerQuickFix;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
const List<Symbol *> *symbols = m_variableDecl->symbols;
|
|
|
|
|
QTC_ASSERT(symbols, return);
|
|
|
|
|
|
|
|
|
|
// Find the right symbol in the simple declaration
|
|
|
|
|
Symbol *symbol = 0;
|
|
|
|
|
for (; symbols; symbols = symbols->next) {
|
|
|
|
|
Symbol *s = symbols->value;
|
|
|
|
|
if (const Name *name = s->name()) {
|
|
|
|
|
if (const Identifier *id = name->identifier()) {
|
|
|
|
|
const QString symbolName = QString::fromLatin1(id->chars(), id->size());
|
|
|
|
|
if (symbolName == m_variableString) {
|
|
|
|
|
symbol = s;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTC_ASSERT(symbol, return);
|
|
|
|
|
FullySpecifiedType fullySpecifiedType = symbol->type();
|
|
|
|
|
Type *type = fullySpecifiedType.type();
|
|
|
|
|
QTC_ASSERT(type, return);
|
|
|
|
|
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
|
|
|
oo.showFunctionSignatures = true;
|
|
|
|
|
oo.showReturnTypes = true;
|
|
|
|
|
oo.showArgumentNames = true;
|
|
|
|
|
const QString typeString = oo.prettyType(fullySpecifiedType);
|
|
|
|
|
|
|
|
|
|
const NameAST *classNameAST = m_classSpecifier->name;
|
|
|
|
|
QTC_ASSERT(classNameAST, return);
|
|
|
|
|
const Name *className = classNameAST->name;
|
|
|
|
|
QTC_ASSERT(className, return);
|
|
|
|
|
const Identifier *classId = className->identifier();
|
|
|
|
|
QTC_ASSERT(classId, return);
|
|
|
|
|
QString classString = QString::fromLatin1(classId->chars(), classId->size());
|
|
|
|
|
|
|
|
|
|
bool wasHeader = true;
|
|
|
|
|
QString declFileName = currentFile->fileName();
|
|
|
|
|
QString implFileName = correspondingHeaderOrSource(declFileName, &wasHeader);
|
|
|
|
|
const bool sameFile = !wasHeader || !QFile::exists(implFileName);
|
|
|
|
|
if (sameFile)
|
|
|
|
|
implFileName = declFileName;
|
|
|
|
|
|
|
|
|
|
InsertionPointLocator locator(refactoring);
|
|
|
|
|
InsertionLocation declLocation = locator.methodDeclarationInClass
|
|
|
|
|
(declFileName, m_classSpecifier->symbol->asClass(), InsertionPointLocator::Public);
|
|
|
|
|
|
|
|
|
|
const bool passByValue = type->isIntegerType() || type->isFloatType()
|
|
|
|
|
|| type->isPointerType() || type->isEnumType();
|
|
|
|
|
const QString paramName = m_baseName != m_variableString
|
|
|
|
|
? m_baseName : QLatin1String("value");
|
|
|
|
|
QString paramString;
|
|
|
|
|
if (passByValue) {
|
|
|
|
|
paramString = oo.prettyType(fullySpecifiedType, paramName);
|
|
|
|
|
} else {
|
|
|
|
|
FullySpecifiedType constParamType(fullySpecifiedType);
|
|
|
|
|
constParamType.setConst(true);
|
|
|
|
|
QScopedPointer<ReferenceType> referenceType(new ReferenceType(constParamType, false));
|
|
|
|
|
FullySpecifiedType referenceToConstParamType(referenceType.data());
|
|
|
|
|
paramString = oo.prettyType(referenceToConstParamType, paramName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool isStatic = symbol->storage() == Symbol::Static;
|
|
|
|
|
|
|
|
|
|
// Construct declaration strings
|
|
|
|
|
QString declaration = declLocation.prefix();
|
|
|
|
|
QString getterTypeString = typeString;
|
|
|
|
|
FullySpecifiedType getterType(fullySpecifiedType);
|
|
|
|
|
if (fullySpecifiedType.isConst()) {
|
|
|
|
|
getterType.setConst(false);
|
|
|
|
|
getterTypeString = oo.prettyType(getterType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString declarationGetterTypeAndNameString = oo.prettyType(getterType, m_getterName);
|
|
|
|
|
const QString declarationGetter = QString::fromLatin1("%1%2()%3;\n")
|
|
|
|
|
.arg(isStatic ? QLatin1String("static ") : QString())
|
|
|
|
|
.arg(declarationGetterTypeAndNameString)
|
|
|
|
|
.arg(isStatic ? QString() : QLatin1String(" const"));
|
|
|
|
|
const QString declarationSetter = QString::fromLatin1("%1void %2(%3);\n")
|
|
|
|
|
.arg(isStatic ? QLatin1String("static ") : QString())
|
|
|
|
|
.arg(m_setterName)
|
|
|
|
|
.arg(paramString);
|
|
|
|
|
|
|
|
|
|
declaration += declarationGetter;
|
|
|
|
|
if (!fullySpecifiedType.isConst())
|
|
|
|
|
declaration += declarationSetter;
|
|
|
|
|
declaration += declLocation.suffix();
|
|
|
|
|
|
|
|
|
|
// Construct implementation strings
|
|
|
|
|
const QString implementationGetterTypeAndNameString = oo.prettyType(
|
|
|
|
|
getterType, QString::fromLatin1("%1::%2").arg(classString, m_getterName));
|
|
|
|
|
const QString implementationGetter = QString::fromLatin1(
|
|
|
|
|
"\n%1()%2\n"
|
|
|
|
|
"{\n"
|
|
|
|
|
"return %3;\n"
|
|
|
|
|
"}\n")
|
|
|
|
|
.arg(implementationGetterTypeAndNameString)
|
|
|
|
|
.arg(isStatic ? QString() : QLatin1String(" const"))
|
|
|
|
|
.arg(m_variableString);
|
|
|
|
|
const QString implementationSetter = QString::fromLatin1(
|
|
|
|
|
"\nvoid %1::%2(%3)\n"
|
|
|
|
|
"{\n"
|
|
|
|
|
"%4 = %5;\n"
|
|
|
|
|
"}\n")
|
|
|
|
|
.arg(classString).arg(m_setterName)
|
|
|
|
|
.arg(paramString).arg(m_variableString)
|
|
|
|
|
.arg(paramName);
|
|
|
|
|
QString implementation = implementationGetter;
|
|
|
|
|
if (!fullySpecifiedType.isConst())
|
|
|
|
|
implementation += implementationSetter;
|
|
|
|
|
|
|
|
|
|
// Create and apply changes
|
|
|
|
|
ChangeSet currChanges;
|
|
|
|
|
int declInsertPos = currentFile->position(qMax(1u, declLocation.line()),
|
|
|
|
|
declLocation.column());
|
|
|
|
|
currChanges.insert(declInsertPos, declaration);
|
|
|
|
|
|
|
|
|
|
if (sameFile) {
|
|
|
|
|
const int pos = currentFile->endOf(m_classDecl) + 1;
|
|
|
|
|
unsigned line, column;
|
|
|
|
|
currentFile->lineAndColumn(pos, &line, &column);
|
|
|
|
|
const int insertPos = currentFile->position(line + 1, 1) - 1;
|
|
|
|
|
currChanges.insert(insertPos < 0 ? pos : insertPos, implementation);
|
|
|
|
|
} else {
|
|
|
|
|
CppRefactoringChanges implRefactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr implFile = implRefactoring.file(implFileName);
|
|
|
|
|
ChangeSet implChanges;
|
|
|
|
|
const int implInsertPos = implFile->document()->characterCount() - 1;
|
|
|
|
|
implChanges.insert(implInsertPos, implementation);
|
|
|
|
|
implFile->setChangeSet(implChanges);
|
|
|
|
|
implFile->appendIndentRange(
|
|
|
|
|
ChangeSet::Range(implInsertPos, implInsertPos + implementation.size()));
|
|
|
|
|
implFile->apply();
|
|
|
|
|
}
|
|
|
|
|
currentFile->setChangeSet(currChanges);
|
|
|
|
|
currentFile->appendIndentRange(
|
|
|
|
|
ChangeSet::Range(declInsertPos, declInsertPos + declaration.size()));
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SimpleNameAST *m_variableName;
|
|
|
|
|
DeclaratorIdAST *m_declaratorId;
|
|
|
|
|
DeclaratorAST *m_declarator;
|
|
|
|
|
SimpleDeclarationAST *m_variableDecl;
|
|
|
|
|
ClassSpecifierAST *m_classSpecifier;
|
|
|
|
|
SimpleDeclarationAST *m_classDecl;
|
|
|
|
|
|
|
|
|
|
QString m_baseName;
|
|
|
|
|
QString m_getterName;
|
|
|
|
|
QString m_setterName;
|
|
|
|
|
QString m_variableString;
|
|
|
|
|
bool m_offerQuickFix;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void GenerateGetterSetter::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
GenerateGetterSetterOperation *op = new GenerateGetterSetterOperation(interface);
|
|
|
|
|
if (op->isValid())
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(op));
|
|
|
|
|
else
|
|
|
|
|
delete op;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class ExtractFunctionOperation : public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ExtractFunctionOperation(const CppQuickFixInterface &interface,
|
|
|
|
|
int extractionStart,
|
|
|
|
|
int extractionEnd,
|
|
|
|
|
FunctionDefinitionAST *refFuncDef,
|
|
|
|
|
Symbol *funcReturn,
|
|
|
|
|
QList<QPair<QString, QString> > relevantDecls)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
, m_extractionStart(extractionStart)
|
|
|
|
|
, m_extractionEnd(extractionEnd)
|
|
|
|
|
, m_refFuncDef(refFuncDef)
|
|
|
|
|
, m_funcReturn(funcReturn)
|
|
|
|
|
, m_relevantDecls(relevantDecls)
|
|
|
|
|
{
|
|
|
|
|
setDescription(QCoreApplication::translate("QuickFix::ExtractFunction", "Extract Function"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!m_funcReturn || !m_relevantDecls.isEmpty(), return);
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr currentFile = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
const QString &funcName = getFunctionName();
|
|
|
|
|
if (funcName.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Function *refFunc = m_refFuncDef->symbol;
|
|
|
|
|
|
|
|
|
|
// We don't need to rewrite the type for declarations made inside the reference function,
|
|
|
|
|
// since their scope will remain the same. Then we preserve the original spelling style.
|
|
|
|
|
// However, we must do so for the return type in the definition.
|
|
|
|
|
SubstitutionEnvironment env;
|
|
|
|
|
env.setContext(assistInterface()->context());
|
|
|
|
|
env.switchScope(refFunc);
|
|
|
|
|
ClassOrNamespace *targetCoN =
|
|
|
|
|
assistInterface()->context().lookupType(refFunc->enclosingScope());
|
|
|
|
|
if (!targetCoN)
|
|
|
|
|
targetCoN = assistInterface()->context().globalNamespace();
|
|
|
|
|
UseMinimalNames subs(targetCoN);
|
|
|
|
|
env.enter(&subs);
|
|
|
|
|
|
|
|
|
|
Overview printer = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
|
|
|
Control *control = assistInterface()->context().control().data();
|
|
|
|
|
QString funcDef;
|
|
|
|
|
QString funcDecl; // We generate a declaration only in the case of a member function.
|
|
|
|
|
QString funcCall;
|
|
|
|
|
|
|
|
|
|
Class *matchingClass = isMemberFunction(assistInterface()->context(), refFunc);
|
|
|
|
|
|
|
|
|
|
// Write return type.
|
|
|
|
|
if (!m_funcReturn) {
|
|
|
|
|
funcDef.append(QLatin1String("void "));
|
|
|
|
|
if (matchingClass)
|
|
|
|
|
funcDecl.append(QLatin1String("void "));
|
|
|
|
|
} else {
|
|
|
|
|
const FullySpecifiedType &fullType = rewriteType(m_funcReturn->type(), &env, control);
|
|
|
|
|
funcDef.append(printer.prettyType(fullType) + QLatin1Char(' '));
|
|
|
|
|
funcDecl.append(printer.prettyType(m_funcReturn->type()) + QLatin1Char(' '));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write class qualification, if any.
|
|
|
|
|
if (matchingClass) {
|
|
|
|
|
const Name *name = rewriteName(matchingClass->name(), &env, control);
|
|
|
|
|
funcDef.append(printer.prettyName(name));
|
|
|
|
|
funcDef.append(QLatin1String("::"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write the extracted function itself and its call.
|
|
|
|
|
funcDef.append(funcName);
|
|
|
|
|
if (matchingClass)
|
|
|
|
|
funcDecl.append(funcName);
|
|
|
|
|
funcCall.append(funcName);
|
|
|
|
|
funcDef.append(QLatin1Char('('));
|
|
|
|
|
if (matchingClass)
|
|
|
|
|
funcDecl.append(QLatin1Char('('));
|
|
|
|
|
funcCall.append(QLatin1Char('('));
|
|
|
|
|
for (int i = m_funcReturn ? 1 : 0; i < m_relevantDecls.length(); ++i) {
|
|
|
|
|
QPair<QString, QString> p = m_relevantDecls.at(i);
|
|
|
|
|
funcCall.append(p.first);
|
|
|
|
|
funcDef.append(p.second);
|
|
|
|
|
if (matchingClass)
|
|
|
|
|
funcDecl.append(p.second);
|
|
|
|
|
if (i < m_relevantDecls.length() - 1) {
|
|
|
|
|
funcCall.append(QLatin1String(", "));
|
|
|
|
|
funcDef.append(QLatin1String(", "));
|
|
|
|
|
if (matchingClass)
|
|
|
|
|
funcDecl.append(QLatin1String(", "));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
funcDef.append(QLatin1Char(')'));
|
|
|
|
|
if (matchingClass)
|
|
|
|
|
funcDecl.append(QLatin1Char(')'));
|
|
|
|
|
funcCall.append(QLatin1Char(')'));
|
|
|
|
|
if (refFunc->isConst()) {
|
|
|
|
|
funcDef.append(QLatin1String(" const"));
|
|
|
|
|
funcDecl.append(QLatin1String(" const"));
|
|
|
|
|
}
|
|
|
|
|
funcDef.append(QLatin1String("\n{\n"));
|
|
|
|
|
if (matchingClass)
|
|
|
|
|
funcDecl.append(QLatin1String(";\n"));
|
|
|
|
|
if (m_funcReturn) {
|
|
|
|
|
funcDef.append(QLatin1String("\nreturn ")
|
2013-04-16 09:39:13 +02:00
|
|
|
+ m_relevantDecls.at(0).first
|
|
|
|
|
+ QLatin1String(";"));
|
|
|
|
|
funcCall.prepend(m_relevantDecls.at(0).second + QLatin1String(" = "));
|
2013-04-11 18:01:40 +02:00
|
|
|
}
|
|
|
|
|
funcDef.append(QLatin1String("\n}\n\n"));
|
|
|
|
|
funcDef.replace(QChar::ParagraphSeparator, QLatin1String("\n"));
|
|
|
|
|
funcCall.append(QLatin1Char(';'));
|
|
|
|
|
|
|
|
|
|
// Get starting indentation from original code.
|
|
|
|
|
int indentedExtractionStart = m_extractionStart;
|
|
|
|
|
QChar current = currentFile->document()->characterAt(indentedExtractionStart - 1);
|
|
|
|
|
while (current == QLatin1Char(' ') || current == QLatin1Char('\t')) {
|
|
|
|
|
--indentedExtractionStart;
|
|
|
|
|
current = currentFile->document()->characterAt(indentedExtractionStart - 1);
|
|
|
|
|
}
|
|
|
|
|
QString extract = currentFile->textOf(indentedExtractionStart, m_extractionEnd);
|
|
|
|
|
extract.replace(QChar::ParagraphSeparator, QLatin1String("\n"));
|
|
|
|
|
if (!extract.endsWith(QLatin1Char('\n')) && m_funcReturn)
|
|
|
|
|
extract.append(QLatin1Char('\n'));
|
|
|
|
|
|
|
|
|
|
// Since we need an indent range and a nested reindent range (based on the original
|
|
|
|
|
// formatting) it's simpler to have two different change sets.
|
|
|
|
|
ChangeSet change;
|
|
|
|
|
int position = currentFile->startOf(m_refFuncDef);
|
|
|
|
|
change.insert(position, funcDef);
|
|
|
|
|
change.replace(m_extractionStart, m_extractionEnd, funcCall);
|
|
|
|
|
currentFile->setChangeSet(change);
|
|
|
|
|
currentFile->appendIndentRange(ChangeSet::Range(position, position + 1));
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
|
|
|
|
|
QTextCursor tc = currentFile->document()->find(QLatin1String("{"), position);
|
|
|
|
|
QTC_ASSERT(tc.hasSelection(), return);
|
|
|
|
|
position = tc.selectionEnd() + 1;
|
|
|
|
|
change.clear();
|
|
|
|
|
change.insert(position, extract);
|
|
|
|
|
currentFile->setChangeSet(change);
|
|
|
|
|
currentFile->appendReindentRange(ChangeSet::Range(position, position + 1));
|
|
|
|
|
currentFile->apply();
|
|
|
|
|
|
|
|
|
|
// Write declaration, if necessary.
|
|
|
|
|
if (matchingClass) {
|
|
|
|
|
InsertionPointLocator locator(refactoring);
|
|
|
|
|
const QString fileName = QLatin1String(matchingClass->fileName());
|
|
|
|
|
const InsertionLocation &location =
|
|
|
|
|
locator.methodDeclarationInClass(fileName, matchingClass,
|
|
|
|
|
InsertionPointLocator::Public);
|
|
|
|
|
CppRefactoringFilePtr declFile = refactoring.file(fileName);
|
|
|
|
|
change.clear();
|
|
|
|
|
position = declFile->position(location.line(), location.column());
|
|
|
|
|
change.insert(position, funcDecl);
|
|
|
|
|
declFile->setChangeSet(change);
|
|
|
|
|
declFile->appendIndentRange(ChangeSet::Range(position, position + 1));
|
|
|
|
|
declFile->apply();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString getFunctionName() const
|
|
|
|
|
{
|
|
|
|
|
bool ok;
|
|
|
|
|
QString name =
|
|
|
|
|
QInputDialog::getText(0,
|
|
|
|
|
QCoreApplication::translate("QuickFix::ExtractFunction",
|
|
|
|
|
"Extract Function Refactoring"),
|
|
|
|
|
QCoreApplication::translate("QuickFix::ExtractFunction",
|
|
|
|
|
"Enter function name"),
|
|
|
|
|
QLineEdit::Normal,
|
|
|
|
|
QString(),
|
|
|
|
|
&ok);
|
|
|
|
|
name = name.trimmed();
|
|
|
|
|
if (!ok || name.isEmpty())
|
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
|
|
if (!isValidIdentifier(name)) {
|
|
|
|
|
QMessageBox::critical(0,
|
|
|
|
|
QCoreApplication::translate("QuickFix::ExtractFunction",
|
|
|
|
|
"Extract Function Refactoring"),
|
|
|
|
|
QCoreApplication::translate("QuickFix::ExtractFunction",
|
|
|
|
|
"Invalid function name"));
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int m_extractionStart;
|
|
|
|
|
int m_extractionEnd;
|
|
|
|
|
FunctionDefinitionAST *m_refFuncDef;
|
|
|
|
|
Symbol *m_funcReturn;
|
|
|
|
|
QList<QPair<QString, QString> > m_relevantDecls;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
QPair<QString, QString> assembleDeclarationData(const QString &specifiers, DeclaratorAST *decltr,
|
|
|
|
|
const CppRefactoringFilePtr &file,
|
|
|
|
|
const Overview &printer)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(decltr, return (QPair<QString, QString>()));
|
|
|
|
|
if (decltr->core_declarator
|
|
|
|
|
&& decltr->core_declarator->asDeclaratorId()
|
|
|
|
|
&& decltr->core_declarator->asDeclaratorId()->name) {
|
|
|
|
|
QString decltrText = file->textOf(file->startOf(decltr),
|
|
|
|
|
file->endOf(decltr->core_declarator));
|
|
|
|
|
if (!decltrText.isEmpty()) {
|
|
|
|
|
const QString &name = printer.prettyName(
|
|
|
|
|
decltr->core_declarator->asDeclaratorId()->name->name);
|
|
|
|
|
QString completeDecl = specifiers;
|
|
|
|
|
if (!decltrText.contains(QLatin1Char(' ')))
|
|
|
|
|
completeDecl.append(QLatin1Char(' ') + decltrText);
|
|
|
|
|
else
|
|
|
|
|
completeDecl.append(decltrText);
|
|
|
|
|
return qMakePair(name, completeDecl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QPair<QString, QString>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class FunctionExtractionAnalyser : public ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FunctionExtractionAnalyser(TranslationUnit *unit,
|
|
|
|
|
const int selStart,
|
|
|
|
|
const int selEnd,
|
|
|
|
|
const CppRefactoringFilePtr &file,
|
|
|
|
|
const Overview &printer)
|
|
|
|
|
: ASTVisitor(unit)
|
|
|
|
|
, m_done(false)
|
|
|
|
|
, m_failed(false)
|
|
|
|
|
, m_selStart(selStart)
|
|
|
|
|
, m_selEnd(selEnd)
|
|
|
|
|
, m_extractionStart(0)
|
|
|
|
|
, m_extractionEnd(0)
|
|
|
|
|
, m_file(file)
|
|
|
|
|
, m_printer(printer)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
bool operator()(FunctionDefinitionAST *refFunDef)
|
|
|
|
|
{
|
|
|
|
|
accept(refFunDef);
|
|
|
|
|
|
|
|
|
|
if (!m_failed && m_extractionStart == m_extractionEnd)
|
|
|
|
|
m_failed = true;
|
|
|
|
|
|
|
|
|
|
return !m_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool preVisit(AST *)
|
|
|
|
|
{
|
|
|
|
|
if (m_done)
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void statement(StatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
if (!stmt)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const int stmtStart = m_file->startOf(stmt);
|
|
|
|
|
const int stmtEnd = m_file->endOf(stmt);
|
|
|
|
|
|
|
|
|
|
if (stmtStart >= m_selEnd
|
|
|
|
|
|| (m_extractionStart && stmtEnd > m_selEnd)) {
|
|
|
|
|
m_done = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stmtStart >= m_selStart && !m_extractionStart)
|
|
|
|
|
m_extractionStart = stmtStart;
|
|
|
|
|
if (stmtEnd > m_extractionEnd && m_extractionStart)
|
|
|
|
|
m_extractionEnd = stmtEnd;
|
|
|
|
|
|
|
|
|
|
accept(stmt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(CaseStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
statement(stmt->statement);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(CompoundStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
for (StatementListAST *it = stmt->statement_list; it; it = it->next) {
|
|
|
|
|
statement(it->value);
|
|
|
|
|
if (m_done)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(DoStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
statement(stmt->statement);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(ForeachStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
statement(stmt->statement);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(RangeBasedForStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
statement(stmt->statement);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(ForStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
statement(stmt->initializer);
|
|
|
|
|
if (!m_done)
|
|
|
|
|
statement(stmt->statement);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(IfStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
statement(stmt->statement);
|
|
|
|
|
if (!m_done)
|
|
|
|
|
statement(stmt->else_statement);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(TryBlockStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
statement(stmt->statement);
|
|
|
|
|
for (CatchClauseListAST *it = stmt->catch_clause_list; it; it = it->next) {
|
|
|
|
|
statement(it->value);
|
|
|
|
|
if (m_done)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(WhileStatementAST *stmt)
|
|
|
|
|
{
|
|
|
|
|
statement(stmt->statement);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(DeclarationStatementAST *declStmt)
|
|
|
|
|
{
|
|
|
|
|
// We need to collect the declarations we see before the extraction or even inside it.
|
|
|
|
|
// They might need to be used as either a parameter or return value. Actually, we could
|
|
|
|
|
// still obtain their types from the local uses, but it's good to preserve the original
|
|
|
|
|
// typing style.
|
|
|
|
|
if (declStmt
|
|
|
|
|
&& declStmt->declaration
|
|
|
|
|
&& declStmt->declaration->asSimpleDeclaration()) {
|
|
|
|
|
SimpleDeclarationAST *simpleDecl = declStmt->declaration->asSimpleDeclaration();
|
|
|
|
|
if (simpleDecl->decl_specifier_list
|
|
|
|
|
&& simpleDecl->declarator_list) {
|
|
|
|
|
const QString &specifiers =
|
|
|
|
|
m_file->textOf(m_file->startOf(simpleDecl),
|
|
|
|
|
m_file->endOf(simpleDecl->decl_specifier_list->lastValue()));
|
|
|
|
|
for (DeclaratorListAST *decltrList = simpleDecl->declarator_list;
|
|
|
|
|
decltrList;
|
|
|
|
|
decltrList = decltrList->next) {
|
|
|
|
|
const QPair<QString, QString> p =
|
|
|
|
|
assembleDeclarationData(specifiers, decltrList->value, m_file, m_printer);
|
|
|
|
|
if (!p.first.isEmpty())
|
|
|
|
|
m_knownDecls.insert(p.first, p.second);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool visit(ReturnStatementAST *)
|
|
|
|
|
{
|
|
|
|
|
if (m_extractionStart) {
|
|
|
|
|
m_done = true;
|
|
|
|
|
m_failed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool m_done;
|
|
|
|
|
bool m_failed;
|
|
|
|
|
const int m_selStart;
|
|
|
|
|
const int m_selEnd;
|
|
|
|
|
int m_extractionStart;
|
|
|
|
|
int m_extractionEnd;
|
|
|
|
|
QHash<QString, QString> m_knownDecls;
|
|
|
|
|
CppRefactoringFilePtr m_file;
|
|
|
|
|
const Overview &m_printer;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void ExtractFunction::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor = file->cursor();
|
|
|
|
|
if (!cursor.hasSelection())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
FunctionDefinitionAST *refFuncDef = 0; // The "reference" function, which we will extract from.
|
|
|
|
|
for (int i = path.size() - 1; i >= 0; --i) {
|
|
|
|
|
refFuncDef = path.at(i)->asFunctionDefinition();
|
|
|
|
|
if (refFuncDef)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!refFuncDef
|
|
|
|
|
|| !refFuncDef->function_body
|
|
|
|
|
|| !refFuncDef->function_body->asCompoundStatement()
|
|
|
|
|
|| !refFuncDef->function_body->asCompoundStatement()->statement_list
|
|
|
|
|
|| !refFuncDef->symbol
|
|
|
|
|
|| !refFuncDef->symbol->name()
|
|
|
|
|
|| refFuncDef->symbol->enclosingScope()->isTemplate() /* TODO: Templates... */) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Adjust selection ends.
|
|
|
|
|
int selStart = cursor.selectionStart();
|
|
|
|
|
int selEnd = cursor.selectionEnd();
|
|
|
|
|
if (selStart > selEnd)
|
|
|
|
|
qSwap(selStart, selEnd);
|
|
|
|
|
|
|
|
|
|
Overview printer;
|
|
|
|
|
|
|
|
|
|
// Analyze the content to be extracted, which consists of determining the statements
|
|
|
|
|
// which are complete and collecting the declarations seen.
|
|
|
|
|
FunctionExtractionAnalyser analyser(interface->semanticInfo().doc->translationUnit(),
|
|
|
|
|
selStart, selEnd,
|
|
|
|
|
file,
|
|
|
|
|
printer);
|
|
|
|
|
if (!analyser(refFuncDef))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// We also need to collect the declarations of the parameters from the reference function.
|
|
|
|
|
QSet<QString> refFuncParams;
|
|
|
|
|
if (refFuncDef->declarator->postfix_declarator_list
|
|
|
|
|
&& refFuncDef->declarator->postfix_declarator_list->value
|
|
|
|
|
&& refFuncDef->declarator->postfix_declarator_list->value->asFunctionDeclarator()) {
|
|
|
|
|
FunctionDeclaratorAST *funcDecltr =
|
|
|
|
|
refFuncDef->declarator->postfix_declarator_list->value->asFunctionDeclarator();
|
|
|
|
|
if (funcDecltr->parameter_declaration_clause
|
|
|
|
|
&& funcDecltr->parameter_declaration_clause->parameter_declaration_list) {
|
|
|
|
|
for (ParameterDeclarationListAST *it =
|
|
|
|
|
funcDecltr->parameter_declaration_clause->parameter_declaration_list;
|
|
|
|
|
it;
|
|
|
|
|
it = it->next) {
|
|
|
|
|
ParameterDeclarationAST *paramDecl = it->value->asParameterDeclaration();
|
|
|
|
|
if (paramDecl->declarator) {
|
|
|
|
|
const QString &specifiers =
|
|
|
|
|
file->textOf(file->startOf(paramDecl),
|
|
|
|
|
file->endOf(paramDecl->type_specifier_list->lastValue()));
|
|
|
|
|
const QPair<QString, QString> &p =
|
|
|
|
|
assembleDeclarationData(specifiers, paramDecl->declarator,
|
|
|
|
|
file, printer);
|
|
|
|
|
if (!p.first.isEmpty()) {
|
|
|
|
|
analyser.m_knownDecls.insert(p.first, p.second);
|
|
|
|
|
refFuncParams.insert(p.first);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Identify what would be parameters for the new function and its return value, if any.
|
|
|
|
|
Symbol *funcReturn = 0;
|
|
|
|
|
QList<QPair<QString, QString> > relevantDecls;
|
|
|
|
|
SemanticInfo::LocalUseIterator it(interface->semanticInfo().localUses);
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
|
|
|
|
|
|
|
|
|
bool usedBeforeExtraction = false;
|
|
|
|
|
bool usedAfterExtraction = false;
|
|
|
|
|
bool usedInsideExtraction = false;
|
|
|
|
|
const QList<SemanticInfo::Use> &uses = it.value();
|
|
|
|
|
foreach (const SemanticInfo::Use &use, uses) {
|
|
|
|
|
if (use.isInvalid())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const int position = file->position(use.line, use.column);
|
|
|
|
|
if (position < analyser.m_extractionStart)
|
|
|
|
|
usedBeforeExtraction = true;
|
|
|
|
|
else if (position >= analyser.m_extractionEnd)
|
|
|
|
|
usedAfterExtraction = true;
|
|
|
|
|
else
|
|
|
|
|
usedInsideExtraction = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString &name = printer.prettyName(it.key()->name());
|
|
|
|
|
|
|
|
|
|
if ((usedBeforeExtraction && usedInsideExtraction)
|
|
|
|
|
|| (usedInsideExtraction && refFuncParams.contains(name))) {
|
|
|
|
|
QTC_ASSERT(analyser.m_knownDecls.contains(name), return);
|
|
|
|
|
relevantDecls.append(qMakePair(name, analyser.m_knownDecls.value(name)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We assume that the first use of a local corresponds to its declaration.
|
|
|
|
|
if (usedInsideExtraction && usedAfterExtraction && !usedBeforeExtraction) {
|
|
|
|
|
if (!funcReturn) {
|
|
|
|
|
QTC_ASSERT(analyser.m_knownDecls.contains(name), return);
|
|
|
|
|
// The return, if any, is stored as the first item in the list.
|
|
|
|
|
relevantDecls.prepend(qMakePair(name, analyser.m_knownDecls.value(name)));
|
|
|
|
|
funcReturn = it.key();
|
|
|
|
|
} else {
|
|
|
|
|
// Would require multiple returns. (Unless we do fancy things, as pointed below.)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The current implementation doesn't try to be too smart since it preserves the original form
|
|
|
|
|
// of the declarations. This might be or not the desired effect. An improvement would be to
|
|
|
|
|
// let the user somehow customize the function interface.
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(new ExtractFunctionOperation(interface,
|
|
|
|
|
analyser.m_extractionStart,
|
|
|
|
|
analyser.m_extractionEnd,
|
|
|
|
|
refFuncDef,
|
|
|
|
|
funcReturn,
|
|
|
|
|
relevantDecls)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class InsertQtPropertyMembersOp: public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
enum GenerateFlag {
|
|
|
|
|
GenerateGetter = 1 << 0,
|
|
|
|
|
GenerateSetter = 1 << 1,
|
|
|
|
|
GenerateSignal = 1 << 2,
|
|
|
|
|
GenerateStorage = 1 << 3
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
InsertQtPropertyMembersOp(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
|
|
|
|
|
int priority, QtPropertyDeclarationAST *declaration, Class *klass, int generateFlags,
|
|
|
|
|
const QString &getterName, const QString &setterName, const QString &signalName,
|
|
|
|
|
const QString &storageName)
|
|
|
|
|
: CppQuickFixOperation(interface, priority)
|
|
|
|
|
, m_declaration(declaration)
|
|
|
|
|
, m_class(klass)
|
|
|
|
|
, m_generateFlags(generateFlags)
|
|
|
|
|
, m_getterName(getterName)
|
|
|
|
|
, m_setterName(setterName)
|
|
|
|
|
, m_signalName(signalName)
|
|
|
|
|
, m_storageName(storageName)
|
|
|
|
|
{
|
2013-04-23 17:24:48 +02:00
|
|
|
setDescription(QuickFixFactory::tr("Generate Missing Q_PROPERTY Members..."));
|
2013-04-11 18:01:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr file = refactoring.file(fileName());
|
|
|
|
|
|
|
|
|
|
InsertionPointLocator locator(refactoring);
|
|
|
|
|
ChangeSet declarations;
|
|
|
|
|
|
|
|
|
|
const QString typeName = file->textOf(m_declaration->type_id);
|
|
|
|
|
const QString propertyName = file->textOf(m_declaration->property_name);
|
|
|
|
|
|
|
|
|
|
// getter declaration
|
|
|
|
|
if (m_generateFlags & GenerateGetter) {
|
|
|
|
|
const QString getterDeclaration = typeName + QLatin1Char(' ') + m_getterName +
|
|
|
|
|
QLatin1String("() const\n{\nreturn ") + m_storageName + QLatin1String(";\n}\n");
|
|
|
|
|
InsertionLocation getterLoc = locator.methodDeclarationInClass(file->fileName(), m_class, InsertionPointLocator::Public);
|
|
|
|
|
QTC_ASSERT(getterLoc.isValid(), return);
|
|
|
|
|
insertAndIndent(file, &declarations, getterLoc, getterDeclaration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// setter declaration
|
|
|
|
|
if (m_generateFlags & GenerateSetter) {
|
|
|
|
|
QString setterDeclaration;
|
|
|
|
|
QTextStream setter(&setterDeclaration);
|
|
|
|
|
setter << "void " << m_setterName << '(' << typeName << " arg)\n{\n";
|
|
|
|
|
if (m_signalName.isEmpty()) {
|
|
|
|
|
setter << m_storageName << " = arg;\n}\n";
|
|
|
|
|
} else {
|
|
|
|
|
setter << "if (" << m_storageName << " != arg) {\n" << m_storageName
|
|
|
|
|
<< " = arg;\nemit " << m_signalName << "(arg);\n}\n}\n";
|
|
|
|
|
}
|
|
|
|
|
InsertionLocation setterLoc = locator.methodDeclarationInClass(file->fileName(), m_class, InsertionPointLocator::PublicSlot);
|
|
|
|
|
QTC_ASSERT(setterLoc.isValid(), return);
|
|
|
|
|
insertAndIndent(file, &declarations, setterLoc, setterDeclaration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// signal declaration
|
|
|
|
|
if (m_generateFlags & GenerateSignal) {
|
|
|
|
|
const QString declaration = QLatin1String("void ") + m_signalName + QLatin1Char('(')
|
|
|
|
|
+ typeName + QLatin1String(" arg);\n");
|
|
|
|
|
InsertionLocation loc = locator.methodDeclarationInClass(file->fileName(), m_class, InsertionPointLocator::Signals);
|
|
|
|
|
QTC_ASSERT(loc.isValid(), return);
|
|
|
|
|
insertAndIndent(file, &declarations, loc, declaration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// storage
|
|
|
|
|
if (m_generateFlags & GenerateStorage) {
|
|
|
|
|
const QString storageDeclaration = typeName + QLatin1String(" m_")
|
|
|
|
|
+ propertyName + QLatin1String(";\n");
|
|
|
|
|
InsertionLocation storageLoc = locator.methodDeclarationInClass(file->fileName(), m_class, InsertionPointLocator::Private);
|
|
|
|
|
QTC_ASSERT(storageLoc.isValid(), return);
|
|
|
|
|
insertAndIndent(file, &declarations, storageLoc, storageDeclaration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file->setChangeSet(declarations);
|
|
|
|
|
file->apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void insertAndIndent(const RefactoringFilePtr &file, ChangeSet *changeSet,
|
|
|
|
|
const InsertionLocation &loc, const QString &text)
|
|
|
|
|
{
|
|
|
|
|
int targetPosition1 = file->position(loc.line(), loc.column());
|
|
|
|
|
int targetPosition2 = qMax(0, file->position(loc.line(), 1) - 1);
|
|
|
|
|
changeSet->insert(targetPosition1, loc.prefix() + text + loc.suffix());
|
|
|
|
|
file->appendIndentRange(ChangeSet::Range(targetPosition2, targetPosition1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QtPropertyDeclarationAST *m_declaration;
|
|
|
|
|
Class *m_class;
|
|
|
|
|
int m_generateFlags;
|
|
|
|
|
QString m_getterName;
|
|
|
|
|
QString m_setterName;
|
|
|
|
|
QString m_signalName;
|
|
|
|
|
QString m_storageName;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void InsertQtPropertyMembers::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
|
|
|
|
|
if (path.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AST * const ast = path.last();
|
|
|
|
|
QtPropertyDeclarationAST *qtPropertyDeclaration = ast->asQtPropertyDeclaration();
|
|
|
|
|
if (!qtPropertyDeclaration)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ClassSpecifierAST *klass = 0;
|
|
|
|
|
for (int i = path.size() - 2; i >= 0; --i) {
|
|
|
|
|
klass = path.at(i)->asClassSpecifier();
|
|
|
|
|
if (klass)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!klass)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
const QString propertyName = file->textOf(qtPropertyDeclaration->property_name);
|
|
|
|
|
QString getterName;
|
|
|
|
|
QString setterName;
|
|
|
|
|
QString signalName;
|
|
|
|
|
int generateFlags = 0;
|
|
|
|
|
QtPropertyDeclarationItemListAST *it = qtPropertyDeclaration->property_declaration_item_list;
|
|
|
|
|
for (; it; it = it->next) {
|
|
|
|
|
const char *tokenString = file->tokenAt(it->value->item_name_token).spell();
|
|
|
|
|
if (!qstrcmp(tokenString, "READ")) {
|
|
|
|
|
getterName = file->textOf(it->value->expression);
|
|
|
|
|
generateFlags |= InsertQtPropertyMembersOp::GenerateGetter;
|
|
|
|
|
} else if (!qstrcmp(tokenString, "WRITE")) {
|
|
|
|
|
setterName = file->textOf(it->value->expression);
|
|
|
|
|
generateFlags |= InsertQtPropertyMembersOp::GenerateSetter;
|
|
|
|
|
} else if (!qstrcmp(tokenString, "NOTIFY")) {
|
|
|
|
|
signalName = file->textOf(it->value->expression);
|
|
|
|
|
generateFlags |= InsertQtPropertyMembersOp::GenerateSignal;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const QString storageName = QLatin1String("m_") + propertyName;
|
|
|
|
|
generateFlags |= InsertQtPropertyMembersOp::GenerateStorage;
|
|
|
|
|
|
|
|
|
|
Class *c = klass->symbol;
|
|
|
|
|
|
|
|
|
|
Overview overview;
|
|
|
|
|
for (unsigned i = 0; i < c->memberCount(); ++i) {
|
|
|
|
|
Symbol *member = c->memberAt(i);
|
|
|
|
|
FullySpecifiedType type = member->type();
|
|
|
|
|
if (member->asFunction() || (type.isValid() && type->asFunctionType())) {
|
|
|
|
|
const QString name = overview.prettyName(member->name());
|
|
|
|
|
if (name == getterName)
|
|
|
|
|
generateFlags &= ~InsertQtPropertyMembersOp::GenerateGetter;
|
|
|
|
|
else if (name == setterName)
|
|
|
|
|
generateFlags &= ~InsertQtPropertyMembersOp::GenerateSetter;
|
|
|
|
|
else if (name == signalName)
|
|
|
|
|
generateFlags &= ~InsertQtPropertyMembersOp::GenerateSignal;
|
|
|
|
|
} else if (member->asDeclaration()) {
|
|
|
|
|
const QString name = overview.prettyName(member->name());
|
|
|
|
|
if (name == storageName)
|
|
|
|
|
generateFlags &= ~InsertQtPropertyMembersOp::GenerateStorage;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getterName.isEmpty() && setterName.isEmpty() && signalName.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
result.append(QuickFixOperation::Ptr(
|
|
|
|
|
new InsertQtPropertyMembersOp(interface, path.size() - 1, qtPropertyDeclaration, c,
|
|
|
|
|
generateFlags, getterName, setterName, signalName, storageName)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class ApplyDeclDefLinkOperation : public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
explicit ApplyDeclDefLinkOperation(const CppQuickFixInterface &interface,
|
|
|
|
|
const QSharedPointer<FunctionDeclDefLink> &link)
|
|
|
|
|
: CppQuickFixOperation(interface, 10)
|
|
|
|
|
, m_link(link)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CPPEditorWidget *editor = assistInterface()->editor();
|
|
|
|
|
QSharedPointer<FunctionDeclDefLink> link = editor->declDefLink();
|
|
|
|
|
if (link == m_link)
|
|
|
|
|
editor->applyDeclDefLinkChanges(/*don't jump*/false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual void performChanges(const CppRefactoringFilePtr &, const CppRefactoringChanges &)
|
|
|
|
|
{ /* never called since perform is overridden */ }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QSharedPointer<FunctionDeclDefLink> m_link;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void ApplyDeclDefLinkChanges::match(const CppQuickFixInterface &interface,
|
|
|
|
|
QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
QSharedPointer<FunctionDeclDefLink> link = interface->editor()->declDefLink();
|
|
|
|
|
if (!link || !link->isMarkerVisible())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QSharedPointer<ApplyDeclDefLinkOperation> op(new ApplyDeclDefLinkOperation(interface, link));
|
|
|
|
|
op->setDescription(FunctionDeclDefLink::tr("Apply Function Signature Changes"));
|
|
|
|
|
result += op;
|
|
|
|
|
}
|
2013-04-15 14:23:33 +02:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
QString getDefinitionSignature(const CppQuickFixAssistInterface *assist, Function *func,
|
|
|
|
|
CppRefactoringFilePtr &file, Scope *scope)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(assist, return QString());
|
|
|
|
|
QTC_ASSERT(func, return QString());
|
|
|
|
|
QTC_ASSERT(scope, return QString());
|
|
|
|
|
|
|
|
|
|
LookupContext cppContext(file->cppDocument(), assist->snapshot());
|
|
|
|
|
ClassOrNamespace *cppCoN = cppContext.lookupType(scope);
|
|
|
|
|
if (!cppCoN)
|
|
|
|
|
cppCoN = cppContext.globalNamespace();
|
|
|
|
|
SubstitutionEnvironment env;
|
|
|
|
|
env.setContext(assist->context());
|
|
|
|
|
env.switchScope(func->enclosingScope());
|
|
|
|
|
UseMinimalNames q(cppCoN);
|
|
|
|
|
env.enter(&q);
|
|
|
|
|
Control *control = assist->context().control().data();
|
|
|
|
|
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
|
|
|
oo.showFunctionSignatures = true;
|
|
|
|
|
oo.showReturnTypes = true;
|
|
|
|
|
oo.showArgumentNames = true;
|
|
|
|
|
const FullySpecifiedType tn = rewriteType(func->type(), &env, control);
|
|
|
|
|
const QString name = oo.prettyName(LookupContext::minimalName(func, cppCoN, control));
|
|
|
|
|
|
|
|
|
|
return oo.prettyType(tn, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MoveFuncDefOutsideOp : public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
enum MoveType {
|
|
|
|
|
MoveOutside,
|
|
|
|
|
MoveToCppFile,
|
|
|
|
|
MoveOutsideMemberToCppFile
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
MoveFuncDefOutsideOp(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
|
|
|
|
|
MoveType type, FunctionDefinitionAST *funcDef, const QString cppFileName,
|
|
|
|
|
const SimpleDeclarationAST *classAST, bool insideHeader)
|
|
|
|
|
: CppQuickFixOperation(interface, 0)
|
|
|
|
|
, m_funcDef(funcDef)
|
|
|
|
|
, m_type(type)
|
|
|
|
|
, m_cppFileName(cppFileName)
|
|
|
|
|
, m_classAST(classAST)
|
|
|
|
|
, m_insideHeader(insideHeader)
|
|
|
|
|
, m_func(funcDef->symbol)
|
|
|
|
|
, m_headerFileName(QString::fromUtf8(m_func->fileName(), m_func->fileNameLength()))
|
|
|
|
|
{
|
|
|
|
|
if (m_type == MoveOutside) {
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::QuickFix",
|
|
|
|
|
"Move Definition outside Class"));
|
|
|
|
|
} else {
|
|
|
|
|
const QDir dir = QFileInfo(m_headerFileName).dir();
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::QuickFix",
|
|
|
|
|
"Move Definition to %1")
|
|
|
|
|
.arg(dir.relativeFilePath(m_cppFileName)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr fromFile = refactoring.file(m_headerFileName);
|
|
|
|
|
CppRefactoringFilePtr toFile;
|
|
|
|
|
int insertPos = 0;
|
|
|
|
|
Scope *scopeAtInsertPos = 0;
|
|
|
|
|
|
|
|
|
|
// Determine file, insert position and scope
|
|
|
|
|
if (m_type == MoveOutside) {
|
|
|
|
|
toFile = fromFile;
|
|
|
|
|
insertPos = toFile->endOf(m_classAST);
|
|
|
|
|
scopeAtInsertPos = m_func->enclosingScope()->enclosingScope();
|
|
|
|
|
} else {
|
|
|
|
|
toFile = refactoring.file(m_cppFileName);
|
|
|
|
|
const QTextDocument *doc = toFile->document();
|
|
|
|
|
insertPos = qMax(0, doc->characterCount() - 1);
|
|
|
|
|
scopeAtInsertPos = toFile->cppDocument()->scopeAt(doc->lineCount(),
|
|
|
|
|
doc->lastBlock().length());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// construct definition
|
|
|
|
|
const QString funcDec = getDefinitionSignature(assistInterface(), m_func, toFile,
|
|
|
|
|
scopeAtInsertPos);
|
2013-04-18 11:21:12 +02:00
|
|
|
QString textFuncBody;
|
|
|
|
|
if (m_funcDef->ctor_initializer)
|
|
|
|
|
textFuncBody = fromFile->textOf(m_funcDef->ctor_initializer) + QLatin1Char(' ');
|
|
|
|
|
textFuncBody += fromFile->textOf(m_funcDef->function_body);
|
2013-04-15 14:23:33 +02:00
|
|
|
QString funcDef = QString::fromLatin1("\n%1 %2\n")
|
|
|
|
|
.arg(funcDec)
|
|
|
|
|
.arg(textFuncBody);
|
|
|
|
|
if (m_cppFileName.isEmpty() || !m_insideHeader)
|
|
|
|
|
funcDef = QLatin1String("\n") + funcDef;
|
|
|
|
|
|
|
|
|
|
// insert definition at new position
|
|
|
|
|
ChangeSet cppChanges;
|
|
|
|
|
cppChanges.insert(insertPos, funcDef);
|
|
|
|
|
toFile->setChangeSet(cppChanges);
|
|
|
|
|
toFile->appendIndentRange(ChangeSet::Range(insertPos, insertPos + funcDef.size()));
|
|
|
|
|
toFile->setOpenEditor(true, insertPos);
|
|
|
|
|
toFile->apply();
|
|
|
|
|
|
|
|
|
|
// remove definition from fromFile
|
|
|
|
|
Utils::ChangeSet headerTarget;
|
|
|
|
|
if (m_type == MoveOutsideMemberToCppFile) {
|
|
|
|
|
headerTarget.remove(fromFile->range(m_funcDef));
|
|
|
|
|
} else {
|
|
|
|
|
QString textFuncDecl = fromFile->textOf(m_funcDef);
|
|
|
|
|
textFuncDecl.remove(-textFuncBody.length(), textFuncBody.length());
|
|
|
|
|
textFuncDecl = textFuncDecl.trimmed() + QLatin1String(";");
|
|
|
|
|
headerTarget.replace(fromFile->range(m_funcDef), textFuncDecl);
|
|
|
|
|
}
|
|
|
|
|
fromFile->setChangeSet(headerTarget);
|
|
|
|
|
fromFile->apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
FunctionDefinitionAST *m_funcDef;
|
|
|
|
|
MoveType m_type;
|
|
|
|
|
const QString m_cppFileName;
|
|
|
|
|
const SimpleDeclarationAST *m_classAST;
|
|
|
|
|
bool m_insideHeader;
|
|
|
|
|
Function *m_func;
|
|
|
|
|
const QString m_headerFileName;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void MoveFuncDefOutside::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
SimpleDeclarationAST *classAST = 0;
|
|
|
|
|
FunctionDefinitionAST *funcAST = 0;
|
|
|
|
|
bool moveOutsideMemberDefinition = false;
|
|
|
|
|
|
|
|
|
|
const int pathSize = path.size();
|
|
|
|
|
for (int idx = 1; idx < pathSize; ++idx) {
|
|
|
|
|
if ((funcAST = path.at(idx)->asFunctionDefinition())) {
|
|
|
|
|
// check cursor position
|
|
|
|
|
if (idx != pathSize - 1 // Do not allow "void a() @ {..."
|
|
|
|
|
&& funcAST->function_body
|
|
|
|
|
&& !interface->isCursorOn(funcAST->function_body)) {
|
|
|
|
|
if (path.at(idx - 1)->asTranslationUnit()) { // normal function
|
|
|
|
|
if (idx + 3 < pathSize && path.at(idx + 3)->asQualifiedName()) // Outside member
|
|
|
|
|
moveOutsideMemberDefinition = true; // definition
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idx > 1) {
|
|
|
|
|
if ((classAST = path.at(idx - 2)->asSimpleDeclaration())) // member function
|
|
|
|
|
break;
|
|
|
|
|
if (path.at(idx - 2)->asNamespace()) // normal function in namespace
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
funcAST = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!funcAST)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bool isHeaderFile = false;
|
|
|
|
|
const QString cppFileName = correspondingHeaderOrSource(interface->fileName(), &isHeaderFile);
|
|
|
|
|
|
|
|
|
|
if (isHeaderFile && !cppFileName.isEmpty())
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(
|
|
|
|
|
new MoveFuncDefOutsideOp(interface, ((moveOutsideMemberDefinition) ?
|
|
|
|
|
MoveFuncDefOutsideOp::MoveOutsideMemberToCppFile
|
|
|
|
|
: MoveFuncDefOutsideOp::MoveToCppFile),
|
|
|
|
|
funcAST, cppFileName, 0, isHeaderFile)));
|
|
|
|
|
|
|
|
|
|
if (classAST)
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(
|
|
|
|
|
new MoveFuncDefOutsideOp(interface, MoveFuncDefOutsideOp::MoveOutside,
|
|
|
|
|
funcAST, QLatin1String(""), classAST, false)));
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class MoveFuncDefToDeclOp : public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MoveFuncDefToDeclOp(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
|
|
|
|
|
const QString fromFileName, const QString toFileName,
|
|
|
|
|
FunctionDefinitionAST *funcDef, const QString declText,
|
|
|
|
|
const ChangeSet::Range toRange)
|
|
|
|
|
: CppQuickFixOperation(interface, 0)
|
|
|
|
|
, m_fromFileName(fromFileName)
|
|
|
|
|
, m_toFileName(toFileName)
|
|
|
|
|
, m_funcAST(funcDef)
|
|
|
|
|
, m_declarationText(declText)
|
|
|
|
|
, m_toRange(toRange)
|
|
|
|
|
{
|
|
|
|
|
if (m_toFileName == m_fromFileName) {
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::QuickFix",
|
|
|
|
|
"Move Definition to Class"));
|
|
|
|
|
} else {
|
|
|
|
|
const QDir dir = QFileInfo(m_fromFileName).dir();
|
|
|
|
|
setDescription(QCoreApplication::translate("CppEditor::QuickFix",
|
|
|
|
|
"Move Definition to %1")
|
|
|
|
|
.arg(dir.relativeFilePath(m_toFileName)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr fromFile = refactoring.file(m_fromFileName);
|
|
|
|
|
CppRefactoringFilePtr toFile = refactoring.file(m_toFileName);
|
|
|
|
|
ChangeSet::Range fromRange = fromFile->range(m_funcAST);
|
|
|
|
|
const QString definitionText = fromFile->textOf(m_funcAST->function_body);
|
2013-04-18 11:21:12 +02:00
|
|
|
QString wholeFunctionText = m_declarationText;
|
|
|
|
|
if (m_funcAST->ctor_initializer)
|
|
|
|
|
wholeFunctionText += QLatin1Char(' ') + fromFile->textOf(m_funcAST->ctor_initializer);
|
|
|
|
|
wholeFunctionText += QLatin1Char(' ') + definitionText;
|
|
|
|
|
|
2013-04-15 14:23:33 +02:00
|
|
|
|
|
|
|
|
// Replace declaration with function and delete old definition
|
|
|
|
|
Utils::ChangeSet toTarget;
|
|
|
|
|
toTarget.replace(m_toRange, wholeFunctionText);
|
|
|
|
|
if (m_toFileName == m_fromFileName)
|
|
|
|
|
toTarget.remove(fromRange);
|
|
|
|
|
toFile->setChangeSet(toTarget);
|
|
|
|
|
toFile->appendIndentRange(m_toRange);
|
|
|
|
|
toFile->setOpenEditor(true, m_toRange.start);
|
|
|
|
|
toFile->apply();
|
|
|
|
|
if (m_toFileName != m_fromFileName) {
|
|
|
|
|
Utils::ChangeSet fromTarget;
|
|
|
|
|
fromTarget.remove(fromRange);
|
|
|
|
|
fromFile->setChangeSet(fromTarget);
|
|
|
|
|
fromFile->apply();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const QString m_fromFileName;
|
|
|
|
|
const QString m_toFileName;
|
|
|
|
|
FunctionDefinitionAST *m_funcAST;
|
|
|
|
|
const QString m_declarationText;
|
|
|
|
|
const ChangeSet::Range m_toRange;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Namespace *isNamespaceFunction(const LookupContext &context, Function *function)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(function, return 0);
|
|
|
|
|
if (isMemberFunction(context, function))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
Scope *enclosingScope = function->enclosingScope();
|
|
|
|
|
while (!(enclosingScope->isNamespace() || enclosingScope->isClass()))
|
|
|
|
|
enclosingScope = enclosingScope->enclosingScope();
|
|
|
|
|
QTC_ASSERT(enclosingScope != 0, return 0);
|
|
|
|
|
|
|
|
|
|
const Name *functionName = function->name();
|
|
|
|
|
if (!functionName)
|
|
|
|
|
return 0; // anonymous function names are not valid c++
|
|
|
|
|
|
|
|
|
|
// global namespace
|
|
|
|
|
if (!functionName->isQualifiedNameId()) {
|
|
|
|
|
foreach (Symbol *s, context.globalNamespace()->symbols()) {
|
|
|
|
|
if (Namespace *matchingNamespace = s->asNamespace())
|
|
|
|
|
return matchingNamespace;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QualifiedNameId *q = functionName->asQualifiedNameId();
|
|
|
|
|
if (!q->base())
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
|
|
|
|
|
foreach (Symbol *s, binding->symbols()) {
|
|
|
|
|
if (Namespace *matchingNamespace = s->asNamespace())
|
|
|
|
|
return matchingNamespace;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void MoveFuncDefToDecl::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
FunctionDefinitionAST *funcAST = 0;
|
|
|
|
|
|
|
|
|
|
const int pathSize = path.size();
|
|
|
|
|
for (int idx = 1; idx < pathSize; ++idx) {
|
|
|
|
|
if ((funcAST = path.at(idx)->asFunctionDefinition())) {
|
|
|
|
|
if (path.at(idx - 1)->asClassSpecifier())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// check cursor position
|
|
|
|
|
if (idx != pathSize - 1 // Do not allow "void a() @ {..."
|
|
|
|
|
&& funcAST->function_body
|
|
|
|
|
&& !interface->isCursorOn(funcAST->function_body)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
funcAST = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!funcAST || !funcAST->symbol)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Determine declaration (file, range, text);
|
|
|
|
|
QString declFileName;
|
|
|
|
|
ChangeSet::Range declRange;
|
|
|
|
|
QString declText;
|
|
|
|
|
|
|
|
|
|
Function *func = funcAST->symbol;
|
|
|
|
|
if (Class *matchingClass = isMemberFunction(interface->context(), func)) {
|
|
|
|
|
// Dealing with member functions
|
|
|
|
|
const QualifiedNameId *qName = func->name()->asQualifiedNameId();
|
|
|
|
|
for (Symbol *s = matchingClass->find(qName->identifier()); s; s = s->next()) {
|
|
|
|
|
if (!s->name()
|
|
|
|
|
|| !qName->identifier()->isEqualTo(s->identifier())
|
|
|
|
|
|| !s->type()->isFunctionType()
|
|
|
|
|
|| !s->type().isEqualTo(func->type())
|
|
|
|
|
|| s->isFunction()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
declFileName = QString::fromUtf8(matchingClass->fileName(),
|
|
|
|
|
matchingClass->fileNameLength());
|
|
|
|
|
|
|
|
|
|
const CppRefactoringChanges refactoring(interface->snapshot());
|
|
|
|
|
const CppRefactoringFilePtr declFile = refactoring.file(declFileName);
|
|
|
|
|
ASTPath astPath(declFile->cppDocument());
|
|
|
|
|
const QList<AST *> path = astPath(s->line(), s->column());
|
|
|
|
|
for (int idx = 0; idx < path.size(); ++idx) {
|
|
|
|
|
AST *node = path.at(idx);
|
|
|
|
|
if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
|
|
|
|
|
if (simpleDecl->symbols && !simpleDecl->symbols->next) {
|
|
|
|
|
declRange = declFile->range(simpleDecl);
|
|
|
|
|
declText = declFile->textOf(simpleDecl);
|
|
|
|
|
declText.remove(-1, 1); // remove ';' from declaration text
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!declText.isEmpty())
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (Namespace *matchingNamespace = isNamespaceFunction(interface->context(), func)) {
|
|
|
|
|
// Dealing with free functions
|
|
|
|
|
bool isHeaderFile = false;
|
|
|
|
|
declFileName = correspondingHeaderOrSource(interface->fileName(), &isHeaderFile);
|
|
|
|
|
if (isHeaderFile)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CppRefactoringChanges refactoring(interface->snapshot());
|
|
|
|
|
const CppRefactoringFilePtr declFile = refactoring.file(declFileName);
|
|
|
|
|
const LookupContext lc(declFile->cppDocument(), interface->snapshot());
|
|
|
|
|
const QList<LookupItem> candidates = lc.lookup(func->name(), matchingNamespace);
|
|
|
|
|
for (int i = 0; i < candidates.size(); ++i) {
|
|
|
|
|
if (Symbol *s = candidates.at(i).declaration()) {
|
|
|
|
|
if (s->asDeclaration()) {
|
|
|
|
|
ASTPath astPath(declFile->cppDocument());
|
|
|
|
|
const QList<AST *> path = astPath(s->line(), s->column());
|
|
|
|
|
for (int idx = 0; idx < path.size(); ++idx) {
|
|
|
|
|
AST *node = path.at(idx);
|
|
|
|
|
if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
|
|
|
|
|
declRange = declFile->range(simpleDecl);
|
|
|
|
|
declText = declFile->textOf(simpleDecl);
|
|
|
|
|
declText.remove(-1, 1); // remove ';' from declaration text
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!declText.isEmpty())
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!declFileName.isEmpty() && !declText.isEmpty())
|
|
|
|
|
result.append(QuickFixOperation::Ptr(new MoveFuncDefToDeclOp(interface,
|
|
|
|
|
interface->fileName(),
|
|
|
|
|
declFileName,
|
|
|
|
|
funcAST, declText,
|
|
|
|
|
declRange)));
|
|
|
|
|
}
|
2013-04-17 21:53:20 +02:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class AssignToLocalVariableOperation : public CppQuickFixOperation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
explicit AssignToLocalVariableOperation(const CppQuickFixInterface &interface,
|
|
|
|
|
const int insertPos, const AST *ast, const Name *name)
|
|
|
|
|
: CppQuickFixOperation(interface)
|
|
|
|
|
, m_insertPos(insertPos)
|
|
|
|
|
, m_ast(ast)
|
|
|
|
|
, m_name(name)
|
|
|
|
|
{
|
|
|
|
|
setDescription(QApplication::translate("CppTools::QuickFix", "Assign to Local Variable"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void perform()
|
|
|
|
|
{
|
|
|
|
|
CppRefactoringChanges refactoring(snapshot());
|
|
|
|
|
CppRefactoringFilePtr file = refactoring.file(assistInterface()->fileName());
|
|
|
|
|
|
|
|
|
|
// Determine return type and new variable name
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
|
|
|
|
typeOfExpression.init(assistInterface()->semanticInfo().doc, snapshot(),
|
|
|
|
|
assistInterface()->context().bindings());
|
|
|
|
|
Scope *scope = file->scopeAt(m_ast->firstToken());
|
|
|
|
|
const QList<LookupItem> result = typeOfExpression(file->textOf(m_ast).toUtf8(),
|
|
|
|
|
scope, TypeOfExpression::Preprocess);
|
|
|
|
|
|
|
|
|
|
if (!result.isEmpty()) {
|
|
|
|
|
SubstitutionEnvironment env;
|
|
|
|
|
env.setContext(assistInterface()->context());
|
|
|
|
|
env.switchScope(result.first().scope());
|
|
|
|
|
ClassOrNamespace *con = typeOfExpression.context().lookupType(scope);
|
|
|
|
|
if (!con)
|
|
|
|
|
con = typeOfExpression.context().globalNamespace();
|
|
|
|
|
UseMinimalNames q(con);
|
|
|
|
|
env.enter(&q);
|
|
|
|
|
|
|
|
|
|
Control *control = assistInterface()->context().control().data();
|
|
|
|
|
FullySpecifiedType type = rewriteType(result.first().type(), &env, control);
|
|
|
|
|
|
|
|
|
|
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
|
|
|
QString originalName = oo.prettyName(m_name);
|
|
|
|
|
QString newName = originalName;
|
|
|
|
|
if (newName.startsWith(QLatin1String("get"), Qt::CaseInsensitive)
|
|
|
|
|
&& newName.length() > 3
|
|
|
|
|
&& newName.at(3).isUpper()) {
|
|
|
|
|
newName.remove(0, 3);
|
|
|
|
|
newName.replace(0, 1, newName.at(0).toLower());
|
|
|
|
|
} else if (newName.startsWith(QLatin1String("to"), Qt::CaseInsensitive)
|
|
|
|
|
&& newName.length() > 2
|
|
|
|
|
&& newName.at(2).isUpper()) {
|
|
|
|
|
newName.remove(0, 2);
|
|
|
|
|
newName.replace(0, 1, newName.at(0).toLower());
|
|
|
|
|
} else {
|
|
|
|
|
newName.replace(0, 1, newName.at(0).toUpper());
|
|
|
|
|
newName.prepend(QLatin1String("local"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int nameLength = originalName.length();
|
|
|
|
|
QString tempType = oo.prettyType(type, m_name);
|
|
|
|
|
const QString insertString = tempType.replace(
|
|
|
|
|
tempType.length() - nameLength, nameLength, newName + QLatin1String(" = "));
|
|
|
|
|
if (!tempType.isEmpty()) {
|
|
|
|
|
ChangeSet changes;
|
|
|
|
|
changes.insert(m_insertPos, insertString);
|
|
|
|
|
file->setChangeSet(changes);
|
|
|
|
|
file->apply();
|
|
|
|
|
|
|
|
|
|
// move cursor to new variable name
|
|
|
|
|
QTextCursor c = file->cursor();
|
|
|
|
|
c.setPosition(m_insertPos + insertString.length() - newName.length() - 3);
|
|
|
|
|
c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
assistInterface()->editor()->setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const int m_insertPos;
|
|
|
|
|
const AST *m_ast;
|
|
|
|
|
const Name *m_name;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
void AssignToLocalVariable::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
|
|
|
{
|
|
|
|
|
const QList<AST *> &path = interface->path();
|
|
|
|
|
AST *outerAST = 0;
|
|
|
|
|
SimpleNameAST *nameAST = 0;
|
|
|
|
|
SimpleNameAST *visibleNameAST = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = path.size() - 3; i >= 0; --i) {
|
|
|
|
|
if (CallAST *callAST = path.at(i)->asCall()) {
|
|
|
|
|
if (!interface->isCursorOn(callAST))
|
|
|
|
|
return;
|
|
|
|
|
if (i - 2 >= 0) {
|
|
|
|
|
const int idx = i - 2;
|
|
|
|
|
if (path.at(idx)->asSimpleDeclaration())
|
|
|
|
|
return;
|
|
|
|
|
if (path.at(idx)->asExpressionStatement())
|
|
|
|
|
return;
|
|
|
|
|
if (path.at(idx)->asMemInitializer())
|
|
|
|
|
return;
|
|
|
|
|
if (path.at(i - 1)->asBinaryExpression())
|
|
|
|
|
return;
|
|
|
|
|
if (path.at(i - 1)->asReturnStatement())
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (MemberAccessAST *member = path.at(i + 1)->asMemberAccess()) { // member
|
|
|
|
|
if (member->base_expression) {
|
|
|
|
|
if (IdExpressionAST *idex = member->base_expression->asIdExpression()) {
|
|
|
|
|
nameAST = idex->name->asSimpleName();
|
|
|
|
|
visibleNameAST = member->member_name->asSimpleName();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (QualifiedNameAST *qname = path.at(i + 2)->asQualifiedName()) { // static or
|
|
|
|
|
nameAST = qname->unqualified_name->asSimpleName(); // func in ns
|
|
|
|
|
visibleNameAST = nameAST;
|
|
|
|
|
} else { // normal
|
|
|
|
|
nameAST = path.at(i + 2)->asSimpleName();
|
|
|
|
|
visibleNameAST = nameAST;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nameAST && visibleNameAST) {
|
|
|
|
|
outerAST = callAST;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else if (NewExpressionAST *newexp = path.at(i)->asNewExpression()) {
|
|
|
|
|
if (!interface->isCursorOn(newexp))
|
|
|
|
|
return;
|
|
|
|
|
if (i - 2 >= 0) {
|
|
|
|
|
const int idx = i - 2;
|
|
|
|
|
if (path.at(idx)->asSimpleDeclaration())
|
|
|
|
|
return;
|
|
|
|
|
if (path.at(idx)->asExpressionStatement())
|
|
|
|
|
return;
|
|
|
|
|
if (path.at(idx)->asMemInitializer())
|
|
|
|
|
return;
|
|
|
|
|
if (path.at(i-1)->asReturnStatement())
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (NamedTypeSpecifierAST *ts = path.at(i + 2)->asNamedTypeSpecifier()) {
|
|
|
|
|
nameAST = ts->name->asSimpleName();
|
|
|
|
|
visibleNameAST = nameAST;
|
|
|
|
|
outerAST = newexp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (outerAST && nameAST && visibleNameAST) {
|
|
|
|
|
const CppRefactoringFilePtr file = interface->currentFile();
|
|
|
|
|
QList<LookupItem> items;
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
|
|
|
|
typeOfExpression.init(interface->semanticInfo().doc, interface->snapshot(),
|
|
|
|
|
interface->context().bindings());
|
|
|
|
|
if (CallAST *callAST = outerAST->asCall()) {
|
|
|
|
|
Scope *scope = file->scopeAt(callAST->base_expression->firstToken());
|
|
|
|
|
items = typeOfExpression(file->textOf(callAST->base_expression).toUtf8(), scope,
|
|
|
|
|
TypeOfExpression::Preprocess);
|
|
|
|
|
} else {
|
|
|
|
|
Scope *scope = file->scopeAt(nameAST->firstToken());
|
|
|
|
|
items = typeOfExpression(file->textOf(nameAST).toUtf8(), scope,
|
|
|
|
|
TypeOfExpression::Preprocess);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (const LookupItem &item, items) {
|
|
|
|
|
if (!item.declaration())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (Function *func = item.declaration()->asFunction()) {
|
|
|
|
|
if (func->isSignal() || func->returnType()->isVoidType())
|
|
|
|
|
return;
|
|
|
|
|
} else if (Declaration *dec = item.declaration()->asDeclaration()) {
|
|
|
|
|
if (Function *func = dec->type()->asFunctionType()) {
|
|
|
|
|
if (func->isSignal() || func->returnType()->isVoidType())
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Name *name = visibleNameAST->name;
|
|
|
|
|
const int insertPos = interface->currentFile()->startOf(outerAST);
|
|
|
|
|
result.append(CppQuickFixOperation::Ptr(
|
|
|
|
|
new AssignToLocalVariableOperation(interface, insertPos, outerAST,
|
|
|
|
|
name)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|