From 2e8d471c3b0fba5043a85f014039c253714d9127 Mon Sep 17 00:00:00 2001 From: Lorenz Haas Date: Sun, 7 Apr 2013 18:46:51 +0200 Subject: [PATCH] CppEditor: Fix insert position of AddIncludeForUndefinedIdentifier If there are no includes, add new include at the top of file but skip possible comments at the beginning. Task-number: QTCREATORBUG-8799 Change-Id: Ie2be644f6ad0a948cf3d8700efa00087753d9863 Reviewed-by: Nikolai Kosjar --- src/plugins/cppeditor/cppplugin.h | 5 + src/plugins/cppeditor/cppquickfix_test.cpp | 162 +++++++++++++++++++++ src/plugins/cppeditor/cppquickfixes.cpp | 67 +++++++-- 3 files changed, 222 insertions(+), 12 deletions(-) diff --git a/src/plugins/cppeditor/cppplugin.h b/src/plugins/cppeditor/cppplugin.h index 5dac683e45e..705ca705a92 100644 --- a/src/plugins/cppeditor/cppplugin.h +++ b/src/plugins/cppeditor/cppplugin.h @@ -125,6 +125,11 @@ private slots: void test_quickfix_InsertDefFromDecl_headerSource_namespace1(); void test_quickfix_InsertDefFromDecl_headerSource_namespace2(); void test_quickfix_InsertDefFromDecl_freeFunction(); + + void test_quickfix_AddIncludeForUndefinedIdentifier_normal(); + void test_quickfix_AddIncludeForUndefinedIdentifier_noinclude(); + void test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment01(); + void test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment02(); #endif // WITH_TESTS private: diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index 57113bb10e3..3cf0f80c3d2 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -881,3 +881,165 @@ void CppPlugin::test_quickfix_InsertDefFromDecl_freeFunction() TestCase data(original, expected); data.run(&factory); } + +/// Check normal add include if there is already a include +void CppPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_normal() +{ + 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 \"someheader.h\"\n" + "\n" + "void f()\n" + "{\n" + " Fo@o foo;\n" + "}\n" + ; + expected = + "#include \"someheader.h\"\n" + "#include \"file.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 if no include is present +void CppPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noinclude() +{ + 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" + ; + expected = + "#include \"file.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 if no include is present with comment on top +void CppPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment01() +{ + 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 = + "\n" + "// comment\n" + "\n" + "void f()\n" + "{\n" + " Fo@o foo;\n" + "}\n" + ; + expected = + "\n" + "// comment\n" + "\n" + "#include \"file.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 if no include is present with comment on top +void CppPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment02() +{ + 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 = + "\n" + "/*\n" + " comment\n" + " */\n" + "\n" + "void f()\n" + "{\n" + " Fo@o foo;\n" + "}\n" + ; + expected = + "\n" + "/*\n" + " comment\n" + " */\n" + "\n" + "#include \"file.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); +} diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index bf9fa326c8e..2274fa6c0f5 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -1606,20 +1606,63 @@ public: CppRefactoringChanges refactoring(snapshot()); CppRefactoringFilePtr file = refactoring.file(fileName()); - // find location of last include in file QList includes = file->cppDocument()->includes(); - unsigned lastIncludeLine = 0; - foreach (const Document::Include &include, includes) { - if (include.line() > lastIncludeLine) - lastIncludeLine = include.line(); - } + 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(); - // add include - const int insertPos = file->position(lastIncludeLine + 1, 1) - 1; - ChangeSet changes; - changes.insert(insertPos, QLatin1String("\n#include ") + m_include); - file->setChangeSet(changes); - file->apply(); + // 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; + 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(); + } } private: