diff --git a/src/plugins/diffeditor/diffeditordocument.cpp b/src/plugins/diffeditor/diffeditordocument.cpp index 529dac382e8..f68143bbbaf 100644 --- a/src/plugins/diffeditor/diffeditordocument.cpp +++ b/src/plugins/diffeditor/diffeditordocument.cpp @@ -76,6 +76,14 @@ DiffEditorController *DiffEditorDocument::controller() const return m_controller; } +static void appendRow(ChunkData *chunk, const RowData &row) +{ + const bool isSeparator = row.leftLine.textLineType == TextLineData::Separator + && row.rightLine.textLineType == TextLineData::Separator; + if (!isSeparator) + chunk->rows.append(row); +} + ChunkData DiffEditorDocument::filterChunk(const ChunkData &data, const ChunkSelection &selection, bool revert) { @@ -83,27 +91,47 @@ ChunkData DiffEditorDocument::filterChunk(const ChunkData &data, return data; ChunkData chunk(data); - for (int i = 0; i < chunk.rows.count(); ++i) { - RowData &row = chunk.rows[i]; - if (i < selection.startRow || i >= selection.startRow + selection.selectedRowsCount) { + chunk.rows.clear(); + for (int i = 0; i < data.rows.count(); ++i) { + RowData row = data.rows[i]; + const bool isLeftSelected = selection.leftSelection.contains(i); + const bool isRightSelected = selection.rightSelection.contains(i); + + if (isLeftSelected || isRightSelected) { + if (row.equal || (isLeftSelected && isRightSelected)) { + appendRow(&chunk, row); + } else if (isLeftSelected) { + RowData newRow = row; + + row.rightLine = TextLineData(TextLineData::Separator); + appendRow(&chunk, row); + + if (revert) { + newRow.leftLine = newRow.rightLine; + newRow.equal = true; + appendRow(&chunk, newRow); + } + } else { // isRightSelected + if (!revert) { + RowData newRow = row; + newRow.rightLine = newRow.leftLine; + newRow.equal = true; + appendRow(&chunk, newRow); + } + + row.leftLine = TextLineData(TextLineData::Separator); + appendRow(&chunk, row); + } + } else { if (revert) row.leftLine = row.rightLine; else row.rightLine = row.leftLine; row.equal = true; + appendRow(&chunk, row); } } - for (int i = 0; i < chunk.rows.count(); ) { - const RowData &row = chunk.rows[i]; - const bool isSeparator = row.leftLine.textLineType == TextLineData::Separator - && row.rightLine.textLineType == TextLineData::Separator; - if (isSeparator) - chunk.rows.removeAt(i); - else - ++i; - } - return chunk; } diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp index 652c6e9d14f..a4372ae0b1e 100644 --- a/src/plugins/diffeditor/diffeditorplugin.cpp +++ b/src/plugins/diffeditor/diffeditorplugin.cpp @@ -1431,11 +1431,14 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch() } } +using ListOfStringPairs = QList>; + void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch_data() { QTest::addColumn("chunk"); - QTest::addColumn("rightLines"); + QTest::addColumn("rows"); QTest::addColumn("selection"); + QTest::addColumn("revert"); auto createChunk = []() { ChunkData chunk; @@ -1455,29 +1458,29 @@ void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch_data() chunk->rows.append(row); }; ChunkData chunk; - QStringList rightLines; + ListOfStringPairs rows; chunk = createChunk(); appendRow(&chunk, "A", "A"); // 50 appendRow(&chunk, "", "B"); // 51 + appendRow(&chunk, "C", "C"); // 52 - rightLines = QStringList { - "A", - "B", - "C" + rows = ListOfStringPairs { + {"A", "A"}, + {"", "B"}, + {"C", "C"} }; - QTest::newRow("one added") << chunk << rightLines << ChunkSelection(); + QTest::newRow("one added") << chunk << rows << ChunkSelection() << false; chunk = createChunk(); appendRow(&chunk, "A", "A"); // 50 appendRow(&chunk, "B", ""); // 51 - appendRow(&chunk, "C", "C"); // 52 - rightLines = QStringList { - "A", - "", - "C" + rows = ListOfStringPairs { + {"A", "A"}, + {"B", ""}, + {"C", "C"} }; - QTest::newRow("one removed") << chunk << rightLines << ChunkSelection(); + QTest::newRow("one removed") << chunk << rows << ChunkSelection() << false; chunk = createChunk(); appendRow(&chunk, "A", "A"); // 50 @@ -1486,26 +1489,26 @@ void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch_data() appendRow(&chunk, "", "D"); // 53 + appendRow(&chunk, "", "E"); // 54 appendRow(&chunk, "F", "F"); // 55 - rightLines = QStringList { - "A", - "C", - "D", - "F", + rows = ListOfStringPairs { + {"A", "A"}, + {"", "C"}, + {"", "D"}, + {"F", "F"} }; - QTest::newRow("stage selected added") << chunk << rightLines << ChunkSelection(2, 2); + QTest::newRow("stage selected added") << chunk << rows << ChunkSelection({2, 3}, {2, 3}) << false; chunk = createChunk(); appendRow(&chunk, "A", "A"); // 50 appendRow(&chunk, "", "B"); // 51 + appendRow(&chunk, "C", "D"); // 52 appendRow(&chunk, "E", "E"); // 53 - rightLines = QStringList { - "A", - "B", - "C", - "E", + rows = ListOfStringPairs { + {"A", "A"}, + {"", "B"}, + {"C", "C"}, + {"E", "E"} }; - QTest::newRow("stage selected added keep changed") << chunk << rightLines << ChunkSelection(1, 1); + QTest::newRow("stage selected added keep changed") << chunk << rows << ChunkSelection({1}, {1}) << false; chunk = createChunk(); appendRow(&chunk, "A", "A"); // 50 @@ -1514,15 +1517,15 @@ void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch_data() appendRow(&chunk, "D", ""); // 53 - appendRow(&chunk, "E", ""); // 54 appendRow(&chunk, "F", "F"); // 55 - rightLines = QStringList { - "A", - "B", - "", - "", - "E", - "F", + rows = ListOfStringPairs { + {"A", "A"}, + {"B", "B"}, + {"C", ""}, + {"D", ""}, + {"E", "E"}, + {"F", "F"} }; - QTest::newRow("stage selected removed") << chunk << rightLines << ChunkSelection(2, 2); + QTest::newRow("stage selected removed") << chunk << rows << ChunkSelection({2, 3}, {2, 3}) << false; chunk = createChunk(); appendRow(&chunk, "A", "A"); // 50 @@ -1531,37 +1534,132 @@ void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch_data() appendRow(&chunk, "", "D"); // 53 + appendRow(&chunk, "", "E"); // 54 appendRow(&chunk, "F", "F"); // 55 - rightLines = QStringList { - "A", - "B", - "", - "D", - "F", + rows = ListOfStringPairs { + {"A", "A"}, + {"B", "B"}, + {"C", ""}, + {"", "D"}, + {"F", "F"} }; - QTest::newRow("stage selected added/removed") << chunk << rightLines << ChunkSelection(2, 2); + QTest::newRow("stage selected added/removed") << chunk << rows << ChunkSelection({2, 3}, {2, 3}) << false; chunk = createChunk(); appendRow(&chunk, "A", "A"); // 50 appendRow(&chunk, "B", "C"); // 51 -/+ appendRow(&chunk, "D", "D"); // 52 - rightLines = QStringList { - "A", - "C", - "D", + rows = ListOfStringPairs { + {"A", "A"}, + {"B", "C"}, + {"D", "D"} }; - QTest::newRow("stage modified row") << chunk << rightLines << ChunkSelection(1, 1); + QTest::newRow("stage modified row") << chunk << rows << ChunkSelection({1}, {1}) << false; + + chunk = createChunk(); + appendRow(&chunk, "A", "A"); // 50 + appendRow(&chunk, "B", "C"); // 51 -/+ + appendRow(&chunk, "D", "D"); // 52 + rows = ListOfStringPairs { + {"A", "A"}, + {"B", "C"}, + {"D", "D"} + }; + QTest::newRow("stage modified and unmodified rows") << chunk << rows << ChunkSelection({0, 1, 2}, {0, 1, 2}) << false; + + chunk = createChunk(); + appendRow(&chunk, "A", "A"); // 50 + appendRow(&chunk, "B", "C"); // 51 -/+ + appendRow(&chunk, "D", "D"); // 52 + rows = ListOfStringPairs { + {"A", "A"}, + {"B", "C"}, + {"D", "D"} + }; + QTest::newRow("stage unmodified left rows") << chunk << rows << ChunkSelection({0, 1, 2}, {1}) << false; + + chunk = createChunk(); + appendRow(&chunk, "A", "A"); // 50 + appendRow(&chunk, "B", "C"); // 51 -/+ + appendRow(&chunk, "D", "D"); // 52 + rows = ListOfStringPairs { + {"A", "A"}, + {"B", "C"}, + {"D", "D"} + }; + QTest::newRow("stage unmodified right rows") << chunk << rows << ChunkSelection({1}, {0, 1, 2}) << false; + + chunk = createChunk(); + appendRow(&chunk, "A", "A"); // 50 + appendRow(&chunk, "B", "C"); // 51 -/+ + appendRow(&chunk, "D", "D"); // 52 + rows = ListOfStringPairs { + {"A", "A"}, + {"B", ""}, + {"D", "D"} + }; + QTest::newRow("stage left only") << chunk << rows << ChunkSelection({1}, {}) << false; + + chunk = createChunk(); + appendRow(&chunk, "A", "A"); // 50 + appendRow(&chunk, "B", "C"); // 51 -/+ + appendRow(&chunk, "D", "D"); // 52 + rows = ListOfStringPairs { + {"A", "A"}, + {"B", "B"}, + {"", "C"}, + {"D", "D"} + }; + QTest::newRow("stage right only") << chunk << rows << ChunkSelection({}, {1}) << false; + + chunk = createChunk(); + appendRow(&chunk, "A", "A"); // 50 + appendRow(&chunk, "B", "C"); // 51 -/+ + appendRow(&chunk, "D", "D"); // 52 + rows = ListOfStringPairs { + {"A", "A"}, + {"B", "C"}, + {"D", "D"} + }; + QTest::newRow("stage modified row and revert") << chunk << rows << ChunkSelection({1}, {1}) << true; + + chunk = createChunk(); + appendRow(&chunk, "A", "A"); // 50 + appendRow(&chunk, "B", "C"); // 51 -/+ + appendRow(&chunk, "D", "D"); // 52 + rows = ListOfStringPairs { + {"A", "A"}, + {"B", ""}, + {"C", "C"}, + {"D", "D"} + }; + // symmetric to: "stage right only" + QTest::newRow("stage left only and revert") << chunk << rows << ChunkSelection({1}, {}) << true; + + chunk = createChunk(); + appendRow(&chunk, "A", "A"); // 50 + appendRow(&chunk, "B", "C"); // 51 -/+ + appendRow(&chunk, "D", "D"); // 52 + rows = ListOfStringPairs { + {"A", "A"}, + {"", "C"}, + {"D", "D"} + }; + // symmetric to: "stage left only" + QTest::newRow("stage right only and revert") << chunk << rows << ChunkSelection({}, {1}) << true; + } void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch() { QFETCH(ChunkData, chunk); - QFETCH(QStringList, rightLines); + QFETCH(ListOfStringPairs, rows); QFETCH(ChunkSelection, selection); + QFETCH(bool, revert); - ChunkData result = DiffEditorDocument::filterChunk(chunk, selection, false); - QCOMPARE(result.rows.size(), rightLines.size()); - for (int i = 0; i < rightLines.size(); ++i) { - QCOMPARE(result.rows.at(i).rightLine.text, rightLines.at(i)); + const ChunkData result = DiffEditorDocument::filterChunk(chunk, selection, revert); + QCOMPARE(result.rows.size(), rows.size()); + for (int i = 0; i < rows.size(); ++i) { + QCOMPARE(result.rows.at(i).leftLine.text, rows.at(i).first); + QCOMPARE(result.rows.at(i).rightLine.text, rows.at(i).second); } } diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp index e233173629f..6c024ebd01a 100644 --- a/src/plugins/diffeditor/diffutils.cpp +++ b/src/plugins/diffeditor/diffutils.cpp @@ -26,6 +26,7 @@ #include "diffutils.h" #include +#include #include #include @@ -37,6 +38,11 @@ using namespace Utils; namespace DiffEditor { +int ChunkSelection::selectedRowsCount() const +{ + return Utils::toSet(leftSelection).unite(Utils::toSet(rightSelection)).count(); +} + static QList assemblyRows(const QList &lines, const QMap &lineSpans) { diff --git a/src/plugins/diffeditor/diffutils.h b/src/plugins/diffeditor/diffutils.h index 47ae8c126ac..d4f05af7f7d 100644 --- a/src/plugins/diffeditor/diffutils.h +++ b/src/plugins/diffeditor/diffutils.h @@ -102,10 +102,12 @@ public: class DIFFEDITOR_EXPORT ChunkSelection { public: ChunkSelection() {} - ChunkSelection(int s, int c) : startRow(s), selectedRowsCount(c) {} - bool isNull() const { return selectedRowsCount <= 0; } - int startRow = -1; - int selectedRowsCount = 0; + ChunkSelection(const QList &left, const QList &right) + : leftSelection(left), rightSelection(right) {} + bool isNull() const { return leftSelection.isEmpty() && rightSelection.isEmpty(); } + int selectedRowsCount() const; + QList leftSelection; + QList rightSelection; }; class DIFFEDITOR_EXPORT FileData { diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp index ffe4b2bc14f..274241d4265 100644 --- a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp +++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp @@ -512,7 +512,11 @@ void SideDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e) ? chunkRowForBlockNumber(endBlockNumber) : chunkRowsCountForBlockNumber(blockNumber); - const ChunkSelection selection(selectionStart, selectionEnd - selectionStart + 1); + QList rows; + for (int i = selectionStart; i <= selectionEnd; ++i) + rows.append(i); + + const ChunkSelection selection(rows, rows); emit contextMenuRequested(menu, fileIndexForBlockNumber(blockNumber), chunkIndexForBlockNumber(blockNumber), diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp index a383a55c15f..f5ef0ad8eef 100644 --- a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp +++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp @@ -194,8 +194,7 @@ void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e) const ChunkData chunkData = m_controller.chunkData(fileIndex, chunkIndex); - int selectionStart = -1; - int selectionEnd = -1; + QList leftSelection, rightSelection; for (int i = startBlockNumber; i <= endBlockNumber; ++i) { const int currentFileIndex = fileIndexForBlockNumber(i); @@ -214,18 +213,14 @@ void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e) const int leftRow = m_leftLineNumbers.value(i, qMakePair(-1, -1)).second; const int rightRow = m_rightLineNumbers.value(i, qMakePair(-1, -1)).second; - const int row = leftRow >= 0 ? leftRow : rightRow; - if (row < 0) - continue; - - if (selectionStart < 0 || selectionStart > row) - selectionStart = row; - if (selectionEnd < 0 || selectionEnd < row) - selectionEnd = row; + if (leftRow >= 0) + leftSelection.append(leftRow); + if (rightRow >= 0) + rightSelection.append(rightRow); } - const ChunkSelection selection(selectionStart, selectionEnd - selectionStart + 1); + const ChunkSelection selection(leftSelection, rightSelection); addContextMenuActions(menu, fileIndexForBlockNumber(blockNumber), chunkIndexForBlockNumber(blockNumber), selection); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index c4758d64cb8..74a05760ddc 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -875,7 +875,7 @@ void GitClient::chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex stageChunk(diffController, fileIndex, chunkIndex, DiffEditorController::NoOption, DiffEditor::ChunkSelection()); }); - QAction *stageLinesAction = menu->addAction(tr("Stage %n Line(s)", "", selection.selectedRowsCount)); + QAction *stageLinesAction = menu->addAction(tr("Stage Selection (%n Lines)", "", selection.selectedRowsCount())); connect(stageLinesAction, &QAction::triggered, this, [stageChunk, diffController, fileIndex, chunkIndex, selection]() { stageChunk(diffController, fileIndex, chunkIndex, @@ -887,7 +887,7 @@ void GitClient::chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex stageChunk(diffController, fileIndex, chunkIndex, DiffEditorController::Revert, DiffEditor::ChunkSelection()); }); - QAction *unstageLinesAction = menu->addAction(tr("Unstage %n Line(s)", "", selection.selectedRowsCount)); + QAction *unstageLinesAction = menu->addAction(tr("Unstage Selection (%n Lines)", "", selection.selectedRowsCount())); connect(unstageLinesAction, &QAction::triggered, this, [stageChunk, diffController, fileIndex, chunkIndex, selection]() { stageChunk(diffController, fileIndex, chunkIndex,