CppEditor: Move ConvertToCamelCase quickfix to its own files

Change-Id: Ic45ad1732fbf1253088db1b8adb93d752ed7cd1c
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Christian Kandeler
2024-05-17 10:44:27 +02:00
parent a6758ab76c
commit 3c18fe3cb1
7 changed files with 206 additions and 147 deletions

View File

@@ -101,6 +101,7 @@ add_qtc_plugin(CppEditor
quickfixes/convertnumericliteral.cpp quickfixes/convertnumericliteral.h quickfixes/convertnumericliteral.cpp quickfixes/convertnumericliteral.h
quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
quickfixes/convertstringliteral.cpp quickfixes/convertstringliteral.h quickfixes/convertstringliteral.cpp quickfixes/convertstringliteral.h
quickfixes/converttocamelcase.cpp quickfixes/converttocamelcase.h
quickfixes/converttometamethodcall.cpp quickfixes/converttometamethodcall.h quickfixes/converttometamethodcall.cpp quickfixes/converttometamethodcall.h
quickfixes/cppcodegenerationquickfixes.cpp quickfixes/cppcodegenerationquickfixes.h quickfixes/cppcodegenerationquickfixes.cpp quickfixes/cppcodegenerationquickfixes.h
quickfixes/cppinsertvirtualmethods.cpp quickfixes/cppinsertvirtualmethods.h quickfixes/cppinsertvirtualmethods.cpp quickfixes/cppinsertvirtualmethods.h

View File

@@ -233,6 +233,8 @@ QtcPlugin {
"convertqt4connect.h", "convertqt4connect.h",
"convertstringliteral.cpp", "convertstringliteral.cpp",
"convertstringliteral.h", "convertstringliteral.h",
"converttocamelcase.cpp",
"converttocamelcase.h",
"converttometamethodcall.cpp", "converttometamethodcall.cpp",
"converttometamethodcall.h", "converttometamethodcall.h",
"cppcodegenerationquickfixes.cpp", "cppcodegenerationquickfixes.cpp",

View File

@@ -0,0 +1,191 @@
// 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
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
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) {
ChangeSet changeSet;
changeSet.replace(currentFile->range(m_nameAst), newName);
currentFile->setChangeSet(changeSet);
currentFile->apply();
} 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

View File

@@ -0,0 +1,8 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
namespace CppEditor::Internal {
void registerConvertToCamelCaseQuickfix();
} // namespace CppEditor::Internal

View File

@@ -269,39 +269,6 @@ void QuickfixTest::testGeneric_data()
<< CppQuickFixFactoryPtr(new ReformatPointerDeclaration) << CppQuickFixFactoryPtr(new ReformatPointerDeclaration)
<< _("char@*s;") << _("char@*s;")
<< _("char *s;"); << _("char *s;");
QTest::newRow("convert to camel case: normal")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void @lower_case_function();\n")
<< _("void lowerCaseFunction();\n");
QTest::newRow("convert to camel case: already camel case")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void @camelCaseFunction();\n")
<< _();
QTest::newRow("convert to camel case: no underscores (lower case)")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void @lowercasefunction();\n")
<< _();
QTest::newRow("convert to camel case: no underscores (upper case)")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void @UPPERCASEFUNCTION();\n")
<< _();
QTest::newRow("convert to camel case: non-applicable underscore")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void @m_a_member;\n")
<< _("void m_aMember;\n");
QTest::newRow("convert to camel case: upper case")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void @UPPER_CASE_FUNCTION();\n")
<< _("void upperCaseFunction();\n");
QTest::newRow("convert to camel case: partially camel case already")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void mixed@_andCamelCase();\n")
<< _("void mixedAndCamelCase();\n");
QTest::newRow("convert to camel case: wild mix")
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
<< _("void @WhAt_TODO_hErE();\n")
<< _("void WhAtTODOHErE();\n");
} }
void QuickfixTest::testGeneric() void QuickfixTest::testGeneric()

View File

@@ -11,19 +11,19 @@
#include "../cpppointerdeclarationformatter.h" #include "../cpppointerdeclarationformatter.h"
#include "../cpprefactoringchanges.h" #include "../cpprefactoringchanges.h"
#include "../cpptoolsreuse.h" #include "../cpptoolsreuse.h"
#include "../insertionpointlocator.h"
#include "assigntolocalvariable.h" #include "assigntolocalvariable.h"
#include "bringidentifierintoscope.h" #include "bringidentifierintoscope.h"
#include "completeswitchstatement.h" #include "completeswitchstatement.h"
#include "convertfromandtopointer.h" #include "convertfromandtopointer.h"
#include "convertnumericliteral.h" #include "convertnumericliteral.h"
#include "convertqt4connect.h"
#include "convertstringliteral.h"
#include "converttocamelcase.h"
#include "converttometamethodcall.h" #include "converttometamethodcall.h"
#include "cppcodegenerationquickfixes.h" #include "cppcodegenerationquickfixes.h"
#include "cppinsertvirtualmethods.h" #include "cppinsertvirtualmethods.h"
#include "cppquickfixassistant.h" #include "cppquickfixassistant.h"
#include "cppquickfixhelpers.h" #include "cppquickfixhelpers.h"
#include "convertqt4connect.h"
#include "convertstringliteral.h"
#include "createdeclarationfromuse.h" #include "createdeclarationfromuse.h"
#include "extractfunction.h" #include "extractfunction.h"
#include "extractliteralasparameter.h" #include "extractliteralasparameter.h"
@@ -91,9 +91,7 @@
#include <QTextCursor> #include <QTextCursor>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <bitset>
#include <cctype> #include <cctype>
#include <limits>
using namespace CPlusPlus; using namespace CPlusPlus;
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -137,96 +135,6 @@ namespace Internal {
namespace { 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"));
}
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
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) {
ChangeSet changeSet;
changeSet.replace(currentFile->range(m_nameAst), newName);
currentFile->setChangeSet(changeSet);
currentFile->apply();
} else {
editor()->renameUsages(newName);
}
}
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:
const QString m_name;
const AST * const m_nameAst;
const bool m_isAllUpper;
const bool m_test;
};
} // anonymous namespace
void ConvertToCamelCase::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
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;
}
}
}
namespace {
class RearrangeParamDeclarationListOp: public CppQuickFixOperation class RearrangeParamDeclarationListOp: public CppQuickFixOperation
{ {
public: public:
@@ -476,8 +384,6 @@ void ExtraRefactoringOperations::doMatch(const CppQuickFixInterface &interface,
void createCppQuickFixes() void createCppQuickFixes()
{ {
new ConvertToCamelCase;
new RearrangeParamDeclarationList; new RearrangeParamDeclarationList;
new ReformatPointerDeclaration; new ReformatPointerDeclaration;
@@ -504,6 +410,7 @@ void createCppQuickFixes()
registerConvertToMetaMethodCallQuickfix(); registerConvertToMetaMethodCallQuickfix();
registerSplitSimpleDeclarationQuickfix(); registerSplitSimpleDeclarationQuickfix();
registerConvertNumericLiteralQuickfix(); registerConvertNumericLiteralQuickfix();
registerConvertToCamelCaseQuickfix();
new ExtraRefactoringOperations; new ExtraRefactoringOperations;
} }

View File

@@ -28,23 +28,6 @@ public:
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
}; };
/*!
Turns "an_example_symbol" into "anExampleSymbol" and
"AN_EXAMPLE_SYMBOL" into "AnExampleSymbol".
Activates on: identifiers
*/
class ConvertToCamelCase : public CppQuickFixFactory
{
public:
ConvertToCamelCase(bool test = false) : CppQuickFixFactory(), m_test(test) {}
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
private:
const bool m_test;
};
/*! /*!
Switches places of the parameter declaration under cursor Switches places of the parameter declaration under cursor
with the next or the previous one in the parameter declaration list with the next or the previous one in the parameter declaration list