diff --git a/kdevplatform/language/duchain/stringhelpers.h b/kdevplatform/language/duchain/stringhelpers.h --- a/kdevplatform/language/duchain/stringhelpers.h +++ b/kdevplatform/language/duchain/stringhelpers.h @@ -97,6 +97,11 @@ */ int KDEVPLATFORMLANGUAGE_EXPORT strip( const QByteArray& str, QByteArray& from ); +/** + * Removes all whitespace from the string + */ +QString KDEVPLATFORMLANGUAGE_EXPORT removeWhitespace( const QString& str ); + /** * Can be used to iterate through different kinds of parameters, for example template-parameters */ diff --git a/kdevplatform/language/duchain/stringhelpers.cpp b/kdevplatform/language/duchain/stringhelpers.cpp --- a/kdevplatform/language/duchain/stringhelpers.cpp +++ b/kdevplatform/language/duchain/stringhelpers.cpp @@ -508,6 +508,12 @@ return formatComment_impl(comment); } +QString removeWhitespace(const QString& str) +{ + return str.simplified().remove(QLatin1Char(' ')); +} + + ParamIterator::~ParamIterator() = default; diff --git a/plugins/clang/codecompletion/context.cpp b/plugins/clang/codecompletion/context.cpp --- a/plugins/clang/codecompletion/context.cpp +++ b/plugins/clang/codecompletion/context.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #include "../duchain/navigationwidget.h" #include "../clangsettings/clangsettingsmanager.h" +#include #include #include @@ -338,7 +340,20 @@ void execute(KTextEditor::View* view, const KTextEditor::Range& word) override { - view->document()->replaceText(word, m_replacement); + auto* const document = view->document(); + + // try and replace leading typed text that match the proposed implementation + const QString leading = document->line(word.end().line()).left(word.end().column()); + const QString leadingNoSpace = removeWhitespace(leading); + if (!leadingNoSpace.isEmpty() && (removeWhitespace(m_display).startsWith(leadingNoSpace) + || removeWhitespace(m_replacement).startsWith(leadingNoSpace))) { + const int removeSize = leading.end() - std::find_if_not(leading.begin(), leading.end(), + [](QChar c){ return c.isSpace(); }); + const KTextEditor::Cursor newStart = {word.end().line(), word.end().column() - removeSize}; + document->replaceText({newStart, word.end()}, m_replacement); + } else { + document->replaceText(word, m_replacement); + } } }; diff --git a/plugins/clang/tests/test_codecompletion.cpp b/plugins/clang/tests/test_codecompletion.cpp --- a/plugins/clang/tests/test_codecompletion.cpp +++ b/plugins/clang/tests/test_codecompletion.cpp @@ -1387,6 +1387,30 @@ << CompletionItems({5, 0}, {"Test", "Test::bar(B a)"}) << "Test::bar(B a)" << "template class X, typename B>\nclass Test {\npublic:\nvoid bar(B a);\n};\ntemplate class X, typename B> void Test::bar(B a)\n{\n}\n"; + + QTest::newRow("replace-leading-return-type") + << "void foo(int x);\nvoid " + << CompletionItems({1, 5}, {"foo(int x)"}) + << "foo(int x)" + << "void foo(int x);\nvoid foo(int x)\n{\n}\n"; + + QTest::newRow("replace-leading-function-name") + << "void foo(int x);\nfoo" + << CompletionItems({1, 3}, {"foo(int x)"}) + << "foo(int x)" + << "void foo(int x);\nvoid foo(int x)\n{\n}\n"; + + QTest::newRow("replace-leading-with-class-method") + << "class Foo { int bar(int x); };\nint " + << CompletionItems({1, 4}, {"Foo", "Foo::bar(int x)"}) + << "Foo::bar(int x)" + << "class Foo { int bar(int x); };\nint Foo::bar(int x)\n{\n}\n"; + + QTest::newRow("replace-leading-whitespace-mismatch") + << "class Foo { int** bar(int x); };\nint * *" + << CompletionItems({1, 7}, {"** Foo::bar(int x)"}) + << "** Foo::bar(int x)" + << "class Foo { int** bar(int x); };\nint ** Foo::bar(int x)\n{\n}\n"; } void TestCodeCompletion::testIgnoreGccBuiltins()