diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt index fab94d2f462..ad30808f8af 100644 --- a/src/plugins/cppeditor/CMakeLists.txt +++ b/src/plugins/cppeditor/CMakeLists.txt @@ -123,6 +123,7 @@ add_qtc_plugin(CppEditor quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h quickfixes/rearrangeparamdeclarationlist.cpp quickfixes/rearrangeparamdeclarationlist.h + quickfixes/reformatpointerdeclaration.cpp quickfixes/reformatpointerdeclaration.h quickfixes/removeusingnamespace.cpp quickfixes/removeusingnamespace.h quickfixes/rewritecomment.cpp quickfixes/rewritecomment.cpp quickfixes/rewritecontrolstatements.cpp quickfixes/rewritecontrolstatements.h diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index bad08132a75..97fa40e22ce 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -275,6 +275,8 @@ QtcPlugin { "movefunctiondefinition.h", "rearrangeparamdeclarationlist.cpp", "rearrangeparamdeclarationlist.h", + "reformatpointerdeclaration.cpp", + "reformatpointerdeclaration.h", "removeusingnamespace.cpp", "removeusingnamespace.h", "rewritecomment.cpp", diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 9d0b1615830..9ad640e7494 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -43,7 +43,6 @@ #include "functionutils.h" #include "includeutils.h" #include "projectinfo_test.h" -#include "quickfixes/cppquickfix_test.h" #include "symbolsearcher_test.h" #include "typehierarchybuilder_test.h" #endif @@ -513,7 +512,6 @@ void CppEditorPlugin::registerTests() addTest(); addTest(); addTest(); - addTest(); addTest(); addTest(); #endif diff --git a/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp b/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp index efb2d7fa644..17e2fbf0aef 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/quickfixes/cppquickfix_test.cpp @@ -257,29 +257,6 @@ typedef QSharedPointer CppQuickFixFactoryPtr; namespace CppEditor::Internal::Tests { -void QuickfixTest::testGeneric_data() -{ - QTest::addColumn("factory"); - QTest::addColumn("original"); - QTest::addColumn("expected"); - - // Check: Just a basic test since the main functionality is tested in - // cpppointerdeclarationformatter_test.cpp - QTest::newRow("ReformatPointerDeclaration") - << CppQuickFixFactoryPtr(new ReformatPointerDeclaration) - << _("char@*s;") - << _("char *s;"); -} - -void QuickfixTest::testGeneric() -{ - QFETCH(CppQuickFixFactoryPtr, factory); - QFETCH(QByteArray, original); - QFETCH(QByteArray, expected); - - QuickFixOperationTest(singleDocument(original, expected), factory.data()); -} - class CppCodeStyleSettingsChanger { public: CppCodeStyleSettingsChanger(const CppCodeStyleSettings &settings); diff --git a/src/plugins/cppeditor/quickfixes/cppquickfix_test.h b/src/plugins/cppeditor/quickfixes/cppquickfix_test.h index 0bead0a5b1e..b1990629706 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfix_test.h +++ b/src/plugins/cppeditor/quickfixes/cppquickfix_test.h @@ -89,15 +89,6 @@ public: QList singleDocument(const QByteArray &original, const QByteArray &expected); -class QuickfixTest : public QObject -{ - Q_OBJECT - -private slots: - void testGeneric_data(); - void testGeneric(); -}; - } // namespace Tests } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp b/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp index 74ad56d509f..8fcb17ba1c4 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp +++ b/src/plugins/cppeditor/quickfixes/cppquickfixes.cpp @@ -32,6 +32,7 @@ #include "moveclasstoownfile.h" #include "movefunctiondefinition.h" #include "rearrangeparamdeclarationlist.h" +#include "reformatpointerdeclaration.h" #include "removeusingnamespace.h" #include "rewritecomment.h" #include "rewritecontrolstatements.h" @@ -136,125 +137,6 @@ namespace Internal { namespace { -class ReformatPointerDeclarationOp: public CppQuickFixOperation -{ -public: - ReformatPointerDeclarationOp(const CppQuickFixInterface &interface, const ChangeSet change) - : CppQuickFixOperation(interface) - , m_change(change) - { - QString description; - if (m_change.operationList().size() == 1) { - description = Tr::tr( - "Reformat to \"%1\"").arg(m_change.operationList().constFirst().text()); - } else { // > 1 - description = Tr::tr("Reformat Pointers or References"); - } - setDescription(description); - } - - void perform() override - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); - currentFile->setChangeSet(m_change); - currentFile->apply(); - } - -private: - ChangeSet m_change; -}; - -/// Filter the results of ASTPath. -/// The resulting list contains the supported AST types only once. -/// For this, the results of ASTPath are iterated in reverse order. -class ReformatPointerDeclarationASTPathResultsFilter -{ -public: - QList filter(const QList &astPathList) - { - QList filtered; - - for (int i = astPathList.size() - 1; i >= 0; --i) { - AST *ast = astPathList.at(i); - - if (!m_hasSimpleDeclaration && ast->asSimpleDeclaration()) { - m_hasSimpleDeclaration = true; - filtered.append(ast); - } else if (!m_hasFunctionDefinition && ast->asFunctionDefinition()) { - m_hasFunctionDefinition = true; - filtered.append(ast); - } else if (!m_hasParameterDeclaration && ast->asParameterDeclaration()) { - m_hasParameterDeclaration = true; - filtered.append(ast); - } else if (!m_hasIfStatement && ast->asIfStatement()) { - m_hasIfStatement = true; - filtered.append(ast); - } else if (!m_hasWhileStatement && ast->asWhileStatement()) { - m_hasWhileStatement = true; - filtered.append(ast); - } else if (!m_hasForStatement && ast->asForStatement()) { - m_hasForStatement = true; - filtered.append(ast); - } else if (!m_hasForeachStatement && ast->asForeachStatement()) { - m_hasForeachStatement = true; - filtered.append(ast); - } - } - - return filtered; - } - -private: - bool m_hasSimpleDeclaration = false; - bool m_hasFunctionDefinition = false; - bool m_hasParameterDeclaration = false; - bool m_hasIfStatement = false; - bool m_hasWhileStatement = false; - bool m_hasForStatement = false; - bool m_hasForeachStatement = false; -}; - -} // anonymous namespace - -void ReformatPointerDeclaration::doMatch(const CppQuickFixInterface &interface, - QuickFixOperations &result) -{ - const QList &path = interface.path(); - CppRefactoringFilePtr file = interface.currentFile(); - - Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview(); - overview.showArgumentNames = true; - overview.showReturnTypes = true; - - const QTextCursor cursor = file->cursor(); - ChangeSet change; - PointerDeclarationFormatter formatter(file, overview, - PointerDeclarationFormatter::RespectCursor); - - if (cursor.hasSelection()) { - // This will no work always as expected since this function is only called if - // interface-path() is not empty. If the user selects the whole document via - // ctrl-a and there is an empty line in the end, then the cursor is not on - // any AST and therefore no quick fix will be triggered. - change = formatter.format(file->cppDocument()->translationUnit()->ast()); - if (!change.isEmpty()) - result << new ReformatPointerDeclarationOp(interface, change); - } else { - const QList suitableASTs - = ReformatPointerDeclarationASTPathResultsFilter().filter(path); - for (AST *ast : suitableASTs) { - change = formatter.format(ast); - if (!change.isEmpty()) { - result << new ReformatPointerDeclarationOp(interface, change); - return; - } - } - } -} - -namespace { - class ApplyDeclDefLinkOperation : public CppQuickFixOperation { public: @@ -304,8 +186,6 @@ void ExtraRefactoringOperations::doMatch(const CppQuickFixInterface &interface, void createCppQuickFixes() { - new ReformatPointerDeclaration; - new ApplyDeclDefLinkChanges; registerInsertVirtualMethodsQuickfix(); @@ -331,6 +211,7 @@ void createCppQuickFixes() registerConvertNumericLiteralQuickfix(); registerConvertToCamelCaseQuickfix(); registerRearrangeParamDeclarationListQuickfix(); + registerReformatPointerDeclarationQuickfix(); new ExtraRefactoringOperations; } diff --git a/src/plugins/cppeditor/quickfixes/cppquickfixes.h b/src/plugins/cppeditor/quickfixes/cppquickfixes.h index 2b5c241aa11..89761dcfd3c 100644 --- a/src/plugins/cppeditor/quickfixes/cppquickfixes.h +++ b/src/plugins/cppeditor/quickfixes/cppquickfixes.h @@ -28,21 +28,6 @@ public: void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; }; -/*! - Reformats a pointer, reference or rvalue reference type/declaration. - - Works also with selections (except when the cursor is not on any AST). - - Activates on: simple declarations, parameters and return types of function - declarations and definitions, control flow statements (if, - while, for, foreach) with declarations. -*/ -class ReformatPointerDeclaration : public CppQuickFixFactory -{ -public: - void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override; -}; - /*! Applies function signature changes */ diff --git a/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.cpp b/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.cpp new file mode 100644 index 00000000000..7f93e3d0e0d --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.cpp @@ -0,0 +1,189 @@ +// 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 "reformatpointerdeclaration.h" + +#include "../cppcodestylesettings.h" +#include "../cppeditortr.h" +#include "../cpppointerdeclarationformatter.h" +#include "../cpprefactoringchanges.h" +#include "cppquickfix.h" + +#include + +#ifdef WITH_TESTS +#include "cppquickfix_test.h" +#include +#endif + +using namespace CPlusPlus; +using namespace Utils; + +namespace CppEditor::Internal { +namespace { + +class ReformatPointerDeclarationOp: public CppQuickFixOperation +{ +public: + ReformatPointerDeclarationOp(const CppQuickFixInterface &interface, const ChangeSet change) + : CppQuickFixOperation(interface) + , m_change(change) + { + QString description; + if (m_change.operationList().size() == 1) { + description = Tr::tr( + "Reformat to \"%1\"").arg(m_change.operationList().constFirst().text()); + } else { // > 1 + description = Tr::tr("Reformat Pointers or References"); + } + setDescription(description); + } + + void perform() override + { + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath()); + currentFile->setChangeSet(m_change); + currentFile->apply(); + } + +private: + ChangeSet m_change; +}; + +/// Filter the results of ASTPath. +/// The resulting list contains the supported AST types only once. +/// For this, the results of ASTPath are iterated in reverse order. +class ReformatPointerDeclarationASTPathResultsFilter +{ +public: + QList filter(const QList &astPathList) + { + QList filtered; + + for (int i = astPathList.size() - 1; i >= 0; --i) { + AST *ast = astPathList.at(i); + + if (!m_hasSimpleDeclaration && ast->asSimpleDeclaration()) { + m_hasSimpleDeclaration = true; + filtered.append(ast); + } else if (!m_hasFunctionDefinition && ast->asFunctionDefinition()) { + m_hasFunctionDefinition = true; + filtered.append(ast); + } else if (!m_hasParameterDeclaration && ast->asParameterDeclaration()) { + m_hasParameterDeclaration = true; + filtered.append(ast); + } else if (!m_hasIfStatement && ast->asIfStatement()) { + m_hasIfStatement = true; + filtered.append(ast); + } else if (!m_hasWhileStatement && ast->asWhileStatement()) { + m_hasWhileStatement = true; + filtered.append(ast); + } else if (!m_hasForStatement && ast->asForStatement()) { + m_hasForStatement = true; + filtered.append(ast); + } else if (!m_hasForeachStatement && ast->asForeachStatement()) { + m_hasForeachStatement = true; + filtered.append(ast); + } + } + + return filtered; + } + +private: + bool m_hasSimpleDeclaration = false; + bool m_hasFunctionDefinition = false; + bool m_hasParameterDeclaration = false; + bool m_hasIfStatement = false; + bool m_hasWhileStatement = false; + bool m_hasForStatement = false; + bool m_hasForeachStatement = false; +}; + +/*! + Reformats a pointer, reference or rvalue reference type/declaration. + + Works also with selections (except when the cursor is not on any AST). + + Activates on: simple declarations, parameters and return types of function + declarations and definitions, control flow statements (if, + while, for, foreach) with declarations. +*/ +class ReformatPointerDeclaration : public CppQuickFixFactory +{ +#ifdef WITH_TESTS +public: + static QObject *createTest(); +#endif + +private: + void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override + { + const QList &path = interface.path(); + CppRefactoringFilePtr file = interface.currentFile(); + + Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview(); + overview.showArgumentNames = true; + overview.showReturnTypes = true; + + const QTextCursor cursor = file->cursor(); + ChangeSet change; + PointerDeclarationFormatter formatter(file, overview, + PointerDeclarationFormatter::RespectCursor); + + if (cursor.hasSelection()) { + // This will no work always as expected since this function is only called if + // interface-path() is not empty. If the user selects the whole document via + // ctrl-a and there is an empty line in the end, then the cursor is not on + // any AST and therefore no quick fix will be triggered. + change = formatter.format(file->cppDocument()->translationUnit()->ast()); + if (!change.isEmpty()) + result << new ReformatPointerDeclarationOp(interface, change); + } else { + const QList suitableASTs + = ReformatPointerDeclarationASTPathResultsFilter().filter(path); + for (AST *ast : suitableASTs) { + change = formatter.format(ast); + if (!change.isEmpty()) { + result << new ReformatPointerDeclarationOp(interface, change); + return; + } + } + } + } +}; + +#ifdef WITH_TESTS +using namespace Tests; + +class ReformatPointerDeclarationTest : public QObject +{ + Q_OBJECT + +private slots: + void test() + { + // Check: Just a basic test since the main functionality is tested in + // cpppointerdeclarationformatter_test.cpp + ReformatPointerDeclaration factory; + QuickFixOperationTest( + singleDocument(QByteArray("char@*s;"), QByteArray("char *s;")), &factory); + } +}; + +QObject *ReformatPointerDeclaration::createTest() { return new ReformatPointerDeclarationTest; } + +#endif // WITH_TESTS +} + +void registerReformatPointerDeclarationQuickfix() +{ + CppQuickFixFactory::registerFactory(); +} + +} // namespace CppEditor::Internal + +#ifdef WITH_TESTS +#include +#endif diff --git a/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.h b/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.h new file mode 100644 index 00000000000..89bbe5cd778 --- /dev/null +++ b/src/plugins/cppeditor/quickfixes/reformatpointerdeclaration.h @@ -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 registerReformatPointerDeclarationQuickfix(); +} // namespace CppEditor::Internal