diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index 8f8ddbc3a3f..d5bcf52bead 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -161,6 +161,10 @@ private slots: void test_quickfix_InsertDeclFromDef(); void test_quickfix_AddIncludeForUndefinedIdentifier_normal(); + void test_quickfix_AddIncludeForUndefinedIdentifier_ignoremoc(); + void test_quickfix_AddIncludeForUndefinedIdentifier_sortingTop(); + void test_quickfix_AddIncludeForUndefinedIdentifier_sortingMiddle(); + void test_quickfix_AddIncludeForUndefinedIdentifier_sortingBottom(); void test_quickfix_AddIncludeForUndefinedIdentifier_noinclude(); void test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment01(); void test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment02(); diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index dd88624b26d..6b618f290b9 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -969,13 +969,176 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_normal() "}\n" ; expected = + "#include \"file.h\"\n" "#include \"someheader.h\"\n" + "\n" + "void f()\n" + "{\n" + " Foo foo;\n" + "}\n" + "\n" + ; + testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifier factory; + TestCase data(testFiles); + data.run(&factory); +} + +/// Check add include, ignoring any moc includes. +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_ignoremoc() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + // Header File + original = "class Foo {};\n"; + expected = original + "\n"; + testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + + // Source File + original = + "void f()\n" + "{\n" + " Fo@o foo;\n" + "}\n" + "#include \"file.moc\";\n" + ; + expected = "#include \"file.h\"\n" "\n" "void f()\n" "{\n" " Foo foo;\n" "}\n" + "#include \"file.moc\";\n" + "\n" + ; + testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifier factory; + TestCase data(testFiles); + data.run(&factory); +} + +/// Check add include sorting top +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingTop() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + // Header File + original = "class Foo {};\n"; + expected = original + "\n"; + testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + + // Source File + original = + "#include \"y.h\"\n" + "#include \"z.h\"\n" + "void f()\n" + "{\n" + " Fo@o foo;\n" + "}\n" + "#include \"file.moc\";\n" + ; + expected = + "#include \"file.h\"\n" + "#include \"y.h\"\n" + "#include \"z.h\"\n" + "void f()\n" + "{\n" + " Foo foo;\n" + "}\n" + "#include \"file.moc\";\n" + "\n" + ; + testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifier factory; + TestCase data(testFiles); + data.run(&factory); +} + +/// Check add include sorting middle +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingMiddle() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + // Header File + original = "class Foo {};\n"; + expected = original + "\n"; + testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + + // Source File + original = + "#include \"a.h\"\n" + "#include \"z.h\"\n" + "void f()\n" + "{\n" + " Fo@o foo;\n" + "}\n" + "#include \"file.moc\";\n" + ; + expected = + "#include \"a.h\"\n" + "#include \"file.h\"\n" + "#include \"z.h\"\n" + "void f()\n" + "{\n" + " Foo foo;\n" + "}\n" + "#include \"file.moc\";\n" + "\n" + ; + testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifier factory; + TestCase data(testFiles); + data.run(&factory); +} + + + +/// Check add include sorting bottom +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingBottom() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + // Header File + original = "class Foo {};\n"; + expected = original + "\n"; + testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + + // Source File + original = + "#include \"a.h\"\n" + "#include \"b.h\"\n" + "void f()\n" + "{\n" + " Fo@o foo;\n" + "}\n" + "#include \"file.moc\";\n" + ; + expected = + "#include \"a.h\"\n" + "#include \"b.h\"\n" + "#include \"file.h\"\n" + "void f()\n" + "{\n" + " Foo foo;\n" + "}\n" + "#include \"file.moc\";\n" "\n" ; testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 5d9503398c4..d319932d4e7 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -1730,62 +1730,80 @@ public: CppRefactoringFilePtr file = refactoring.file(fileName()); QList includes = file->cppDocument()->includes(); - 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; - } - - 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; + if (!includes.isEmpty()) { + QHash includePositions; foreach (const Document::Include &include, includes) { - if (include.line() > lastIncludeLine) - lastIncludeLine = include.line(); + if (include.fileName().endsWith(QLatin1String(".moc"))) + continue; + includePositions.insert(include.fileName(), 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(); + if (!includePositions.isEmpty()) { + const QString include = m_include.mid(1, m_include.length() - 2); + QList keys = includePositions.keys(); + keys << include; + qSort(keys); + const int pos = keys.indexOf(include); + + ChangeSet changes; + if (pos + 1 != keys.count()) { + const int insertPos = qMax(0, file->position( + includePositions.value(keys.at(pos + 1)), 1)); + changes.insert(insertPos, + QLatin1String("#include ") + m_include + QLatin1String("\n")); + + } else { + const int insertPos = qMax(0, file->position(includePositions.value( + keys.at(pos - 1)) + 1, 1) - 1); + changes.insert(insertPos, QLatin1String("\n#include ") + m_include); + } + file->setChangeSet(changes); + file->apply(); + return; + } } + + // No includes or no matching include, 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; + } + + 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(); } private: