Commit 15f90404 authored by Lorenz Haas's avatar Lorenz Haas Committed by Nikolai Kosjar
Browse files

CppEditor: InsertDefFromDecl: choose insert position



Now one can decide where the new definition should go: Inside the class,
outside the class or to the implementation file. Further the text cursor
is positioned inside the new created definition body.

Task-number: QTCREATORBUG-6973
Change-Id: I593955dd1e44e35240fa1e9b9a5c1a67eb119456
Reviewed-by: default avatarLeena Miettinen <riitta-leena.miettinen@digia.com>
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@digia.com>
parent 5dbcb974
......@@ -1758,11 +1758,56 @@
\li Adds the matching #include statement for a forward-declared class or struct
\li Forward-declared class or struct
\row
\li Add Definition in 'filename'
\li Inserts a definition stub for a function declaration in the
implementation file. The definition is placed after that of the
preceding declaration. Qualified names are minimized when possible,
instead of always being fully expanded.
\li Add Definition in ...
\li Inserts a definition stub for a function declaration either in the header file
(inside or outside the class) or in the implementation file. For free functions, inserts
the definition after the declaration of the function or in the implementation file.
Qualified names are minimized when possible, instead of always being fully expanded.
For example, rewrites
\code
Class Foo {
void bar();
};
\endcode
as (inside class)
\code
Class Foo {
void bar() {
}
};
\endcode
as (outside class)
\code
Class Foo {
void bar();
};
void Foo::bar()
{
}
\endcode
as (in implementation file)
\code
// Header file
Class Foo {
void bar();
};
// Implementation file
void Foo::bar()
{
}
\endcode
\li Method name
\row
......
......@@ -156,6 +156,7 @@ private slots:
void test_quickfix_InsertDefFromDecl_headerSource_namespace1();
void test_quickfix_InsertDefFromDecl_headerSource_namespace2();
void test_quickfix_InsertDefFromDecl_freeFunction();
void test_quickfix_InsertDefFromDecl_insideClass();
void test_quickfix_InsertDeclFromDef();
......
......@@ -713,11 +713,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_basic()
"\n"
;
const QByteArray expected = original +
"\n"
"Foo::Foo()\n"
"{\n"
"{\n\n"
"}\n"
"\n"
"\n\n"
;
InsertDefFromDecl factory;
......@@ -747,7 +746,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic1()
expected =
"\n"
"Foo::Foo()\n"
"{\n"
"{\n\n"
"}\n"
"\n"
;
......@@ -776,10 +775,9 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic2()
" Foo()@;\n"
"};\n";
expected = original +
"\n"
"\n"
"Foo::Foo()\n"
"{\n"
"{\n\n"
"}\n"
"\n"
;
......@@ -815,7 +813,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_namespace1()
expected =
"\n"
"N::Foo::Foo()\n"
"{\n"
"{\n\n"
"}\n"
"\n"
;
......@@ -855,7 +853,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_namespace2()
expected = original +
"\n"
"Foo::Foo()\n"
"{\n"
"{\n\n"
"}\n"
"\n"
;
......@@ -869,11 +867,8 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_namespace2()
void CppEditorPlugin::test_quickfix_InsertDefFromDecl_freeFunction()
{
const QByteArray original = "void free()@;\n";
const QByteArray expected = original +
"\n"
"\n"
"void free()\n"
"{\n"
const QByteArray expected =
"void free() {\n\n"
"}\n"
"\n"
;
......@@ -883,6 +878,25 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_freeFunction()
data.run(&factory);
}
/// Check definition insert inside class
void CppEditorPlugin::test_quickfix_InsertDefFromDecl_insideClass()
{
const QByteArray original =
"class Foo {\n"
" void b@ar();\n"
"};";
const QByteArray expected =
"class Foo {\n"
" void bar() {\n"
"\n"
" }\n"
"};\n";
InsertDefFromDecl factory;
TestCase data(original, expected);
data.run(&factory, 1);
}
// Function for one of InsertDeclDef section cases
void insertToSectionDeclFromDef(const QByteArray &section, int sectionIndex)
{
......
......@@ -115,6 +115,12 @@ void CppEditor::Internal::registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
// different quick fixes.
namespace {
enum DefPos {
DefPosInsideClass,
DefPosOutsideClass,
DefPosImplementationFile
};
inline bool isQtStringLiteral(const QByteArray &id)
{
return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
......@@ -2446,16 +2452,29 @@ class InsertDefOperation: public CppQuickFixOperation
{
public:
InsertDefOperation(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
Declaration *decl, const InsertionLocation &loc)
Declaration *decl, const InsertionLocation &loc, const DefPos defpos,
bool freeFunction = false)
: CppQuickFixOperation(interface, 0)
, m_decl(decl)
, m_loc(loc)
, m_defpos(defpos)
{
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())));
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"));
}
}
void perform()
......@@ -2469,44 +2488,76 @@ public:
oo.showReturnTypes = true;
oo.showArgumentNames = true;
// 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();
// rewrite the function type
FullySpecifiedType tn = rewriteType(m_decl->type(), &env, control);
// rewrite the function name
QString name = oo.prettyName(LookupContext::minimalName(m_decl, targetCoN, control));
QString defText = oo.prettyType(tn, name) + QLatin1String("\n{\n}");
int targetPos = targetFile->position(m_loc.line(), m_loc.column());
int targetPos2 = qMax(0, targetFile->position(m_loc.line(), 1) - 1);
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();
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();
// 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);
}
}
}
private:
Declaration *m_decl;
InsertionLocation m_loc;
const InsertionLocation m_loc;
const DefPos m_defpos;
};
} // anonymous namespace
......@@ -2525,12 +2576,62 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe
if (Function *func = decl->type()->asFunctionType()) {
if (func->isSignal())
return;
CppRefactoringChanges refactoring(interface->snapshot());
InsertionPointLocator locator(refactoring);
foreach (const InsertionLocation &loc, locator.methodDefinition(decl)) {
if (loc.isValid())
result.append(CppQuickFixOperation::Ptr(new InsertDefOperation(interface, decl, loc)));
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;
}
// 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));
return;
}
}
......
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