diff --git a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp index 6cce4ffeb15..a810deb5f9b 100644 --- a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp +++ b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp @@ -108,10 +108,9 @@ void openEditorAt(const ClangBackEnd::SourceLocationContainer &location) int(location.column() - 1)); } -void applyFixit(const ClangBackEnd::SourceLocationContainer &location, - const QVector &fixits) +void applyFixit(const QVector &fixits) { - ClangCodeModel::ClangFixItOperation operation(location.filePath(), Utf8String(), fixits); + ClangCodeModel::ClangFixItOperation operation(Utf8String(), fixits); operation.perform(); } @@ -146,7 +145,7 @@ QWidget *createDiagnosticLabel(const ClangBackEnd::DiagnosticContainer &diagnost label->setTextFormat(Qt::RichText); QObject::connect(label, &QLabel::linkActivated, [location, fixits](const QString &action) { if (action == QLatin1String(LINK_ACTION_APPLY_FIX)) - applyFixit(location, fixits); + applyFixit(fixits); else openEditorAt(location); diff --git a/src/plugins/clangcodemodel/clangfixitoperation.cpp b/src/plugins/clangcodemodel/clangfixitoperation.cpp index 431286c4751..f064b8d2778 100644 --- a/src/plugins/clangcodemodel/clangfixitoperation.cpp +++ b/src/plugins/clangcodemodel/clangfixitoperation.cpp @@ -27,16 +27,21 @@ #include +#include + #include namespace ClangCodeModel { -ClangFixItOperation::ClangFixItOperation(const Utf8String &filePath, - const Utf8String &fixItText, - const QVector &fixItContainers) - : filePath(filePath), - fixItText(fixItText), - fixItContainers(fixItContainers) +using FileToFixits = QMap>; +using FileToFixitsIterator = QMapIterator>; +using RefactoringFilePtr = QSharedPointer; + +ClangFixItOperation::ClangFixItOperation( + const Utf8String &fixItText, + const QVector &fixItContainers) + : fixItText(fixItText) + , fixItContainers(fixItContainers) { } @@ -50,20 +55,56 @@ QString ClangCodeModel::ClangFixItOperation::description() const return QStringLiteral("Apply Fix: ") + fixItText.toString(); } +static FileToFixits fixitsPerFile(const QVector &fixItContainers) +{ + FileToFixits mapping; + + for (const auto &fixItContainer : fixItContainers) { + const QString rangeStartFilePath = fixItContainer.range().start().filePath().toString(); + const QString rangeEndFilePath = fixItContainer.range().end().filePath().toString(); + QTC_CHECK(rangeStartFilePath == rangeEndFilePath); + mapping[rangeStartFilePath].append(fixItContainer); + } + + return mapping; +} + void ClangFixItOperation::perform() { const TextEditor::RefactoringChanges refactoringChanges; - refactoringFile = refactoringChanges.file(filePath.toString()); - refactoringFile->setChangeSet(changeSet()); - refactoringFile->apply(); + const FileToFixits fileToFixIts = fixitsPerFile(fixItContainers); + + FileToFixitsIterator i(fileToFixIts); + while (i.hasNext()) { + i.next(); + const QString filePath = i.key(); + const QVector fixits = i.value(); + + RefactoringFilePtr refactoringFile = refactoringChanges.file(filePath); + refactoringFiles.append(refactoringFile); + + applyFixitsToFile(*refactoringFile, fixits); + } } -QString ClangFixItOperation::refactoringFileContent_forTestOnly() const +QString ClangFixItOperation::firstRefactoringFileContent_forTestOnly() const { - return refactoringFile->document()->toPlainText(); + return refactoringFiles.first()->document()->toPlainText(); } -Utils::ChangeSet ClangFixItOperation::changeSet() const +void ClangFixItOperation::applyFixitsToFile( + TextEditor::RefactoringFile &refactoringFile, + const QVector fixItContainers) +{ + const Utils::ChangeSet changeSet = toChangeSet(refactoringFile, fixItContainers); + + refactoringFile.setChangeSet(changeSet); + refactoringFile.apply(); +} + +Utils::ChangeSet ClangFixItOperation::toChangeSet( + TextEditor::RefactoringFile &refactoringFile, + const QVector fixItContainers) const { Utils::ChangeSet changeSet; @@ -71,8 +112,8 @@ Utils::ChangeSet ClangFixItOperation::changeSet() const const auto range = fixItContainer.range(); const auto start = range.start(); const auto end = range.end(); - changeSet.replace(refactoringFile->position(start.line(), start.column()), - refactoringFile->position(end.line(), end.column()), + changeSet.replace(refactoringFile.position(start.line(), start.column()), + refactoringFile.position(end.line(), end.column()), fixItContainer.text()); } diff --git a/src/plugins/clangcodemodel/clangfixitoperation.h b/src/plugins/clangcodemodel/clangfixitoperation.h index 83a8c8ed2ec..e4183451b49 100644 --- a/src/plugins/clangcodemodel/clangfixitoperation.h +++ b/src/plugins/clangcodemodel/clangfixitoperation.h @@ -36,6 +36,7 @@ namespace TextEditor { +class RefactoringChanges; class RefactoringFile; } @@ -44,23 +45,25 @@ namespace ClangCodeModel { class ClangFixItOperation : public TextEditor::QuickFixOperation { public: - ClangFixItOperation(const Utf8String &filePath, - const Utf8String &fixItText, + ClangFixItOperation(const Utf8String &fixItText, const QVector &fixItContainers); int priority() const override; QString description() const override; void perform() override; - QString refactoringFileContent_forTestOnly() const; + QString firstRefactoringFileContent_forTestOnly() const; private: - Utils::ChangeSet changeSet() const; + void applyFixitsToFile(TextEditor::RefactoringFile &refactoringFile, + const QVector fixItContainers); + Utils::ChangeSet toChangeSet( + TextEditor::RefactoringFile &refactoringFile, + const QVector fixItContainers) const; private: - Utf8String filePath; Utf8String fixItText; - QSharedPointer refactoringFile; + QVector> refactoringFiles; QVector fixItContainers; }; diff --git a/src/plugins/clangcodemodel/clangfixitoperationsextractor.cpp b/src/plugins/clangcodemodel/clangfixitoperationsextractor.cpp index 7203e18dcb0..91da3de4e15 100644 --- a/src/plugins/clangcodemodel/clangfixitoperationsextractor.cpp +++ b/src/plugins/clangcodemodel/clangfixitoperationsextractor.cpp @@ -109,16 +109,13 @@ ClangFixItOperationsExtractor::extract(const QString &filePath, int line) } void ClangFixItOperationsExtractor::appendFixitOperation( - const QString &filePath, const QString &diagnosticText, const QVector &fixits) { if (!fixits.isEmpty()) { const QString diagnosticTextTweaked = tweakedDiagnosticText(diagnosticText); TextEditor::QuickFixOperation::Ptr operation( - new ClangFixItOperation(filePath, - diagnosticTextTweaked, - fixits)); + new ClangFixItOperation(diagnosticTextTweaked, fixits)); operations.append(operation); } } @@ -130,7 +127,7 @@ void ClangFixItOperationsExtractor::extractFromDiagnostic( { const QVector fixIts = diagnosticContainer.fixIts(); if (hasFixItAt(fixIts, filePath, line)) { - appendFixitOperation(filePath, diagnosticContainer.text().toString(), fixIts); + appendFixitOperation(diagnosticContainer.text().toString(), fixIts); foreach (const auto &child, diagnosticContainer.children()) extractFromDiagnostic(child, filePath, line); diff --git a/src/plugins/clangcodemodel/clangfixitoperationsextractor.h b/src/plugins/clangcodemodel/clangfixitoperationsextractor.h index 6c49a3da181..f0b1fb83abf 100644 --- a/src/plugins/clangcodemodel/clangfixitoperationsextractor.h +++ b/src/plugins/clangcodemodel/clangfixitoperationsextractor.h @@ -42,8 +42,7 @@ private: void extractFromDiagnostic(const ClangBackEnd::DiagnosticContainer &diagnosticContainer, const QString &filePath, int line); - void appendFixitOperation(const QString &filePath, - const QString &diagnosticText, + void appendFixitOperation(const QString &diagnosticText, const QVector &fixits); private: diff --git a/tests/unit/unittest/clangfixitoperationtest.cpp b/tests/unit/unittest/clangfixitoperationtest.cpp index 397cebebad1..ab01f1b103d 100644 --- a/tests/unit/unittest/clangfixitoperationtest.cpp +++ b/tests/unit/unittest/clangfixitoperationtest.cpp @@ -58,7 +58,7 @@ MATCHER_P(MatchText, expectedText, + " expected text:\n" + PrintToString(expectedText)) { const ::ClangFixItOperation &operation = arg; - QString resultText = operation.refactoringFileContent_forTestOnly(); + QString resultText = operation.firstRefactoringFileContent_forTestOnly(); if (resultText != expectedText) { *result_listener << "\n" << resultText.toUtf8().constData(); @@ -95,7 +95,7 @@ protected: TEST_F(ClangFixItOperation, Description) { - ::ClangFixItOperation operation(semicolonFilePath, diagnosticText, {semicolonFixItContainer}); + ::ClangFixItOperation operation(diagnosticText, {semicolonFixItContainer}); ASSERT_THAT(operation.description(), QStringLiteral("Apply Fix: expected ';' at end of declaration")); @@ -103,7 +103,7 @@ TEST_F(ClangFixItOperation, Description) TEST_F(ClangFixItOperation, AppendSemicolon) { - ::ClangFixItOperation operation(semicolonFilePath, diagnosticText, {semicolonFixItContainer}); + ::ClangFixItOperation operation(diagnosticText, {semicolonFixItContainer}); operation.perform(); @@ -112,7 +112,7 @@ TEST_F(ClangFixItOperation, AppendSemicolon) TEST_F(ClangFixItOperation, ComparisonVersusAssignmentChooseComparison) { - ::ClangFixItOperation operation(compareFilePath, diagnosticText, {compareFixItContainer}); + ::ClangFixItOperation operation(diagnosticText, {compareFixItContainer}); operation.perform(); @@ -121,8 +121,7 @@ TEST_F(ClangFixItOperation, ComparisonVersusAssignmentChooseComparison) TEST_F(ClangFixItOperation, ComparisonVersusAssignmentChooseParentheses) { - ::ClangFixItOperation operation(compareFilePath, - diagnosticText, + ::ClangFixItOperation operation(diagnosticText, {assignmentFixItContainerParenLeft, assignmentFixItContainerParenRight});