Commit f0826649 authored by Lorenz Haas's avatar Lorenz Haas
Browse files

CppEditor: Add new QuickFix MoveAllFuncDefOutside



This QuickFix behaves like MoveFuncDefOutside except it moves all
member function definitions. It is triggered on the class name.

Change-Id: I1393a51cad521a7bb58a1ed6223ab0fe4db0e592
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@theqtcompany.com>
parent e58b326a
......@@ -2088,6 +2088,42 @@
\endcode
\li Function signature
\row
\li Move All Function Definitions
\li Moves all function definitions to the implementation file or outside the class. For example, rewrites:
\code
class Foo
{
void bar()
{
// do stuff here
}
void baz()
{
// do stuff here
}
};
\endcode
as
\code
class Foo
{
void bar();
void baz();
};
void Foo::bar() {
// do stuff here
}
void Foo::baz() {
// do stuff here
}
\endcode
\li Class name
\row
\li Assign to Local Variable
\li Adds a local variable which stores the return value of a function call or a new expression. For example, rewrites:
......
......@@ -174,6 +174,11 @@ private slots:
void test_quickfix_MoveFuncDefOutside_respectWsInOperatorNames2();
void test_quickfix_MoveFuncDefOutside_macroUses();
void test_quickfix_MoveAllFuncDefOutside_MemberFuncToCpp();
void test_quickfix_MoveAllFuncDefOutside_MemberFuncOutside();
void test_quickfix_MoveAllFuncDefOutside_DoNotTriggerOnBaseClass();
void test_quickfix_MoveAllFuncDefOutside_classWithBaseClass();
void test_quickfix_MoveFuncDefToDecl_MemberFunc();
void test_quickfix_MoveFuncDefToDecl_MemberFuncOutside();
void test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNS();
......
......@@ -3981,6 +3981,132 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_override()
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
/// Check: Move all definitions from header to cpp.
void CppEditorPlugin::test_quickfix_MoveAllFuncDefOutside_MemberFuncToCpp()
{
QList<QuickFixTestDocument::Ptr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"class Fo@o {\n"
" int numberA() const\n"
" {\n"
" return 5;\n"
" }\n"
" int numberB() const\n"
" {\n"
" return 5;\n"
" }\n"
"};\n";
expected =
"class Foo {\n"
" int numberA() const;\n"
" int numberB() const;\n"
"};\n";
testFiles << QuickFixTestDocument::create("file.h", original, expected);
// Source File
original =
"#include \"file.h\"\n";
expected =
"#include \"file.h\"\n"
"\n"
"\n"
"int Foo::numberA() const\n"
"{\n"
" return 5;\n"
"}\n"
"\n\n"
"int Foo::numberB() const\n"
"{\n"
" return 5;\n"
"}\n"
;
testFiles << QuickFixTestDocument::create("file.cpp", original, expected);
MoveAllFuncDefOutside factory;
QuickFixOperationTest(testFiles, &factory);
}
/// Check: Move all definition outside class
void CppEditorPlugin::test_quickfix_MoveAllFuncDefOutside_MemberFuncOutside()
{
QByteArray original =
"class F@oo {\n"
" int f1()\n"
" {\n"
" return 1;\n"
" }\n"
" int f2() const\n"
" {\n"
" return 2;\n"
" }\n"
"};\n";
QByteArray expected =
"class Foo {\n"
" int f1();\n"
" int f2() const;\n"
"};\n"
"\n\n"
"int Foo::f1()\n"
"{\n"
" return 1;\n"
"}\n"
"\n\n"
"int Foo::f2() const\n"
"{\n"
" return 2;\n"
"}\n";
MoveAllFuncDefOutside factory;
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
/// Check: Move all definition outside class
void CppEditorPlugin::test_quickfix_MoveAllFuncDefOutside_DoNotTriggerOnBaseClass()
{
QByteArray original =
"class Bar;\n"
"class Foo : public Ba@r {\n"
" int f1()\n"
" {\n"
" return 1;\n"
" }\n"
"};\n";
QByteArray expected = original;
MoveAllFuncDefOutside factory;
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
/// Check: Move all definition outside class
void CppEditorPlugin::test_quickfix_MoveAllFuncDefOutside_classWithBaseClass()
{
QByteArray original =
"class Bar;\n"
"class Fo@o : public Bar {\n"
" int f1()\n"
" {\n"
" return 1;\n"
" }\n"
"};\n";
QByteArray expected =
"class Bar;\n"
"class Foo : public Bar {\n"
" int f1();\n"
"};\n"
"\n\n"
"int Foo::f1()\n"
"{\n"
" return 1;\n"
"}\n";
MoveAllFuncDefOutside factory;
QuickFixOperationTest(singleDocument(original, expected), &factory);
}
void CppEditorPlugin::test_quickfix_AssignToLocalVariable_templates()
{
......
......@@ -117,6 +117,7 @@ void registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
plugIn->addAutoReleasedObject(new InsertDefFromDecl);
plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
plugIn->addAutoReleasedObject(new MoveAllFuncDefOutside);
plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
plugIn->addAutoReleasedObject(new AssignToLocalVariable);
......@@ -4454,7 +4455,7 @@ QString definitionSignature(const CppQuickFixInterface *assist,
return oo.prettyType(tn, nameText);
}
class MoveFuncDefOutsideOp : public CppQuickFixOperation
class MoveFuncDefRefactoringHelper
{
public:
enum MoveType {
......@@ -4463,78 +4464,106 @@ public:
MoveOutsideMemberToCppFile
};
MoveFuncDefOutsideOp(const CppQuickFixInterface &interface,
MoveType type, FunctionDefinitionAST *funcDef, const QString &cppFileName)
: CppQuickFixOperation(interface, 0)
, m_funcDef(funcDef)
, m_type(type)
, m_cppFileName(cppFileName)
, m_func(funcDef->symbol)
, m_headerFileName(QString::fromUtf8(m_func->fileName(), m_func->fileNameLength()))
MoveFuncDefRefactoringHelper(CppQuickFixOperation *operation, MoveType type,
const QString &fromFile, const QString &toFile)
: m_operation(operation), m_type(type), m_changes(m_operation->snapshot())
{
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)));
}
m_fromFile = m_changes.file(fromFile);
m_toFile = (m_type == MoveOutside) ? m_fromFile : m_changes.file(toFile);
}
void perform()
void performMove(FunctionDefinitionAST *funcAST)
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr fromFile = refactoring.file(m_headerFileName);
CppRefactoringFilePtr toFile = (m_type == MoveOutside) ? fromFile
: refactoring.file(m_cppFileName);
// Determine file, insert position and scope
InsertionLocation l
= insertLocationForMethodDefinition(m_func, false, refactoring, toFile->fileName());
InsertionLocation l = insertLocationForMethodDefinition(funcAST->symbol, false,
m_changes, m_toFile->fileName());
const QString prefix = l.prefix();
const QString suffix = l.suffix();
const int insertPos = toFile->position(l.line(), l.column());
Scope *scopeAtInsertPos = toFile->cppDocument()->scopeAt(l.line(), l.column());
const int insertPos = m_toFile->position(l.line(), l.column());
Scope *scopeAtInsertPos = m_toFile->cppDocument()->scopeAt(l.line(), l.column());
// construct definition
const QString funcDec = definitionSignature(this, m_funcDef,
fromFile, toFile,
const QString funcDec = definitionSignature(m_operation, funcAST, m_fromFile, m_toFile,
scopeAtInsertPos);
QString funcDef = prefix + funcDec;
const int startPosition = fromFile->endOf(m_funcDef->declarator);
const int endPosition = fromFile->endOf(m_funcDef->function_body);
funcDef += fromFile->textOf(startPosition, endPosition);
const int startPosition = m_fromFile->endOf(funcAST->declarator);
const int endPosition = m_fromFile->endOf(funcAST->function_body);
funcDef += m_fromFile->textOf(startPosition, endPosition);
funcDef += suffix;
// 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();
m_toFileChangeSet.insert(insertPos, funcDef);
m_toFile->appendIndentRange(ChangeSet::Range(insertPos, insertPos + funcDef.size()));
m_toFile->setOpenEditor(true, insertPos);
// remove definition from fromFile
ChangeSet headerTarget;
if (m_type == MoveOutsideMemberToCppFile) {
headerTarget.remove(fromFile->range(m_funcDef));
m_fromFileChangeSet.remove(m_fromFile->range(funcAST));
} else {
QString textFuncDecl = fromFile->textOf(m_funcDef);
textFuncDecl.truncate(startPosition - fromFile->startOf(m_funcDef));
QString textFuncDecl = m_fromFile->textOf(funcAST);
textFuncDecl.truncate(startPosition - m_fromFile->startOf(funcAST));
textFuncDecl = textFuncDecl.trimmed() + QLatin1Char(';');
headerTarget.replace(fromFile->range(m_funcDef), textFuncDecl);
m_fromFileChangeSet.replace(m_fromFile->range(funcAST), textFuncDecl);
}
}
void applyChanges()
{
if (!m_toFileChangeSet.isEmpty()) {
m_toFile->setChangeSet(m_toFileChangeSet);
m_toFile->apply();
}
if (!m_fromFileChangeSet.isEmpty()) {
m_fromFile->setChangeSet(m_fromFileChangeSet);
m_fromFile->apply();
}
fromFile->setChangeSet(headerTarget);
fromFile->apply();
}
private:
FunctionDefinitionAST *m_funcDef;
CppQuickFixOperation *m_operation;
MoveType m_type;
CppRefactoringChanges m_changes;
CppRefactoringFilePtr m_fromFile;
CppRefactoringFilePtr m_toFile;
ChangeSet m_fromFileChangeSet;
ChangeSet m_toFileChangeSet;
};
class MoveFuncDefOutsideOp : public CppQuickFixOperation
{
public:
MoveFuncDefOutsideOp(const CppQuickFixInterface &interface,
MoveFuncDefRefactoringHelper::MoveType type,
FunctionDefinitionAST *funcDef, const QString &cppFileName)
: CppQuickFixOperation(interface, 0)
, m_funcDef(funcDef)
, m_type(type)
, m_cppFileName(cppFileName)
, m_headerFileName(QString::fromUtf8(funcDef->symbol->fileName(),
funcDef->symbol->fileNameLength()))
{
if (m_type == MoveFuncDefRefactoringHelper::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()
{
MoveFuncDefRefactoringHelper helper(this, m_type, m_headerFileName, m_cppFileName);
helper.performMove(m_funcDef);
helper.applyChanges();
}
private:
FunctionDefinitionAST *m_funcDef;
MoveFuncDefRefactoringHelper::MoveType m_type;
const QString m_cppFileName;
Function *m_func;
const QString m_headerFileName;
};
......@@ -4577,14 +4606,15 @@ void MoveFuncDefOutside::match(const CppQuickFixInterface &interface, QuickFixOp
bool isHeaderFile = false;
const QString cppFileName = correspondingHeaderOrSource(interface.fileName(), &isHeaderFile);
if (isHeaderFile && !cppFileName.isEmpty())
result.append(new MoveFuncDefOutsideOp(interface, ((moveOutsideMemberDefinition) ?
MoveFuncDefOutsideOp::MoveOutsideMemberToCppFile
: MoveFuncDefOutsideOp::MoveToCppFile),
funcAST, cppFileName));
if (isHeaderFile && !cppFileName.isEmpty()) {
const MoveFuncDefRefactoringHelper::MoveType type = moveOutsideMemberDefinition
? MoveFuncDefRefactoringHelper::MoveOutsideMemberToCppFile
: MoveFuncDefRefactoringHelper::MoveToCppFile;
result.append(new MoveFuncDefOutsideOp(interface, type, funcAST, cppFileName));
}
if (classAST)
result.append(new MoveFuncDefOutsideOp(interface, MoveFuncDefOutsideOp::MoveOutside,
result.append(new MoveFuncDefOutsideOp(interface, MoveFuncDefRefactoringHelper::MoveOutside,
funcAST, QLatin1String("")));
return;
......@@ -4592,6 +4622,90 @@ void MoveFuncDefOutside::match(const CppQuickFixInterface &interface, QuickFixOp
namespace {
class MoveAllFuncDefOutsideOp : public CppQuickFixOperation
{
public:
MoveAllFuncDefOutsideOp(const CppQuickFixInterface &interface,
MoveFuncDefRefactoringHelper::MoveType type,
ClassSpecifierAST *classDef, const QString &cppFileName)
: CppQuickFixOperation(interface, 0)
, m_type(type)
, m_classDef(classDef)
, m_cppFileName(cppFileName)
, m_headerFileName(QString::fromUtf8(classDef->symbol->fileName(),
classDef->symbol->fileNameLength()))
{
if (m_type == MoveFuncDefRefactoringHelper::MoveOutside) {
setDescription(QCoreApplication::translate("CppEditor::QuickFix", "Move All Function "
"Definitions Outside Class"));
} else {
const QDir dir = QFileInfo(m_headerFileName).dir();
setDescription(QCoreApplication::translate("CppEditor::QuickFix",
"Move All Function Definitions to %1")
.arg(dir.relativeFilePath(m_cppFileName)));
}
}
void perform()
{
MoveFuncDefRefactoringHelper helper(this, m_type, m_headerFileName, m_cppFileName);
for (DeclarationListAST *it = m_classDef->member_specifier_list; it; it = it->next) {
if (FunctionDefinitionAST *funcAST = it->value->asFunctionDefinition())
helper.performMove(funcAST);
}
helper.applyChanges();
}
private:
MoveFuncDefRefactoringHelper::MoveType m_type;
ClassSpecifierAST *m_classDef;
const QString m_cppFileName;
const QString m_headerFileName;
};
} // anonymous namespace
void MoveAllFuncDefOutside::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
const QList<AST *> &path = interface.path();
const int pathSize = path.size();
if (pathSize < 2)
return;
// Determine if cursor is on a class which is not a base class
ClassSpecifierAST *classAST = Q_NULLPTR;
if (SimpleNameAST *nameAST = path.at(pathSize - 1)->asSimpleName()) {
if (!interface.isCursorOn(nameAST))
return;
classAST = path.at(pathSize - 2)->asClassSpecifier();
}
if (!classAST)
return;
// Determine if the class has at least one function definition
bool classContainsFunctions = false;
for (DeclarationListAST *it = classAST->member_specifier_list; it; it = it->next) {
if (it->value->asFunctionDefinition()) {
classContainsFunctions = true;
break;
}
}
if (!classContainsFunctions)
return;
bool isHeaderFile = false;
const QString cppFileName = correspondingHeaderOrSource(interface.fileName(), &isHeaderFile);
if (isHeaderFile && !cppFileName.isEmpty()) {
result.append(new MoveAllFuncDefOutsideOp(interface,
MoveFuncDefRefactoringHelper::MoveToCppFile,
classAST, cppFileName));
}
result.append(new MoveAllFuncDefOutsideOp(interface, MoveFuncDefRefactoringHelper::MoveOutside,
classAST, QLatin1String("")));
}
namespace {
class MoveFuncDefToDeclOp : public CppQuickFixOperation
{
public:
......
......@@ -506,6 +506,15 @@ public:
void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result);
};
/*!
Moves all member function definitions outside the class or to the implementation file.
*/
class MoveAllFuncDefOutside: public CppQuickFixFactory
{
public:
void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result);
};
/*!
Moves the definition of a function to its declaration.
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment