From c95b324b79cb66a2dd0e128d8b41331af6e17c3c Mon Sep 17 00:00:00 2001
From: Lorenz Haas <lykurg@gmail.com>
Date: Tue, 4 Jun 2013 17:37:24 +0200
Subject: [PATCH] CppEditor: Same insert position for quick fixes

Now definitions are inserted at the same position inside the
implementation file for MoveFuncDefOutside and InsertDefFromDecl.

Task-number: QTCREATORBUG-9389
Change-Id: If823ffd15ec39a7bc2edb53519380cb9cabb4c55
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com>
---
 src/plugins/cppeditor/cppeditorplugin.h    |   3 +-
 src/plugins/cppeditor/cppquickfix_test.cpp |  76 ++++++++++----
 src/plugins/cppeditor/cppquickfixes.cpp    | 114 ++++++++++++---------
 3 files changed, 125 insertions(+), 68 deletions(-)

diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h
index a18c528e2a9..c0fb4094c69 100644
--- a/src/plugins/cppeditor/cppeditorplugin.h
+++ b/src/plugins/cppeditor/cppeditorplugin.h
@@ -186,7 +186,8 @@ private slots:
     void test_quickfix_AddIncludeForUndefinedIdentifier_veryFirstIncludeCStyleCommentOnTop();
 
     void test_quickfix_MoveFuncDefOutside_MemberFuncToCpp();
-    void test_quickfix_MoveFuncDefOutside_MemberFuncOutside();
+    void test_quickfix_MoveFuncDefOutside_MemberFuncOutside1();
+    void test_quickfix_MoveFuncDefOutside_MemberFuncOutside2();
     void test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS();
     void test_quickfix_MoveFuncDefOutside_MemberFuncToCppNSUsing();
     void test_quickfix_MoveFuncDefOutside_MemberFuncOutsideWithNs();
diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp
index e93bf76267b..1ce9ed1aa7f 100644
--- a/src/plugins/cppeditor/cppquickfix_test.cpp
+++ b/src/plugins/cppeditor/cppquickfix_test.cpp
@@ -760,15 +760,14 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_basic()
         "struct Foo\n"
         "{\n"
         "    Foo();@\n"
-        "};\n"
-        "\n"
-        ;
+        "};\n";
     const QByteArray expected = original +
+        "\n"
+        "\n"
         "Foo::Foo()\n"
         "{\n\n"
         "}\n"
-        "\n\n"
-        ;
+        "\n";
 
     InsertDefFromDecl factory;
     TestCase data(original, expected);
@@ -826,6 +825,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic2()
         "    Foo()@;\n"
         "};\n";
     expected = original +
+        "\n"
         "\n"
         "Foo::Foo()\n"
         "{\n\n"
@@ -1768,8 +1768,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCpp()
 
     // Source File
     original =
-        "#include \"file.h\"\n"
-        "\n";
+        "#include \"file.h\"\n";
     expected =
         "#include \"file.h\"\n"
         "\n"
@@ -1787,7 +1786,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCpp()
 }
 
 /// Check: Move definition outside class
-void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside()
+void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside1()
 {
     QByteArray original =
         "class Foo {\n"
@@ -1814,7 +1813,6 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside()
         "    return 1;\n"
         "}\n"
         "\n"
-        "\n"
         "void Foo::f4() {}\n\n";
 
     MoveFuncDefOutside factory;
@@ -1822,6 +1820,50 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside()
     data.run(&factory);
 }
 
+/// Check: Move definition outside class
+void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside2()
+{
+    QList<TestDocumentPtr> testFiles;
+    QByteArray original;
+    QByteArray expected;
+
+    // Header File
+    original =
+        "class Foo {\n"
+        "    void f1();\n"
+        "    int f2@()\n"
+        "    {\n"
+        "        return 1;\n"
+        "    }\n"
+        "    void f3();\n"
+        "};\n";
+    expected =
+        "class Foo {\n"
+        "    void f1();\n"
+        "    int f2();\n"
+        "    void f3();\n"
+        "};\n"
+        "\n"
+        "\n"
+        "int Foo::f2()\n"
+        "{\n"
+        "    return 1;\n"
+        "}\n\n";
+    testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
+
+    // Source File
+    original =
+        "#include \"file.h\"\n"
+        "void Foo::f1() {}\n"
+        "void Foo::f3() {}\n";
+    expected = original + "\n";
+    testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
+
+    MoveFuncDefOutside factory;
+    TestCase data(testFiles);
+    data.run(&factory, 1);
+}
+
 /// Check: Move definition from header to cpp (with namespace).
 void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS()
 {
@@ -1849,8 +1891,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS()
 
     // Source File
     original =
-        "#include \"file.h\"\n"
-        "\n";
+        "#include \"file.h\"\n";
     expected =
         "#include \"file.h\"\n"
         "\n"
@@ -1895,8 +1936,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNSUsing()
     // Source File
     original =
         "#include \"file.h\"\n"
-        "using namespace MyNs;\n"
-        "\n";
+        "using namespace MyNs;\n";
     expected =
         "#include \"file.h\"\n"
         "using namespace MyNs;\n"
@@ -1934,7 +1974,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutsideWithNs()
         "int Foo::number() const\n"
         "{\n"
         "    return 5;\n"
-        "}"
+        "}\n"
         "\n}\n";
 
     MoveFuncDefOutside factory;
@@ -1962,8 +2002,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_FreeFuncToCpp()
 
     // Source File
     original =
-        "#include \"file.h\"\n"
-        "\n";
+        "#include \"file.h\"\n";
     expected =
         "#include \"file.h\"\n"
         "\n"
@@ -2004,8 +2043,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_FreeFuncToCppNS()
 
     // Source File
     original =
-        "#include \"file.h\"\n"
-        "\n";
+        "#include \"file.h\"\n";
     expected =
         "#include \"file.h\"\n"
         "\n"
@@ -2053,6 +2091,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_CtorWithInitialization1()
     expected =
         "#include \"file.h\"\n"
         "\n"
+        "\n"
         "Foo::Foo() : a(42), b(3.141) {}\n"
         "\n";
     testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
@@ -2096,6 +2135,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_CtorWithInitialization2()
     expected =
         "#include \"file.h\"\n"
         "\n"
+        "\n"
         "Foo::Foo() : member(2)\n"
         "{\n"
         "}\n"
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index ac629d14fa2..50aaf7b494b 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -143,6 +143,37 @@ enum DefPos {
     DefPosImplementationFile
 };
 
+InsertionLocation insertLocationForMethodDefinition(Symbol *symbol,
+                                                    CppRefactoringChanges& refactoring,
+                                                    const QString& fileName)
+{
+    QTC_ASSERT(symbol, return InsertionLocation());
+
+    // Try to find optimal location
+    const InsertionPointLocator locator(refactoring);
+    const QList<InsertionLocation> list = locator.methodDefinition(symbol, symbol->asDeclaration(),
+                                                                   fileName);
+    for (int i = 0; i < list.count(); ++i) {
+        InsertionLocation location = list.at(i);
+        if (location.isValid() && location.fileName() == fileName) {
+            return location;
+            break;
+        }
+    }
+
+    // ...failed, so return location at end of file
+    CppRefactoringFilePtr file = refactoring.file(fileName);
+    const QTextDocument *doc = file->document();
+    int pos = qMax(0, doc->characterCount() - 1);
+
+    //TODO watch for matching namespace
+    //TODO watch for moc-includes
+
+    unsigned line, column;
+    file->lineAndColumn(pos, &line, &column);
+    return InsertionLocation(fileName, QLatin1String("\n\n"), QLatin1String("\n"), line, column);
+}
+
 inline bool isQtStringLiteral(const QByteArray &id)
 {
     return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral";
@@ -2549,11 +2580,6 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe
                             if (func->isSignal())
                                 return;
 
-                            InsertDefOperation *op = 0;
-                            bool isHeaderFile = false;
-                            const QString cppFileName = correspondingHeaderOrSource(
-                                        interface->fileName(), &isHeaderFile);
-
                             // Check if there is already a definition
                             CppTools::SymbolFinder symbolFinder;
                             if (symbolFinder.findMatchingDefinition(decl, interface->snapshot(),
@@ -2561,18 +2587,21 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe
                                 return;
                             }
 
+                            InsertDefOperation *op = 0;
+                            bool isHeaderFile = false;
+                            const QString cppFileName = correspondingHeaderOrSource(
+                                        interface->fileName(), &isHeaderFile);
+                            InsertionLocation loc;
+
                             // 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;
-                                    }
+                                loc = insertLocationForMethodDefinition(decl, refactoring,
+                                                                        cppFileName);
+                                if (loc.isValid()) {
+                                    op = new InsertDefOperation(interface, decl, loc,
+                                                                DefPosImplementationFile);
+                                    result.append(CppQuickFixOperation::Ptr(op));
                                 }
                             }
 
@@ -2581,8 +2610,8 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe
                             unsigned line, column;
                             if (func->enclosingClass() == 0) {
                                 file->lineAndColumn(file->endOf(simpleDecl), &line, &column);
-                                InsertionLocation loc(interface->fileName(), QLatin1String(""),
-                                                      QLatin1String(""), line, column);
+                                loc = InsertionLocation(interface->fileName(), QLatin1String(""),
+                                                        QLatin1String(""), line, column);
                                 op = new InsertDefOperation(interface, decl, loc,
                                                             DefPosInsideClass, true);
                                 result.append(CppQuickFixOperation::Ptr(op));
@@ -2590,25 +2619,19 @@ void InsertDefFromDecl::match(const CppQuickFixInterface &interface, QuickFixOpe
                             }
 
                             // 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;
-                                }
+                            CppRefactoringChanges refactoring(interface->snapshot());
+                            loc = insertLocationForMethodDefinition(decl, refactoring,
+                                                                    interface->fileName());
+                            if (loc.isValid()) {
+                                op = new InsertDefOperation(interface, decl, loc,
+                                                            DefPosOutsideClass);
+                                result.append(CppQuickFixOperation::Ptr(op));
                             }
 
                             // Insert Position: Inside Class
                             file->lineAndColumn(file->endOf(simpleDecl), &line, &column);
-                            InsertionLocation loc(interface->fileName(), QLatin1String(""),
-                                                  QLatin1String(""), line, column);
+                            loc = InsertionLocation(interface->fileName(), QLatin1String(""),
+                                                    QLatin1String(""), line, column);
                             op = new InsertDefOperation(interface, decl, loc, DefPosInsideClass);
                             result.append(CppQuickFixOperation::Ptr(op));
                             return;
@@ -3726,32 +3749,25 @@ public:
     {
         CppRefactoringChanges refactoring(snapshot());
         CppRefactoringFilePtr fromFile = refactoring.file(m_headerFileName);
-        CppRefactoringFilePtr toFile;
-        int insertPos = 0;
-        Scope *scopeAtInsertPos = 0;
+        CppRefactoringFilePtr toFile = (m_type == MoveOutside) ? fromFile
+                                                               : refactoring.file(m_cppFileName);
 
         // 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());
-        }
+        InsertionLocation l = insertLocationForMethodDefinition(m_func, refactoring,
+                                                                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());
 
         // construct definition
         const QString funcDec = getDefinitionSignature(assistInterface(), m_func, toFile,
                                                        scopeAtInsertPos);
-        QString funcDef = QLatin1String("\n") + funcDec;
+        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) + QLatin1String("\n");
-        if (m_cppFileName.isEmpty() || !m_insideHeader)
-            funcDef = QLatin1String("\n") + funcDef;
+        funcDef += fromFile->textOf(startPosition, endPosition);
+        funcDef += suffix;
 
         // insert definition at new position
         ChangeSet cppChanges;
-- 
GitLab