diff options
Diffstat (limited to 'src/plugins/cppeditor/quickfixes/converttocamelcase.cpp')
-rw-r--r-- | src/plugins/cppeditor/quickfixes/converttocamelcase.cpp | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/src/plugins/cppeditor/quickfixes/converttocamelcase.cpp b/src/plugins/cppeditor/quickfixes/converttocamelcase.cpp new file mode 100644 index 0000000000..c597a29c11 --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/converttocamelcase.cpp @@ -0,0 +1,184 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "converttocamelcase.h" + +#include "../cppeditortr.h" +#include "../cppeditorwidget.h" +#include "../cpprefactoringchanges.h" +#include "cppquickfix.h" + +#ifdef WITH_TESTS +#include "cppquickfix_test.h" +#include <QtTest> +#endif + +using namespace CPlusPlus; +using namespace Utils; + +namespace CppEditor::Internal { +namespace { + +class ConvertToCamelCaseOp: public CppQuickFixOperation +{ +public: + ConvertToCamelCaseOp(const CppQuickFixInterface &interface, const QString &name, + const AST *nameAst, bool test) + : CppQuickFixOperation(interface, -1) + , m_name(name) + , m_nameAst(nameAst) + , m_isAllUpper(name.isUpper()) + , m_test(test) + { + setDescription(Tr::tr("Convert to Camel Case")); + } + + static bool isConvertibleUnderscore(const QString &name, int pos) + { + return name.at(pos) == QLatin1Char('_') && name.at(pos+1).isLetter() + && !(pos == 1 && name.at(0) == QLatin1Char('m')); + } + +private: + void perform() override + { + QString newName = m_isAllUpper ? m_name.toLower() : m_name; + for (int i = 1; i < newName.length(); ++i) { + const QChar c = newName.at(i); + if (c.isUpper() && m_isAllUpper) { + newName[i] = c.toLower(); + } else if (i < newName.length() - 1 && isConvertibleUnderscore(newName, i)) { + newName.remove(i, 1); + newName[i] = newName.at(i).toUpper(); + } + } + if (m_test) + currentFile()->apply(ChangeSet::makeReplace(currentFile()->range(m_nameAst), newName)); + else + editor()->renameUsages(newName); + } + + const QString m_name; + const AST * const m_nameAst; + const bool m_isAllUpper; + const bool m_test; +}; + +/*! + Turns "an_example_symbol" into "anExampleSymbol" and + "AN_EXAMPLE_SYMBOL" into "AnExampleSymbol". + + Activates on: identifiers +*/ +class ConvertToCamelCase : public CppQuickFixFactory +{ +public: + ConvertToCamelCase(bool test = false) : m_test(test) {} + +#ifdef WITH_TESTS + static QObject *createTest(); +#endif + +private: + void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override + { + const QList<AST *> &path = interface.path(); + + if (path.isEmpty()) + return; + + AST * const ast = path.last(); + const Name *name = nullptr; + const AST *astForName = nullptr; + if (const NameAST * const nameAst = ast->asName()) { + if (nameAst->name && nameAst->name->asNameId()) { + astForName = nameAst; + name = nameAst->name; + } + } else if (const NamespaceAST * const namespaceAst = ast->asNamespace()) { + astForName = namespaceAst; + name = namespaceAst->symbol->name(); + } + + if (!name) + return; + + QString nameString = QString::fromUtf8(name->identifier()->chars()); + if (nameString.length() < 3) + return; + for (int i = 1; i < nameString.length() - 1; ++i) { + if (ConvertToCamelCaseOp::isConvertibleUnderscore(nameString, i)) { + result << new ConvertToCamelCaseOp(interface, nameString, astForName, m_test); + return; + } + } + } + + const bool m_test; +}; + +#ifdef WITH_TESTS +using namespace Tests; + +class ConvertToCamelCaseTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data() + { + QTest::addColumn<QByteArray>("original"); + QTest::addColumn<QByteArray>("expected"); + + using QByteArray = QByteArray; + + QTest::newRow("convert to camel case: normal") + << QByteArray("void @lower_case_function();\n") + << QByteArray("void lowerCaseFunction();\n"); + QTest::newRow("convert to camel case: already camel case") + << QByteArray("void @camelCaseFunction();\n") + << QByteArray(); + QTest::newRow("convert to camel case: no underscores (lower case)") + << QByteArray("void @lowercasefunction();\n") + << QByteArray(); + QTest::newRow("convert to camel case: no underscores (upper case)") + << QByteArray("void @UPPERCASEFUNCTION();\n") + << QByteArray(); + QTest::newRow("convert to camel case: non-applicable underscore") + << QByteArray("void @m_a_member;\n") + << QByteArray("void m_aMember;\n"); + QTest::newRow("convert to camel case: upper case") + << QByteArray("void @UPPER_CASE_FUNCTION();\n") + << QByteArray("void upperCaseFunction();\n"); + QTest::newRow("convert to camel case: partially camel case already") + << QByteArray("void mixed@_andCamelCase();\n") + << QByteArray("void mixedAndCamelCase();\n"); + QTest::newRow("convert to camel case: wild mix") + << QByteArray("void @WhAt_TODO_hErE();\n") + << QByteArray("void WhAtTODOHErE();\n"); + } + + void test() + { + QFETCH(QByteArray, original); + QFETCH(QByteArray, expected); + ConvertToCamelCase factory(true); + QuickFixOperationTest(singleDocument(original, expected), &factory); + } +}; + +QObject *ConvertToCamelCase::createTest() { return new ConvertToCamelCaseTest; } + +#endif // WITH_TESTS +} // namespace + +void registerConvertToCamelCaseQuickfix() +{ + CppQuickFixFactory::registerFactory<ConvertToCamelCase>(); +} + +} // namespace CppEditor::Internal + +#ifdef WITH_TESTS +#include <converttocamelcase.moc> +#endif |