Git/DiffEditor: Fix staging added/removed lines separately

Fixes: QTCREATORBUG-23243
Change-Id: Ice19e1c778aabd9cb1b9fe0681234073de85cfcb
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: André Hartmann <aha_1980@gmx.de>
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
Jarek Kobus
2019-11-20 16:01:17 +01:00
parent 27d503558f
commit 3b9ce98865
7 changed files with 213 additions and 80 deletions

View File

@@ -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;
}

View File

@@ -1431,11 +1431,14 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch()
}
}
using ListOfStringPairs = QList<QPair<QString, QString>>;
void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch_data()
{
QTest::addColumn<ChunkData>("chunk");
QTest::addColumn<QStringList>("rightLines");
QTest::addColumn<ListOfStringPairs>("rows");
QTest::addColumn<ChunkSelection>("selection");
QTest::addColumn<bool>("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);
}
}

View File

@@ -26,6 +26,7 @@
#include "diffutils.h"
#include <texteditor/fontsettings.h>
#include <utils/algorithm.h>
#include <utils/differ.h>
#include <QFutureInterfaceBase>
@@ -37,6 +38,11 @@ using namespace Utils;
namespace DiffEditor {
int ChunkSelection::selectedRowsCount() const
{
return Utils::toSet(leftSelection).unite(Utils::toSet(rightSelection)).count();
}
static QList<TextLineData> assemblyRows(const QList<TextLineData> &lines,
const QMap<int, int> &lineSpans)
{

View File

@@ -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<int> &left, const QList<int> &right)
: leftSelection(left), rightSelection(right) {}
bool isNull() const { return leftSelection.isEmpty() && rightSelection.isEmpty(); }
int selectedRowsCount() const;
QList<int> leftSelection;
QList<int> rightSelection;
};
class DIFFEDITOR_EXPORT FileData {

View File

@@ -512,7 +512,11 @@ void SideDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
? chunkRowForBlockNumber(endBlockNumber)
: chunkRowsCountForBlockNumber(blockNumber);
const ChunkSelection selection(selectionStart, selectionEnd - selectionStart + 1);
QList<int> rows;
for (int i = selectionStart; i <= selectionEnd; ++i)
rows.append(i);
const ChunkSelection selection(rows, rows);
emit contextMenuRequested(menu, fileIndexForBlockNumber(blockNumber),
chunkIndexForBlockNumber(blockNumber),

View File

@@ -194,8 +194,7 @@ void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
const ChunkData chunkData = m_controller.chunkData(fileIndex, chunkIndex);
int selectionStart = -1;
int selectionEnd = -1;
QList<int> 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);

View File

@@ -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,