forked from qt-creator/qt-creator
DiffEditor: Stage and unstage selected lines for Git
Fixes: QTCREATORBUG-19071 Change-Id: I560ba208e68e477ea865e499847d819cfdfeb6f3 Reviewed-by: Orgad Shaneh <orgads@gmail.com> Reviewed-by: André Hartmann <aha_1980@gmx.de>
This commit is contained in:
committed by
Jarek Kobus
parent
2758682723
commit
1766832918
@@ -71,9 +71,10 @@ bool DiffEditorController::ignoreWhitespace() const
|
||||
}
|
||||
|
||||
QString DiffEditorController::makePatch(int fileIndex, int chunkIndex,
|
||||
const ChunkSelection &selection,
|
||||
PatchOptions options) const
|
||||
{
|
||||
return m_document->makePatch(fileIndex, chunkIndex,
|
||||
return m_document->makePatch(fileIndex, chunkIndex, selection,
|
||||
options & Revert, options & AddPrefix);
|
||||
}
|
||||
|
||||
@@ -143,9 +144,10 @@ void DiffEditorController::reloadFinished(bool success)
|
||||
m_isReloading = false;
|
||||
}
|
||||
|
||||
void DiffEditorController::requestChunkActions(QMenu *menu, int fileIndex, int chunkIndex)
|
||||
void DiffEditorController::requestChunkActions(QMenu *menu, int fileIndex, int chunkIndex,
|
||||
const ChunkSelection &selection)
|
||||
{
|
||||
emit chunkActionsRequested(menu, fileIndex, chunkIndex);
|
||||
emit chunkActionsRequested(menu, fileIndex, chunkIndex, selection);
|
||||
}
|
||||
|
||||
bool DiffEditorController::chunkExists(int fileIndex, int chunkIndex) const
|
||||
|
@@ -38,6 +38,8 @@ namespace DiffEditor {
|
||||
|
||||
namespace Internal { class DiffEditorDocument; }
|
||||
|
||||
class ChunkSelection;
|
||||
|
||||
class DIFFEDITOR_EXPORT DiffEditorController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -58,18 +60,21 @@ public:
|
||||
AddPrefix = 2
|
||||
};
|
||||
Q_DECLARE_FLAGS(PatchOptions, PatchOption)
|
||||
QString makePatch(int fileIndex, int chunkIndex, PatchOptions options) const;
|
||||
QString makePatch(int fileIndex, int chunkIndex, const ChunkSelection &selection,
|
||||
PatchOptions options) const;
|
||||
|
||||
static Core::IDocument *findOrCreateDocument(const QString &vcsId,
|
||||
const QString &displayName);
|
||||
static DiffEditorController *controller(Core::IDocument *document);
|
||||
|
||||
void requestChunkActions(QMenu *menu, int fileIndex, int chunkIndex);
|
||||
void requestChunkActions(QMenu *menu, int fileIndex, int chunkIndex,
|
||||
const ChunkSelection &selection);
|
||||
bool chunkExists(int fileIndex, int chunkIndex) const;
|
||||
Core::IDocument *document() const;
|
||||
|
||||
signals:
|
||||
void chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex);
|
||||
void chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex,
|
||||
const ChunkSelection &selection);
|
||||
|
||||
protected:
|
||||
// reloadFinished() should be called
|
||||
|
@@ -76,7 +76,39 @@ DiffEditorController *DiffEditorDocument::controller() const
|
||||
return m_controller;
|
||||
}
|
||||
|
||||
ChunkData DiffEditorDocument::filterChunk(const ChunkData &data,
|
||||
const ChunkSelection &selection, bool revert)
|
||||
{
|
||||
if (selection.isNull())
|
||||
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) {
|
||||
if (revert)
|
||||
row.leftLine = row.rightLine;
|
||||
else
|
||||
row.rightLine = row.leftLine;
|
||||
row.equal = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QString DiffEditorDocument::makePatch(int fileIndex, int chunkIndex,
|
||||
const ChunkSelection &selection,
|
||||
bool revert, bool addPrefix,
|
||||
const QString &overriddenFileName) const
|
||||
{
|
||||
@@ -90,7 +122,7 @@ QString DiffEditorDocument::makePatch(int fileIndex, int chunkIndex,
|
||||
if (chunkIndex >= fileData.chunks.count())
|
||||
return QString();
|
||||
|
||||
const ChunkData &chunkData = fileData.chunks.at(chunkIndex);
|
||||
const ChunkData chunkData = filterChunk(fileData.chunks.at(chunkIndex), selection, revert);
|
||||
const bool lastChunk = (chunkIndex == fileData.chunks.count() - 1);
|
||||
|
||||
const QString fileName = !overriddenFileName.isEmpty()
|
||||
|
@@ -34,6 +34,7 @@ QT_FORWARD_DECLARE_CLASS(QMenu)
|
||||
namespace DiffEditor {
|
||||
|
||||
class DiffEditorController;
|
||||
class ChunkSelection;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
@@ -52,7 +53,9 @@ public:
|
||||
LoadFailed
|
||||
};
|
||||
|
||||
QString makePatch(int fileIndex, int chunkIndex,
|
||||
static ChunkData filterChunk(const ChunkData &data,
|
||||
const ChunkSelection &selection, bool revert);
|
||||
QString makePatch(int fileIndex, int chunkIndex, const ChunkSelection &selection,
|
||||
bool revert, bool addPrefix = false,
|
||||
const QString &overriddenFileName = QString()) const;
|
||||
|
||||
|
@@ -610,6 +610,7 @@ void DiffEditorPlugin::diffExternalFiles()
|
||||
|
||||
Q_DECLARE_METATYPE(DiffEditor::ChunkData)
|
||||
Q_DECLARE_METATYPE(DiffEditor::FileData)
|
||||
Q_DECLARE_METATYPE(DiffEditor::ChunkSelection)
|
||||
|
||||
static inline QString _(const char *string) { return QString::fromLatin1(string); }
|
||||
|
||||
@@ -1430,6 +1431,140 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch()
|
||||
}
|
||||
}
|
||||
|
||||
void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch_data()
|
||||
{
|
||||
QTest::addColumn<ChunkData>("chunk");
|
||||
QTest::addColumn<QStringList>("rightLines");
|
||||
QTest::addColumn<ChunkSelection>("selection");
|
||||
|
||||
auto createChunk = []() {
|
||||
ChunkData chunk;
|
||||
chunk.contextInfo = "void DiffEditor::ctor()";
|
||||
chunk.contextChunk = false;
|
||||
chunk.leftStartingLineNumber = 49;
|
||||
chunk.rightStartingLineNumber = 49;
|
||||
return chunk;
|
||||
};
|
||||
auto appendRow = [](ChunkData *chunk, const QString &left, const QString &right) {
|
||||
RowData row;
|
||||
row.equal = (left == right);
|
||||
row.leftLine.text = left;
|
||||
row.leftLine.textLineType = left.isEmpty() ? TextLineData::Separator : TextLineData::TextLine;
|
||||
row.rightLine.text = right;
|
||||
row.rightLine.textLineType = right.isEmpty() ? TextLineData::Separator : TextLineData::TextLine;
|
||||
chunk->rows.append(row);
|
||||
};
|
||||
ChunkData chunk;
|
||||
QStringList rightLines;
|
||||
|
||||
chunk = createChunk();
|
||||
appendRow(&chunk, "A", "A"); // 50
|
||||
appendRow(&chunk, "", "B"); // 51 +
|
||||
appendRow(&chunk, "C", "C"); // 52
|
||||
rightLines = QStringList {
|
||||
"A",
|
||||
"B",
|
||||
"C"
|
||||
};
|
||||
QTest::newRow("one added") << chunk << rightLines << ChunkSelection();
|
||||
|
||||
chunk = createChunk();
|
||||
appendRow(&chunk, "A", "A"); // 50
|
||||
appendRow(&chunk, "B", ""); // 51 -
|
||||
appendRow(&chunk, "C", "C"); // 52
|
||||
rightLines = QStringList {
|
||||
"A",
|
||||
"",
|
||||
"C"
|
||||
};
|
||||
QTest::newRow("one removed") << chunk << rightLines << ChunkSelection();
|
||||
|
||||
chunk = createChunk();
|
||||
appendRow(&chunk, "A", "A"); // 50
|
||||
appendRow(&chunk, "", "B"); // 51
|
||||
appendRow(&chunk, "", "C"); // 52 +
|
||||
appendRow(&chunk, "", "D"); // 53 +
|
||||
appendRow(&chunk, "", "E"); // 54
|
||||
appendRow(&chunk, "F", "F"); // 55
|
||||
rightLines = QStringList {
|
||||
"A",
|
||||
"C",
|
||||
"D",
|
||||
"F",
|
||||
};
|
||||
QTest::newRow("stage selected added") << chunk << rightLines << ChunkSelection(2, 2);
|
||||
|
||||
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",
|
||||
};
|
||||
QTest::newRow("stage selected added keep changed") << chunk << rightLines << ChunkSelection(1, 1);
|
||||
|
||||
chunk = createChunk();
|
||||
appendRow(&chunk, "A", "A"); // 50
|
||||
appendRow(&chunk, "B", ""); // 51
|
||||
appendRow(&chunk, "C", ""); // 52 -
|
||||
appendRow(&chunk, "D", ""); // 53 -
|
||||
appendRow(&chunk, "E", ""); // 54
|
||||
appendRow(&chunk, "F", "F"); // 55
|
||||
rightLines = QStringList {
|
||||
"A",
|
||||
"B",
|
||||
"",
|
||||
"",
|
||||
"E",
|
||||
"F",
|
||||
};
|
||||
QTest::newRow("stage selected removed") << chunk << rightLines << ChunkSelection(2, 2);
|
||||
|
||||
chunk = createChunk();
|
||||
appendRow(&chunk, "A", "A"); // 50
|
||||
appendRow(&chunk, "B", ""); // 51
|
||||
appendRow(&chunk, "C", ""); // 52 -
|
||||
appendRow(&chunk, "", "D"); // 53 +
|
||||
appendRow(&chunk, "", "E"); // 54
|
||||
appendRow(&chunk, "F", "F"); // 55
|
||||
rightLines = QStringList {
|
||||
"A",
|
||||
"B",
|
||||
"",
|
||||
"D",
|
||||
"F",
|
||||
};
|
||||
QTest::newRow("stage selected added/removed") << chunk << rightLines << ChunkSelection(2, 2);
|
||||
|
||||
chunk = createChunk();
|
||||
appendRow(&chunk, "A", "A"); // 50
|
||||
appendRow(&chunk, "B", "C"); // 51 -/+
|
||||
appendRow(&chunk, "D", "D"); // 52
|
||||
rightLines = QStringList {
|
||||
"A",
|
||||
"C",
|
||||
"D",
|
||||
};
|
||||
QTest::newRow("stage modified row") << chunk << rightLines << ChunkSelection(1, 1);
|
||||
}
|
||||
|
||||
void DiffEditor::Internal::DiffEditorPlugin::testFilterPatch()
|
||||
{
|
||||
QFETCH(ChunkData, chunk);
|
||||
QFETCH(QStringList, rightLines);
|
||||
QFETCH(ChunkSelection, selection);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WITH_TESTS
|
||||
|
||||
#include "diffeditorplugin.moc"
|
||||
|
@@ -73,6 +73,8 @@ private slots:
|
||||
void testMakePatch();
|
||||
void testReadPatch_data();
|
||||
void testReadPatch();
|
||||
void testFilterPatch_data();
|
||||
void testFilterPatch();
|
||||
#endif // WITH_TESTS
|
||||
};
|
||||
|
||||
|
@@ -158,7 +158,7 @@ void DiffEditorWidgetController::patch(bool revert, int fileIndex, int chunkInde
|
||||
if (patchBehaviour == DiffFileInfo::PatchFile) {
|
||||
const int strip = m_document->baseDirectory().isEmpty() ? -1 : 0;
|
||||
|
||||
const QString patch = m_document->makePatch(fileIndex, chunkIndex, revert);
|
||||
const QString patch = m_document->makePatch(fileIndex, chunkIndex, ChunkSelection(), revert);
|
||||
|
||||
if (patch.isEmpty())
|
||||
return;
|
||||
@@ -183,8 +183,9 @@ void DiffEditorWidgetController::patch(bool revert, int fileIndex, int chunkInde
|
||||
const QString contentsCopyFileName = contentsCopy.fileName();
|
||||
const QString contentsCopyDir = QFileInfo(contentsCopyFileName).absolutePath();
|
||||
|
||||
const QString patch = m_document->makePatch(fileIndex,
|
||||
chunkIndex, revert, false, QFileInfo(contentsCopyFileName).fileName());
|
||||
const QString patch = m_document->makePatch(fileIndex, chunkIndex,
|
||||
ChunkSelection(), revert, false,
|
||||
QFileInfo(contentsCopyFileName).fileName());
|
||||
|
||||
if (patch.isEmpty())
|
||||
return;
|
||||
@@ -244,6 +245,24 @@ bool DiffEditorWidgetController::chunkExists(int fileIndex, int chunkIndex) cons
|
||||
return false;
|
||||
}
|
||||
|
||||
ChunkData DiffEditorWidgetController::chunkData(int fileIndex, int chunkIndex) const
|
||||
{
|
||||
if (!m_document)
|
||||
return ChunkData();
|
||||
|
||||
if (fileIndex < 0 || chunkIndex < 0)
|
||||
return ChunkData();
|
||||
|
||||
if (fileIndex >= m_contextFileData.count())
|
||||
return ChunkData();
|
||||
|
||||
const FileData fileData = m_contextFileData.at(fileIndex);
|
||||
if (chunkIndex >= fileData.chunks.count())
|
||||
return ChunkData();
|
||||
|
||||
return fileData.chunks.at(chunkIndex);
|
||||
}
|
||||
|
||||
bool DiffEditorWidgetController::fileNamesAreDifferent(int fileIndex) const
|
||||
{
|
||||
const FileData fileData = m_contextFileData.at(fileIndex);
|
||||
@@ -268,10 +287,11 @@ void DiffEditorWidgetController::addRevertAction(QMenu *menu, int fileIndex, int
|
||||
revertAction->setEnabled(chunkExists(fileIndex, chunkIndex));
|
||||
}
|
||||
|
||||
void DiffEditorWidgetController::addExtraActions(QMenu *menu, int fileIndex, int chunkIndex)
|
||||
void DiffEditorWidgetController::addExtraActions(QMenu *menu, int fileIndex, int chunkIndex,
|
||||
const ChunkSelection &selection)
|
||||
{
|
||||
if (DiffEditorController *controller = m_document->controller())
|
||||
controller->requestChunkActions(menu, fileIndex, chunkIndex);
|
||||
controller->requestChunkActions(menu, fileIndex, chunkIndex, selection);
|
||||
}
|
||||
|
||||
void DiffEditorWidgetController::sendChunkToCodePaster(int fileIndex, int chunkIndex)
|
||||
@@ -283,7 +303,8 @@ void DiffEditorWidgetController::sendChunkToCodePaster(int fileIndex, int chunkI
|
||||
auto pasteService = ExtensionSystem::PluginManager::getObject<CodePaster::Service>();
|
||||
QTC_ASSERT(pasteService, return);
|
||||
|
||||
const QString patch = m_document->makePatch(fileIndex, chunkIndex, false);
|
||||
const QString patch = m_document->makePatch(fileIndex, chunkIndex,
|
||||
ChunkSelection(), false);
|
||||
|
||||
if (patch.isEmpty())
|
||||
return;
|
||||
|
@@ -39,6 +39,8 @@ namespace Utils { class ProgressIndicator; }
|
||||
|
||||
namespace DiffEditor {
|
||||
|
||||
class ChunkSelection;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class DiffEditorDocument;
|
||||
@@ -58,7 +60,9 @@ public:
|
||||
void addCodePasterAction(QMenu *menu, int fileIndex, int chunkIndex);
|
||||
void addApplyAction(QMenu *menu, int fileIndex, int chunkIndex);
|
||||
void addRevertAction(QMenu *menu, int fileIndex, int chunkIndex);
|
||||
void addExtraActions(QMenu *menu, int fileIndex, int chunkIndex);
|
||||
void addExtraActions(QMenu *menu, int fileIndex, int chunkIndex, const ChunkSelection &selection);
|
||||
|
||||
ChunkData chunkData(int fileIndex, int chunkIndex) const;
|
||||
|
||||
bool m_ignoreCurrentIndexChange = false;
|
||||
QList<FileData> m_contextFileData; // ultimate data to be shown
|
||||
|
@@ -99,6 +99,15 @@ public:
|
||||
bool contextChunk = false;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class DIFFEDITOR_EXPORT FileData {
|
||||
public:
|
||||
enum FileOperation {
|
||||
|
@@ -79,6 +79,8 @@ public:
|
||||
int blockNumberForFileIndex(int fileIndex) const;
|
||||
int fileIndexForBlockNumber(int blockNumber) const;
|
||||
int chunkIndexForBlockNumber(int blockNumber) const;
|
||||
int chunkRowForBlockNumber(int blockNumber) const;
|
||||
int chunkRowsCountForBlockNumber(int blockNumber) const;
|
||||
bool isChunkLine(int blockNumber) const {
|
||||
return m_skippedLines.contains(blockNumber);
|
||||
}
|
||||
@@ -97,7 +99,8 @@ signals:
|
||||
int columnNumber);
|
||||
void contextMenuRequested(QMenu *menu,
|
||||
int diffFileIndex,
|
||||
int chunkIndex);
|
||||
int chunkIndex,
|
||||
const ChunkSelection &selection);
|
||||
void foldChanged(int blockNumber, bool folded);
|
||||
void gotDisplaySettings();
|
||||
void gotFocus();
|
||||
@@ -355,6 +358,40 @@ int SideDiffEditorWidget::chunkIndexForBlockNumber(int blockNumber) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SideDiffEditorWidget::chunkRowForBlockNumber(int blockNumber) const
|
||||
{
|
||||
if (m_chunkInfo.isEmpty())
|
||||
return -1;
|
||||
|
||||
auto it = m_chunkInfo.upperBound(blockNumber);
|
||||
if (it == m_chunkInfo.constBegin())
|
||||
return -1;
|
||||
|
||||
--it;
|
||||
|
||||
if (blockNumber < it.key() + it.value().first)
|
||||
return blockNumber - it.key();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SideDiffEditorWidget::chunkRowsCountForBlockNumber(int blockNumber) const
|
||||
{
|
||||
if (m_chunkInfo.isEmpty())
|
||||
return -1;
|
||||
|
||||
auto it = m_chunkInfo.upperBound(blockNumber);
|
||||
if (it == m_chunkInfo.constBegin())
|
||||
return -1;
|
||||
|
||||
--it;
|
||||
|
||||
if (blockNumber < it.key() + it.value().first)
|
||||
return it.value().first;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SideDiffEditorWidget::clearAll(const QString &message)
|
||||
{
|
||||
setBlockSelection(false);
|
||||
@@ -446,11 +483,40 @@ void SideDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
||||
{
|
||||
QPointer<QMenu> menu = createStandardContextMenu();
|
||||
|
||||
const QTextCursor tc = textCursor();
|
||||
QTextCursor start = tc;
|
||||
start.setPosition(tc.selectionStart());
|
||||
QTextCursor end = tc;
|
||||
end.setPosition(tc.selectionEnd());
|
||||
const int startBlockNumber = start.blockNumber();
|
||||
const int endBlockNumber = end.blockNumber();
|
||||
|
||||
QTextCursor cursor = cursorForPosition(e->pos());
|
||||
const int blockNumber = cursor.blockNumber();
|
||||
|
||||
const int fileIndex = fileIndexForBlockNumber(blockNumber);
|
||||
const int chunkIndex = chunkIndexForBlockNumber(blockNumber);
|
||||
|
||||
const int selectionStartFileIndex = fileIndexForBlockNumber(startBlockNumber);
|
||||
const int selectionStartChunkIndex = chunkIndexForBlockNumber(startBlockNumber);
|
||||
const int selectionEndFileIndex = fileIndexForBlockNumber(endBlockNumber);
|
||||
const int selectionEndChunkIndex = chunkIndexForBlockNumber(endBlockNumber);
|
||||
|
||||
const int selectionStart = selectionStartFileIndex == fileIndex
|
||||
&& selectionStartChunkIndex == chunkIndex
|
||||
? chunkRowForBlockNumber(startBlockNumber)
|
||||
: 0;
|
||||
|
||||
const int selectionEnd = selectionEndFileIndex == fileIndex
|
||||
&& selectionEndChunkIndex == chunkIndex
|
||||
? chunkRowForBlockNumber(endBlockNumber)
|
||||
: chunkRowsCountForBlockNumber(blockNumber);
|
||||
|
||||
const ChunkSelection selection(selectionStart, selectionEnd - selectionStart + 1);
|
||||
|
||||
emit contextMenuRequested(menu, fileIndexForBlockNumber(blockNumber),
|
||||
chunkIndexForBlockNumber(blockNumber));
|
||||
chunkIndexForBlockNumber(blockNumber),
|
||||
selection);
|
||||
|
||||
connect(this, &SideDiffEditorWidget::destroyed, menu.data(), &QMenu::deleteLater);
|
||||
menu->exec(e->globalPos());
|
||||
@@ -1067,24 +1133,26 @@ void SideBySideDiffEditorWidget::slotRightJumpToOriginalFileRequested(
|
||||
|
||||
void SideBySideDiffEditorWidget::slotLeftContextMenuRequested(QMenu *menu,
|
||||
int fileIndex,
|
||||
int chunkIndex)
|
||||
int chunkIndex,
|
||||
const ChunkSelection &selection)
|
||||
{
|
||||
menu->addSeparator();
|
||||
|
||||
m_controller.addCodePasterAction(menu, fileIndex, chunkIndex);
|
||||
m_controller.addApplyAction(menu, fileIndex, chunkIndex);
|
||||
m_controller.addExtraActions(menu, fileIndex, chunkIndex);
|
||||
m_controller.addExtraActions(menu, fileIndex, chunkIndex, selection);
|
||||
}
|
||||
|
||||
void SideBySideDiffEditorWidget::slotRightContextMenuRequested(QMenu *menu,
|
||||
int fileIndex,
|
||||
int chunkIndex)
|
||||
int chunkIndex,
|
||||
const ChunkSelection &selection)
|
||||
{
|
||||
menu->addSeparator();
|
||||
|
||||
m_controller.addCodePasterAction(menu, fileIndex, chunkIndex);
|
||||
m_controller.addRevertAction(menu, fileIndex, chunkIndex);
|
||||
m_controller.addExtraActions(menu, fileIndex, chunkIndex);
|
||||
m_controller.addExtraActions(menu, fileIndex, chunkIndex, selection);
|
||||
}
|
||||
|
||||
void SideBySideDiffEditorWidget::leftVSliderChanged()
|
||||
|
@@ -85,9 +85,9 @@ private:
|
||||
void slotRightJumpToOriginalFileRequested(int diffFileIndex,
|
||||
int lineNumber, int columnNumber);
|
||||
void slotLeftContextMenuRequested(QMenu *menu, int fileIndex,
|
||||
int chunkIndex);
|
||||
int chunkIndex, const ChunkSelection &selection);
|
||||
void slotRightContextMenuRequested(QMenu *menu, int fileIndex,
|
||||
int chunkIndex);
|
||||
int chunkIndex, const ChunkSelection &selection);
|
||||
void leftVSliderChanged();
|
||||
void rightVSliderChanged();
|
||||
void leftHSliderChanged();
|
||||
|
@@ -178,11 +178,57 @@ void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
||||
{
|
||||
QPointer<QMenu> menu = createStandardContextMenu();
|
||||
|
||||
const QTextCursor tc = textCursor();
|
||||
QTextCursor start = tc;
|
||||
start.setPosition(tc.selectionStart());
|
||||
QTextCursor end = tc;
|
||||
end.setPosition(tc.selectionEnd());
|
||||
const int startBlockNumber = start.blockNumber();
|
||||
const int endBlockNumber = end.blockNumber();
|
||||
|
||||
QTextCursor cursor = cursorForPosition(e->pos());
|
||||
const int blockNumber = cursor.blockNumber();
|
||||
|
||||
const int fileIndex = fileIndexForBlockNumber(blockNumber);
|
||||
const int chunkIndex = chunkIndexForBlockNumber(blockNumber);
|
||||
|
||||
const ChunkData chunkData = m_controller.chunkData(fileIndex, chunkIndex);
|
||||
|
||||
int selectionStart = -1;
|
||||
int selectionEnd = -1;
|
||||
|
||||
for (int i = startBlockNumber; i <= endBlockNumber; ++i) {
|
||||
const int currentFileIndex = fileIndexForBlockNumber(i);
|
||||
if (currentFileIndex < fileIndex)
|
||||
continue;
|
||||
|
||||
if (currentFileIndex > fileIndex)
|
||||
break;
|
||||
|
||||
const int currentChunkIndex = chunkIndexForBlockNumber(i);
|
||||
if (currentChunkIndex < chunkIndex)
|
||||
continue;
|
||||
|
||||
if (currentChunkIndex > chunkIndex)
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const ChunkSelection selection(selectionStart, selectionEnd - selectionStart + 1);
|
||||
|
||||
addContextMenuActions(menu, fileIndexForBlockNumber(blockNumber),
|
||||
chunkIndexForBlockNumber(blockNumber));
|
||||
chunkIndexForBlockNumber(blockNumber), selection);
|
||||
|
||||
connect(this, &UnifiedDiffEditorWidget::destroyed, menu.data(), &QMenu::deleteLater);
|
||||
menu->exec(e->globalPos());
|
||||
@@ -191,14 +237,15 @@ void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
||||
|
||||
void UnifiedDiffEditorWidget::addContextMenuActions(QMenu *menu,
|
||||
int fileIndex,
|
||||
int chunkIndex)
|
||||
int chunkIndex,
|
||||
const ChunkSelection &selection)
|
||||
{
|
||||
menu->addSeparator();
|
||||
|
||||
m_controller.addCodePasterAction(menu, fileIndex, chunkIndex);
|
||||
m_controller.addApplyAction(menu, fileIndex, chunkIndex);
|
||||
m_controller.addRevertAction(menu, fileIndex, chunkIndex);
|
||||
m_controller.addExtraActions(menu, fileIndex, chunkIndex);
|
||||
m_controller.addExtraActions(menu, fileIndex, chunkIndex, selection);
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::clear(const QString &message)
|
||||
@@ -228,7 +275,7 @@ QString UnifiedDiffEditorWidget::lineNumber(int blockNumber) const
|
||||
|
||||
if (leftLineExists || rightLineExists) {
|
||||
const QString leftLine = leftLineExists
|
||||
? QString::number(m_leftLineNumbers.value(blockNumber))
|
||||
? QString::number(m_leftLineNumbers.value(blockNumber).first)
|
||||
: QString();
|
||||
lineNumberString += QString(m_leftLineNumberDigits - leftLine.count(),
|
||||
' ') + leftLine;
|
||||
@@ -236,7 +283,7 @@ QString UnifiedDiffEditorWidget::lineNumber(int blockNumber) const
|
||||
lineNumberString += '|';
|
||||
|
||||
const QString rightLine = rightLineExists
|
||||
? QString::number(m_rightLineNumbers.value(blockNumber))
|
||||
? QString::number(m_rightLineNumbers.value(blockNumber).first)
|
||||
: QString();
|
||||
lineNumberString += QString(m_rightLineNumberDigits - rightLine.count(),
|
||||
' ') + rightLine;
|
||||
@@ -249,18 +296,20 @@ int UnifiedDiffEditorWidget::lineNumberDigits() const
|
||||
return m_leftLineNumberDigits + m_rightLineNumberDigits + 1;
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::setLeftLineNumber(int blockNumber, int lineNumber)
|
||||
void UnifiedDiffEditorWidget::setLeftLineNumber(int blockNumber, int lineNumber,
|
||||
int rowNumberInChunk)
|
||||
{
|
||||
const QString lineNumberString = QString::number(lineNumber);
|
||||
m_leftLineNumbers.insert(blockNumber, lineNumber);
|
||||
m_leftLineNumbers.insert(blockNumber, qMakePair(lineNumber, rowNumberInChunk));
|
||||
m_leftLineNumberDigits = qMax(m_leftLineNumberDigits,
|
||||
lineNumberString.count());
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::setRightLineNumber(int blockNumber, int lineNumber)
|
||||
void UnifiedDiffEditorWidget::setRightLineNumber(int blockNumber, int lineNumber,
|
||||
int rowNumberInChunk)
|
||||
{
|
||||
const QString lineNumberString = QString::number(lineNumber);
|
||||
m_rightLineNumbers.insert(blockNumber, lineNumber);
|
||||
m_rightLineNumbers.insert(blockNumber, qMakePair(lineNumber, rowNumberInChunk));
|
||||
m_rightLineNumberDigits = qMax(m_rightLineNumberDigits,
|
||||
lineNumberString.count());
|
||||
}
|
||||
@@ -307,6 +356,7 @@ QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData,
|
||||
int blockCount = 0;
|
||||
int charCount = 0;
|
||||
QList<TextLineData> leftBuffer, rightBuffer;
|
||||
QList<int> leftRowsBuffer, rightRowsBuffer;
|
||||
|
||||
(*selections)[*blockNumber].append(DiffSelection(&m_controller.m_chunkLineFormat));
|
||||
|
||||
@@ -356,7 +406,8 @@ QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData,
|
||||
if (!line.isEmpty()) {
|
||||
setLeftLineNumber(*blockNumber + blockCount + 1,
|
||||
chunkData.leftStartingLineNumber
|
||||
+ leftLineCount + 1);
|
||||
+ leftLineCount + 1,
|
||||
leftRowsBuffer.at(j));
|
||||
blockCount += blockDelta;
|
||||
++leftLineCount;
|
||||
}
|
||||
@@ -366,6 +417,7 @@ QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData,
|
||||
charCount += line.count();
|
||||
}
|
||||
leftBuffer.clear();
|
||||
leftRowsBuffer.clear();
|
||||
}
|
||||
if (rightBuffer.count()) {
|
||||
for (int j = 0; j < rightBuffer.count(); j++) {
|
||||
@@ -396,7 +448,8 @@ QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData,
|
||||
if (!line.isEmpty()) {
|
||||
setRightLineNumber(*blockNumber + blockCount + 1,
|
||||
chunkData.rightStartingLineNumber
|
||||
+ rightLineCount + 1);
|
||||
+ rightLineCount + 1,
|
||||
rightRowsBuffer.at(j));
|
||||
blockCount += blockDelta;
|
||||
++rightLineCount;
|
||||
}
|
||||
@@ -406,6 +459,7 @@ QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData,
|
||||
charCount += line.count();
|
||||
}
|
||||
rightBuffer.clear();
|
||||
rightRowsBuffer.clear();
|
||||
}
|
||||
if (i < chunkData.rows.count()) {
|
||||
const QString line = DiffUtils::makePatchLine(' ',
|
||||
@@ -416,10 +470,12 @@ QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData,
|
||||
if (!line.isEmpty()) {
|
||||
setLeftLineNumber(*blockNumber + blockCount + 1,
|
||||
chunkData.leftStartingLineNumber
|
||||
+ leftLineCount + 1);
|
||||
+ leftLineCount + 1,
|
||||
i);
|
||||
setRightLineNumber(*blockNumber + blockCount + 1,
|
||||
chunkData.rightStartingLineNumber
|
||||
+ rightLineCount + 1);
|
||||
+ rightLineCount + 1,
|
||||
i);
|
||||
blockCount += line.count('\n');
|
||||
++leftLineCount;
|
||||
++rightLineCount;
|
||||
@@ -430,10 +486,14 @@ QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData,
|
||||
charCount += line.count();
|
||||
}
|
||||
} else {
|
||||
if (rowData.leftLine.textLineType == TextLineData::TextLine)
|
||||
if (rowData.leftLine.textLineType == TextLineData::TextLine) {
|
||||
leftBuffer.append(rowData.leftLine);
|
||||
if (rowData.rightLine.textLineType == TextLineData::TextLine)
|
||||
leftRowsBuffer.append(i);
|
||||
}
|
||||
if (rowData.rightLine.textLineType == TextLineData::TextLine) {
|
||||
rightBuffer.append(rowData.rightLine);
|
||||
rightRowsBuffer.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,13 +644,13 @@ void UnifiedDiffEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
|
||||
|
||||
const int columnNumber = cursor.positionInBlock() - 1; // -1 for the first character in line
|
||||
|
||||
const int rightLineNumber = m_rightLineNumbers.value(blockNumber, -1);
|
||||
const int rightLineNumber = m_rightLineNumbers.value(blockNumber, qMakePair(-1, 0)).first;
|
||||
if (rightLineNumber >= 0) {
|
||||
m_controller.jumpToOriginalFile(rightFileName, rightLineNumber, columnNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
const int leftLineNumber = m_leftLineNumbers.value(blockNumber, -1);
|
||||
const int leftLineNumber = m_leftLineNumbers.value(blockNumber, qMakePair(-1, 0)).first;
|
||||
if (leftLineNumber >= 0) {
|
||||
if (leftFileName == rightFileName) {
|
||||
for (const ChunkData &chunkData : fileData.chunks) {
|
||||
|
@@ -39,6 +39,7 @@ namespace DiffEditor {
|
||||
|
||||
class ChunkData;
|
||||
class FileData;
|
||||
class ChunkSelection;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
@@ -79,8 +80,8 @@ private:
|
||||
|
||||
void slotCursorPositionChangedInEditor();
|
||||
|
||||
void setLeftLineNumber(int blockNumber, int lineNumber);
|
||||
void setRightLineNumber(int blockNumber, int lineNumber);
|
||||
void setLeftLineNumber(int blockNumber, int lineNumber, int rowNumberInChunk);
|
||||
void setRightLineNumber(int blockNumber, int lineNumber, int rowNumberInChunk);
|
||||
void setFileInfo(int blockNumber,
|
||||
const DiffFileInfo &leftFileInfo,
|
||||
const DiffFileInfo &rightFileInfo);
|
||||
@@ -97,11 +98,12 @@ private:
|
||||
void jumpToOriginalFile(const QTextCursor &cursor);
|
||||
void addContextMenuActions(QMenu *menu,
|
||||
int fileIndex,
|
||||
int chunkIndex);
|
||||
int chunkIndex,
|
||||
const ChunkSelection &selection);
|
||||
|
||||
// block number, visual line number.
|
||||
QMap<int, int> m_leftLineNumbers;
|
||||
QMap<int, int> m_rightLineNumbers;
|
||||
// block number, visual line number, chunk row number
|
||||
QMap<int, QPair<int, int> > m_leftLineNumbers;
|
||||
QMap<int, QPair<int, int> > m_rightLineNumbers;
|
||||
|
||||
DiffEditorWidgetController m_controller;
|
||||
|
||||
|
@@ -852,38 +852,58 @@ QTextCodec *GitClient::codecFor(GitClient::CodecType codecType, const QString &s
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GitClient::chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex)
|
||||
void GitClient::chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex,
|
||||
const DiffEditor::ChunkSelection &selection)
|
||||
{
|
||||
QPointer<DiffEditor::DiffEditorController> diffController
|
||||
= qobject_cast<DiffEditorController *>(sender());
|
||||
|
||||
auto stageChunk = [this](QPointer<DiffEditor::DiffEditorController> diffController,
|
||||
int fileIndex, int chunkIndex, bool revert) {
|
||||
int fileIndex, int chunkIndex, DiffEditorController::PatchOptions options,
|
||||
const DiffEditor::ChunkSelection &selection) {
|
||||
if (diffController.isNull())
|
||||
return;
|
||||
|
||||
DiffEditorController::PatchOptions options = DiffEditorController::AddPrefix;
|
||||
if (revert)
|
||||
options |= DiffEditorController::Revert;
|
||||
const QString patch = diffController->makePatch(fileIndex, chunkIndex, options);
|
||||
stage(diffController, patch, revert);
|
||||
options |= DiffEditorController::AddPrefix;
|
||||
const QString patch = diffController->makePatch(fileIndex, chunkIndex, selection, options);
|
||||
stage(diffController, patch, options & Revert);
|
||||
};
|
||||
|
||||
menu->addSeparator();
|
||||
QAction *stageChunkAction = menu->addAction(tr("Stage Chunk"));
|
||||
connect(stageChunkAction, &QAction::triggered, this,
|
||||
[stageChunk, diffController, fileIndex, chunkIndex]() {
|
||||
stageChunk(diffController, fileIndex, chunkIndex, false);
|
||||
stageChunk(diffController, fileIndex, chunkIndex,
|
||||
DiffEditorController::NoOption, DiffEditor::ChunkSelection());
|
||||
});
|
||||
QAction *stageLinesAction = menu->addAction(tr("Stage %n Line(s)", "", selection.selectedRowsCount));
|
||||
connect(stageLinesAction, &QAction::triggered, this,
|
||||
[stageChunk, diffController, fileIndex, chunkIndex, selection]() {
|
||||
stageChunk(diffController, fileIndex, chunkIndex,
|
||||
DiffEditorController::NoOption, selection);
|
||||
});
|
||||
QAction *unstageChunkAction = menu->addAction(tr("Unstage Chunk"));
|
||||
connect(unstageChunkAction, &QAction::triggered, this,
|
||||
[stageChunk, diffController, fileIndex, chunkIndex]() {
|
||||
stageChunk(diffController, fileIndex, chunkIndex, true);
|
||||
stageChunk(diffController, fileIndex, chunkIndex,
|
||||
DiffEditorController::Revert, DiffEditor::ChunkSelection());
|
||||
});
|
||||
|
||||
QAction *unstageLinesAction = menu->addAction(tr("Unstage %n Line(s)", "", selection.selectedRowsCount));
|
||||
connect(unstageLinesAction, &QAction::triggered, this,
|
||||
[stageChunk, diffController, fileIndex, chunkIndex, selection]() {
|
||||
stageChunk(diffController, fileIndex, chunkIndex,
|
||||
DiffEditorController::Revert,
|
||||
selection);
|
||||
});
|
||||
if (selection.isNull()) {
|
||||
stageLinesAction->setVisible(false);
|
||||
unstageLinesAction->setVisible(false);
|
||||
}
|
||||
if (!diffController || !diffController->chunkExists(fileIndex, chunkIndex)) {
|
||||
stageChunkAction->setEnabled(false);
|
||||
stageLinesAction->setEnabled(false);
|
||||
unstageChunkAction->setEnabled(false);
|
||||
unstageLinesAction->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -54,6 +54,7 @@ namespace VcsBase {
|
||||
}
|
||||
|
||||
namespace DiffEditor {
|
||||
class ChunkSelection;
|
||||
class DiffEditorController;
|
||||
}
|
||||
|
||||
@@ -333,7 +334,8 @@ public:
|
||||
|
||||
private:
|
||||
void finishSubmoduleUpdate();
|
||||
void chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex);
|
||||
void chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex,
|
||||
const DiffEditor::ChunkSelection &selection);
|
||||
|
||||
void stage(DiffEditor::DiffEditorController *diffController,
|
||||
const QString &patch, bool revert);
|
||||
|
Reference in New Issue
Block a user