2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
#include "sidebysidediffeditorwidget.h"
|
2022-09-29 17:02:04 +02:00
|
|
|
|
2017-05-09 12:19:11 +02:00
|
|
|
#include "diffeditorconstants.h"
|
2015-03-10 14:19:38 +01:00
|
|
|
#include "diffeditordocument.h"
|
2022-09-28 11:32:13 +02:00
|
|
|
#include "diffeditorplugin.h"
|
2014-02-24 11:10:17 +01:00
|
|
|
|
2015-02-26 13:22:35 +01:00
|
|
|
#include <QMenu>
|
2016-07-13 15:55:18 +02:00
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QScrollBar>
|
|
|
|
|
#include <QTextBlock>
|
2015-03-10 14:19:38 +01:00
|
|
|
#include <QVBoxLayout>
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2017-05-09 12:19:11 +02:00
|
|
|
#include <coreplugin/icore.h>
|
2017-11-02 13:06:38 +01:00
|
|
|
#include <coreplugin/find/highlightscrollbarcontroller.h>
|
2013-05-24 13:22:46 +02:00
|
|
|
#include <coreplugin/minisplitter.h>
|
2022-09-28 11:32:13 +02:00
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
2013-08-14 13:52:13 +02:00
|
|
|
|
2022-09-29 17:02:04 +02:00
|
|
|
#include <texteditor/displaysettings.h>
|
|
|
|
|
#include <texteditor/fontsettings.h>
|
|
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
#include <texteditor/textdocumentlayout.h>
|
|
|
|
|
#include <texteditor/texteditorsettings.h>
|
|
|
|
|
|
2022-10-28 14:53:17 +02:00
|
|
|
#include <utils/asynctask.h>
|
2022-11-25 17:31:12 +01:00
|
|
|
#include <utils/mathutils.h>
|
2013-06-18 14:09:54 +02:00
|
|
|
#include <utils/tooltip/tooltip.h>
|
|
|
|
|
|
2013-08-14 13:52:13 +02:00
|
|
|
using namespace Core;
|
2013-02-15 12:49:50 +01:00
|
|
|
using namespace TextEditor;
|
2014-08-28 18:46:39 +02:00
|
|
|
using namespace Utils;
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
using namespace std::placeholders;
|
|
|
|
|
|
2013-02-21 17:03:00 +01:00
|
|
|
namespace DiffEditor {
|
2015-01-30 14:55:56 +01:00
|
|
|
namespace Internal {
|
2015-01-30 14:46:20 +01:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
static DiffSide oppositeSide(DiffSide side)
|
|
|
|
|
{
|
|
|
|
|
return side == LeftSide ? RightSide : LeftSide;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-13 16:43:28 +01:00
|
|
|
class SideDiffEditorWidget : public SelectableTextEditorWidget
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
public:
|
2017-12-06 21:30:57 +01:00
|
|
|
SideDiffEditorWidget(QWidget *parent = nullptr);
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2013-05-07 14:02:08 +02:00
|
|
|
void clearAll(const QString &message);
|
2015-01-30 16:59:25 +01:00
|
|
|
void saveState();
|
2020-04-23 14:14:05 +02:00
|
|
|
using TextEditor::TextEditorWidget::restoreState;
|
2015-01-30 16:59:25 +01:00
|
|
|
void restoreState();
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2017-10-11 14:14:10 +02:00
|
|
|
void setFolded(int blockNumber, bool folded);
|
|
|
|
|
|
2022-10-19 23:48:03 +02:00
|
|
|
void setDisplaySettings(const DisplaySettings &displaySettings) override;
|
2013-06-03 15:17:34 +02:00
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
SideDiffData diffData() const { return m_data; }
|
|
|
|
|
void setDiffData(const SideDiffData &data) { m_data = data; }
|
|
|
|
|
|
2013-07-01 13:15:27 +02:00
|
|
|
signals:
|
|
|
|
|
void jumpToOriginalFileRequested(int diffFileIndex,
|
|
|
|
|
int lineNumber,
|
|
|
|
|
int columnNumber);
|
2014-02-13 16:43:28 +01:00
|
|
|
void contextMenuRequested(QMenu *menu,
|
|
|
|
|
int diffFileIndex,
|
2017-11-29 21:36:30 +01:00
|
|
|
int chunkIndex,
|
|
|
|
|
const ChunkSelection &selection);
|
2017-11-02 13:06:38 +01:00
|
|
|
void gotDisplaySettings();
|
|
|
|
|
void gotFocus();
|
2013-07-01 13:15:27 +02:00
|
|
|
|
2013-02-15 12:49:50 +01:00
|
|
|
protected:
|
2017-12-06 21:30:57 +01:00
|
|
|
int extraAreaWidth(int *markWidthPtr = nullptr) const override {
|
2014-02-13 16:43:28 +01:00
|
|
|
return SelectableTextEditorWidget::extraAreaWidth(markWidthPtr);
|
|
|
|
|
}
|
2015-06-01 17:55:31 +02:00
|
|
|
void applyFontSettings() override;
|
|
|
|
|
|
|
|
|
|
QString lineNumber(int blockNumber) const override;
|
|
|
|
|
int lineNumberDigits() const override;
|
|
|
|
|
bool selectionVisible(int blockNumber) const override;
|
|
|
|
|
bool replacementVisible(int blockNumber) const override;
|
|
|
|
|
QColor replacementPenColor(int blockNumber) const override;
|
|
|
|
|
QString plainTextFromSelection(const QTextCursor &cursor) const override;
|
|
|
|
|
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
2017-07-30 08:53:27 +03:00
|
|
|
void keyPressEvent(QKeyEvent *e) override;
|
2015-06-01 17:55:31 +02:00
|
|
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
|
|
|
|
void paintEvent(QPaintEvent *e) override;
|
|
|
|
|
void scrollContentsBy(int dx, int dy) override;
|
2017-10-11 14:14:10 +02:00
|
|
|
void customDrawCollapsedBlockPopup(QPainter &painter,
|
|
|
|
|
const QTextBlock &block,
|
|
|
|
|
QPointF offset,
|
|
|
|
|
const QRect &clip);
|
|
|
|
|
void drawCollapsedBlockPopup(QPainter &painter,
|
|
|
|
|
const QTextBlock &block,
|
|
|
|
|
QPointF offset,
|
2017-11-07 10:50:50 +01:00
|
|
|
const QRect &clip) override;
|
2017-11-02 13:06:38 +01:00
|
|
|
void focusInEvent(QFocusEvent *e) override;
|
2013-02-15 12:49:50 +01:00
|
|
|
|
|
|
|
|
private:
|
2013-06-05 15:25:42 +02:00
|
|
|
void paintSeparator(QPainter &painter, QColor &color, const QString &text,
|
|
|
|
|
const QTextBlock &block, int top);
|
2013-04-26 11:50:10 +02:00
|
|
|
void jumpToOriginalFile(const QTextCursor &cursor);
|
2013-04-25 17:37:20 +02:00
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
SideDiffData m_data;
|
|
|
|
|
|
2013-06-05 15:25:42 +02:00
|
|
|
QColor m_fileLineForeground;
|
|
|
|
|
QColor m_chunkLineForeground;
|
|
|
|
|
QColor m_textForeground;
|
2014-07-11 12:55:31 +02:00
|
|
|
QByteArray m_state;
|
2017-10-11 14:14:10 +02:00
|
|
|
|
|
|
|
|
QTextBlock m_drawCollapsedBlock;
|
|
|
|
|
QPointF m_drawCollapsedOffset;
|
|
|
|
|
QRect m_drawCollapsedClip;
|
2013-02-15 12:49:50 +01:00
|
|
|
};
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
SideDiffEditorWidget::SideDiffEditorWidget(QWidget *parent)
|
2016-07-13 15:55:18 +02:00
|
|
|
: SelectableTextEditorWidget("DiffEditor.SideDiffEditor", parent)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2018-04-06 15:57:30 +02:00
|
|
|
connect(this, &TextEditorWidget::tooltipRequested, this, [this](const QPoint &point, int position) {
|
2017-06-27 11:59:11 +02:00
|
|
|
const int block = document()->findBlock(position).blockNumber();
|
2022-09-26 16:47:39 +02:00
|
|
|
const auto it = m_data.m_fileInfo.constFind(block);
|
|
|
|
|
if (it != m_data.m_fileInfo.constEnd())
|
2014-12-08 10:57:05 +01:00
|
|
|
ToolTip::show(point, it.value().fileName, this);
|
2014-08-28 18:46:39 +02:00
|
|
|
else
|
|
|
|
|
ToolTip::hide();
|
|
|
|
|
});
|
2017-10-11 14:14:10 +02:00
|
|
|
|
2018-05-16 13:28:29 +02:00
|
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
void SideDiffEditorWidget::saveState()
|
2014-07-11 12:55:31 +02:00
|
|
|
{
|
|
|
|
|
if (!m_state.isNull())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
m_state = SelectableTextEditorWidget::saveState();
|
2014-07-11 12:55:31 +02:00
|
|
|
}
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
void SideDiffEditorWidget::restoreState()
|
2014-07-11 12:55:31 +02:00
|
|
|
{
|
|
|
|
|
if (m_state.isNull())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
SelectableTextEditorWidget::restoreState(m_state);
|
2014-07-11 12:55:31 +02:00
|
|
|
m_state.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-11 14:14:10 +02:00
|
|
|
void SideDiffEditorWidget::setFolded(int blockNumber, bool folded)
|
|
|
|
|
{
|
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (TextDocumentLayout::isFolded(block) == folded)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, !folded);
|
|
|
|
|
|
2018-11-07 23:22:59 +01:00
|
|
|
auto documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout());
|
2017-10-11 14:14:10 +02:00
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-19 23:48:03 +02:00
|
|
|
void SideDiffEditorWidget::setDisplaySettings(const DisplaySettings &displaySettings)
|
2013-06-03 15:17:34 +02:00
|
|
|
{
|
2022-10-19 23:48:03 +02:00
|
|
|
SelectableTextEditorWidget::setDisplaySettings(displaySettings);
|
2017-11-02 13:06:38 +01:00
|
|
|
emit gotDisplaySettings();
|
2013-06-03 15:17:34 +02:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideDiffEditorWidget::applyFontSettings()
|
2013-06-05 15:25:42 +02:00
|
|
|
{
|
2014-02-13 16:43:28 +01:00
|
|
|
SelectableTextEditorWidget::applyFontSettings();
|
2015-02-03 23:54:47 +02:00
|
|
|
const FontSettings &fs = textDocument()->fontSettings();
|
2013-06-05 15:25:42 +02:00
|
|
|
m_fileLineForeground = fs.formatFor(C_DIFF_FILE_LINE).foreground();
|
|
|
|
|
m_chunkLineForeground = fs.formatFor(C_DIFF_CONTEXT_LINE).foreground();
|
|
|
|
|
m_textForeground = fs.toTextCharFormat(C_TEXT).foreground().color();
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
QString SideDiffEditorWidget::lineNumber(int blockNumber) const
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2022-09-26 16:47:39 +02:00
|
|
|
const auto it = m_data.m_lineNumbers.constFind(blockNumber);
|
|
|
|
|
if (it != m_data.m_lineNumbers.constEnd())
|
2017-06-27 11:59:11 +02:00
|
|
|
return QString::number(it.value());
|
2022-09-27 15:13:33 +02:00
|
|
|
return {};
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
int SideDiffEditorWidget::lineNumberDigits() const
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2022-09-26 16:47:39 +02:00
|
|
|
return m_data.m_lineNumberDigits;
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
bool SideDiffEditorWidget::selectionVisible(int blockNumber) const
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2022-09-26 16:47:39 +02:00
|
|
|
return !m_data.m_separators.value(blockNumber, false);
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
bool SideDiffEditorWidget::replacementVisible(int blockNumber) const
|
2013-04-10 13:51:04 +02:00
|
|
|
{
|
2022-09-26 16:47:39 +02:00
|
|
|
return m_data.isChunkLine(blockNumber) || (m_data.isFileLine(blockNumber)
|
2017-06-27 11:59:11 +02:00
|
|
|
&& TextDocumentLayout::isFolded(document()->findBlockByNumber(blockNumber)));
|
2013-04-10 13:51:04 +02:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
QColor SideDiffEditorWidget::replacementPenColor(int blockNumber) const
|
2013-06-05 15:25:42 +02:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(blockNumber)
|
|
|
|
|
return m_chunkLineForeground;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
QString SideDiffEditorWidget::plainTextFromSelection(const QTextCursor &cursor) const
|
2013-04-23 09:09:20 +02:00
|
|
|
{
|
|
|
|
|
const int startPosition = cursor.selectionStart();
|
|
|
|
|
const int endPosition = cursor.selectionEnd();
|
|
|
|
|
if (startPosition == endPosition)
|
2022-09-27 15:13:33 +02:00
|
|
|
return {}; // no selection
|
2013-04-23 09:09:20 +02:00
|
|
|
|
2017-06-27 11:59:11 +02:00
|
|
|
const QTextBlock startBlock = document()->findBlock(startPosition);
|
|
|
|
|
const QTextBlock endBlock = document()->findBlock(endPosition);
|
2013-04-23 09:09:20 +02:00
|
|
|
QTextBlock block = startBlock;
|
|
|
|
|
QString text;
|
|
|
|
|
bool textInserted = false;
|
|
|
|
|
while (block.isValid() && block.blockNumber() <= endBlock.blockNumber()) {
|
|
|
|
|
if (selectionVisible(block.blockNumber())) {
|
|
|
|
|
if (block == startBlock) {
|
|
|
|
|
if (block == endBlock)
|
|
|
|
|
text = cursor.selectedText(); // just one line text
|
|
|
|
|
else
|
|
|
|
|
text = block.text().mid(startPosition - block.position());
|
|
|
|
|
} else {
|
|
|
|
|
if (textInserted)
|
2017-12-06 21:30:57 +01:00
|
|
|
text += '\n';
|
2013-04-23 09:09:20 +02:00
|
|
|
if (block == endBlock)
|
2022-07-13 09:27:18 +02:00
|
|
|
text += QStringView(block.text()).left(endPosition - block.position());
|
2013-04-23 09:09:20 +02:00
|
|
|
else
|
|
|
|
|
text += block.text();
|
|
|
|
|
}
|
|
|
|
|
textInserted = true;
|
|
|
|
|
}
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-30 11:37:50 +02:00
|
|
|
return TextDocument::convertToPlainText(text);
|
2013-04-23 09:09:20 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-27 15:48:33 +02:00
|
|
|
SideBySideDiffOutput SideDiffData::diffOutput(QFutureInterface<void> &fi, int progressMin,
|
|
|
|
|
int progressMax, const DiffEditorInput &input)
|
|
|
|
|
{
|
|
|
|
|
SideBySideDiffOutput output;
|
|
|
|
|
|
|
|
|
|
const QChar separator = '\n';
|
|
|
|
|
int blockNumber = 0;
|
|
|
|
|
int i = 0;
|
|
|
|
|
const int count = input.m_contextFileData.size();
|
2022-09-28 09:31:42 +02:00
|
|
|
std::array<QString, SideCount> diffText{};
|
|
|
|
|
|
|
|
|
|
auto addFileLine = [&](DiffSide side, const FileData &fileData) {
|
|
|
|
|
output.side[side].selections[blockNumber].append({input.m_fileLineFormat});
|
|
|
|
|
output.side[side].diffData.setFileInfo(blockNumber, fileData.fileInfo[side]);
|
|
|
|
|
diffText[side] += separator;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto addChunkLine = [&](DiffSide side, int skippedLines, const QString &contextInfo = {}) {
|
|
|
|
|
output.side[side].selections[blockNumber].append({input.m_chunkLineFormat});
|
|
|
|
|
output.side[side].diffData.setSkippedLines(blockNumber, skippedLines, contextInfo);
|
|
|
|
|
diffText[side] += separator;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto addRowLine = [&](DiffSide side, const RowData &rowData,
|
|
|
|
|
int *lineNumber, int *lastLineNumber = nullptr) {
|
|
|
|
|
if (rowData.line[side].textLineType == TextLineData::TextLine) {
|
|
|
|
|
diffText[side] += rowData.line[side].text;
|
|
|
|
|
if (lastLineNumber)
|
|
|
|
|
*lastLineNumber = *lineNumber;
|
|
|
|
|
++(*lineNumber);
|
|
|
|
|
output.side[side].diffData.setLineNumber(blockNumber, *lineNumber);
|
|
|
|
|
} else if (rowData.line[side].textLineType == TextLineData::Separator) {
|
|
|
|
|
output.side[side].diffData.setSeparator(blockNumber, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!rowData.equal) {
|
|
|
|
|
if (rowData.line[side].textLineType == TextLineData::TextLine)
|
|
|
|
|
output.side[side].selections[blockNumber].append({input.m_lineFormat[side]});
|
|
|
|
|
else
|
|
|
|
|
output.side[side].selections[blockNumber].append({input.m_spanLineFormat});
|
|
|
|
|
}
|
|
|
|
|
for (auto it = rowData.line[side].changedPositions.cbegin(),
|
|
|
|
|
end = rowData.line[side].changedPositions.cend(); it != end; ++it) {
|
|
|
|
|
output.side[side].selections[blockNumber].append(
|
|
|
|
|
{input.m_charFormat[side], it.key(), it.value()});
|
|
|
|
|
}
|
|
|
|
|
diffText[side] += separator;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto addSkippedLine = [&](DiffSide side, int skippedLines) {
|
|
|
|
|
output.side[side].selections[blockNumber].append({input.m_chunkLineFormat});
|
|
|
|
|
output.side[side].diffData.setSkippedLines(blockNumber, skippedLines);
|
|
|
|
|
diffText[side] += separator;
|
|
|
|
|
};
|
2022-09-27 15:48:33 +02:00
|
|
|
|
2022-09-28 09:31:42 +02:00
|
|
|
for (const FileData &contextFileData : input.m_contextFileData) {
|
|
|
|
|
diffText = {};
|
|
|
|
|
output.foldingIndent.insert(blockNumber, 1);
|
2022-09-27 15:48:33 +02:00
|
|
|
|
2022-09-28 09:31:42 +02:00
|
|
|
addFileLine(LeftSide, contextFileData);
|
|
|
|
|
addFileLine(RightSide, contextFileData);
|
2022-09-27 15:48:33 +02:00
|
|
|
blockNumber++;
|
|
|
|
|
|
|
|
|
|
int lastLeftLineNumber = -1;
|
|
|
|
|
|
|
|
|
|
if (contextFileData.binaryFiles) {
|
2022-09-28 09:31:42 +02:00
|
|
|
output.foldingIndent.insert(blockNumber, 2);
|
|
|
|
|
addChunkLine(LeftSide, -2);
|
|
|
|
|
addChunkLine(RightSide, -2);
|
2022-09-27 15:48:33 +02:00
|
|
|
blockNumber++;
|
|
|
|
|
} else {
|
|
|
|
|
for (int j = 0; j < contextFileData.chunks.count(); j++) {
|
|
|
|
|
const ChunkData &chunkData = contextFileData.chunks.at(j);
|
|
|
|
|
|
|
|
|
|
int leftLineNumber = chunkData.startingLineNumber[LeftSide];
|
|
|
|
|
int rightLineNumber = chunkData.startingLineNumber[RightSide];
|
|
|
|
|
|
|
|
|
|
if (!chunkData.contextChunk) {
|
|
|
|
|
const int skippedLines = leftLineNumber - lastLeftLineNumber - 1;
|
|
|
|
|
if (skippedLines > 0) {
|
2022-09-28 09:31:42 +02:00
|
|
|
output.foldingIndent.insert(blockNumber, 2);
|
|
|
|
|
addChunkLine(LeftSide, skippedLines, chunkData.contextInfo);
|
|
|
|
|
addChunkLine(RightSide, skippedLines, chunkData.contextInfo);
|
2022-09-27 15:48:33 +02:00
|
|
|
blockNumber++;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-29 19:13:12 +02:00
|
|
|
const int rows = chunkData.rows.count();
|
|
|
|
|
output.side[LeftSide].diffData.m_chunkInfo.setChunkIndex(blockNumber, rows, j);
|
|
|
|
|
output.side[RightSide].diffData.m_chunkInfo.setChunkIndex(blockNumber, rows, j);
|
2022-09-27 15:48:33 +02:00
|
|
|
|
|
|
|
|
for (const RowData &rowData : chunkData.rows) {
|
2022-09-28 09:31:42 +02:00
|
|
|
addRowLine(LeftSide, rowData, &leftLineNumber, &lastLeftLineNumber);
|
|
|
|
|
addRowLine(RightSide, rowData, &rightLineNumber);
|
2022-09-27 15:48:33 +02:00
|
|
|
blockNumber++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (j == contextFileData.chunks.count() - 1) { // the last chunk
|
|
|
|
|
int skippedLines = -2;
|
|
|
|
|
if (chunkData.contextChunk) {
|
|
|
|
|
// if it's context chunk
|
|
|
|
|
skippedLines = chunkData.rows.count();
|
|
|
|
|
} else if (!contextFileData.lastChunkAtTheEndOfFile
|
|
|
|
|
&& !contextFileData.contextChunksIncluded) {
|
|
|
|
|
// if not a context chunk and not a chunk at the end of file
|
|
|
|
|
// and context lines not included
|
|
|
|
|
skippedLines = -1; // unknown count skipped by the end of file
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (skippedLines >= -1) {
|
2022-09-28 09:31:42 +02:00
|
|
|
addSkippedLine(LeftSide, skippedLines);
|
|
|
|
|
addSkippedLine(RightSide, skippedLines);
|
2022-09-27 15:48:33 +02:00
|
|
|
blockNumber++;
|
|
|
|
|
} // otherwise nothing skipped
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-28 09:31:42 +02:00
|
|
|
diffText[LeftSide].replace('\r', ' ');
|
|
|
|
|
diffText[RightSide].replace('\r', ' ');
|
|
|
|
|
output.side[LeftSide].diffText += diffText[LeftSide];
|
|
|
|
|
output.side[RightSide].diffText += diffText[RightSide];
|
2022-11-25 17:31:12 +01:00
|
|
|
fi.setProgressValue(MathUtils::interpolate(++i, 0, count, progressMin, progressMax));
|
2022-09-27 15:48:33 +02:00
|
|
|
if (fi.isCanceled())
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
output.side[LeftSide].selections = SelectableTextEditorWidget::polishedSelections(
|
|
|
|
|
output.side[LeftSide].selections);
|
|
|
|
|
output.side[RightSide].selections = SelectableTextEditorWidget::polishedSelections(
|
|
|
|
|
output.side[RightSide].selections);
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
void SideDiffData::setLineNumber(int blockNumber, int lineNumber)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2013-04-26 11:50:10 +02:00
|
|
|
const QString lineNumberString = QString::number(lineNumber);
|
2013-02-15 12:49:50 +01:00
|
|
|
m_lineNumbers.insert(blockNumber, lineNumber);
|
2013-04-26 11:50:10 +02:00
|
|
|
m_lineNumberDigits = qMax(m_lineNumberDigits, lineNumberString.count());
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
void SideDiffData::setFileInfo(int blockNumber, const DiffFileInfo &fileInfo)
|
2013-08-14 13:52:13 +02:00
|
|
|
{
|
|
|
|
|
m_fileInfo[blockNumber] = fileInfo;
|
|
|
|
|
setSeparator(blockNumber, true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
int SideDiffData::blockNumberForFileIndex(int fileIndex) const
|
2013-05-23 13:36:27 +02:00
|
|
|
{
|
|
|
|
|
if (fileIndex < 0 || fileIndex >= m_fileInfo.count())
|
|
|
|
|
return -1;
|
|
|
|
|
|
2020-08-12 09:40:51 +02:00
|
|
|
return std::next(m_fileInfo.constBegin(), fileIndex).key();
|
2013-05-23 13:36:27 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
int SideDiffData::fileIndexForBlockNumber(int blockNumber) const
|
2013-05-23 13:36:27 +02:00
|
|
|
{
|
|
|
|
|
int i = -1;
|
2017-06-27 11:59:11 +02:00
|
|
|
for (auto it = m_fileInfo.cbegin(), end = m_fileInfo.cend(); it != end; ++it, ++i) {
|
2013-05-23 13:36:27 +02:00
|
|
|
if (it.key() > blockNumber)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-06-27 11:59:11 +02:00
|
|
|
|
2013-05-23 13:36:27 +02:00
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideDiffEditorWidget::clearAll(const QString &message)
|
2013-05-07 14:02:08 +02:00
|
|
|
{
|
|
|
|
|
clear();
|
2022-09-26 16:47:39 +02:00
|
|
|
m_data = {};
|
2022-09-27 13:20:26 +02:00
|
|
|
setSelections({});
|
2022-09-27 15:13:33 +02:00
|
|
|
setExtraSelections(TextEditorWidget::OtherSelection, {});
|
|
|
|
|
setPlainText(message);
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
2015-01-30 16:59:25 +01:00
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideDiffEditorWidget::scrollContentsBy(int dx, int dy)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2014-02-13 16:43:28 +01:00
|
|
|
SelectableTextEditorWidget::scrollContentsBy(dx, dy);
|
2013-02-15 12:49:50 +01:00
|
|
|
// TODO: update only chunk lines
|
|
|
|
|
viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideDiffEditorWidget::paintSeparator(QPainter &painter,
|
2013-06-05 15:25:42 +02:00
|
|
|
QColor &color,
|
|
|
|
|
const QString &text,
|
|
|
|
|
const QTextBlock &block,
|
|
|
|
|
int top)
|
2013-04-25 17:37:20 +02:00
|
|
|
{
|
|
|
|
|
QPointF offset = contentOffset();
|
|
|
|
|
painter.save();
|
2013-06-05 15:25:42 +02:00
|
|
|
|
|
|
|
|
QColor foreground = color;
|
|
|
|
|
if (!foreground.isValid())
|
|
|
|
|
foreground = m_textForeground;
|
|
|
|
|
if (!foreground.isValid())
|
2019-02-11 10:17:53 +01:00
|
|
|
foreground = palette().windowText().color();
|
2013-06-05 15:25:42 +02:00
|
|
|
|
|
|
|
|
painter.setPen(foreground);
|
|
|
|
|
|
2017-12-06 21:30:57 +01:00
|
|
|
const QString replacementText = " {" + foldReplacementText(block) + "}; ";
|
2019-02-11 10:32:46 +01:00
|
|
|
const int replacementTextWidth = fontMetrics().horizontalAdvance(replacementText) + 24;
|
2017-12-07 06:52:42 +01:00
|
|
|
int x = replacementTextWidth + int(offset.x());
|
2014-02-13 16:43:28 +01:00
|
|
|
if (x < document()->documentMargin()
|
2015-02-03 23:54:47 +02:00
|
|
|
|| !TextDocumentLayout::isFolded(block)) {
|
2017-12-07 06:52:42 +01:00
|
|
|
x = int(document()->documentMargin());
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
2013-04-25 17:37:20 +02:00
|
|
|
const QString elidedText = fontMetrics().elidedText(text,
|
|
|
|
|
Qt::ElideRight,
|
|
|
|
|
viewport()->width() - x);
|
|
|
|
|
QTextLayout *layout = block.layout();
|
|
|
|
|
QTextLine textLine = layout->lineAt(0);
|
|
|
|
|
QRectF lineRect = textLine.naturalTextRect().translated(offset.x(), top);
|
|
|
|
|
QRect clipRect = contentsRect();
|
|
|
|
|
clipRect.setLeft(x);
|
|
|
|
|
painter.setClipRect(clipRect);
|
|
|
|
|
painter.drawText(QPointF(x, lineRect.top() + textLine.ascent()),
|
|
|
|
|
elidedText);
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideDiffEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
|
2013-04-26 11:50:10 +02:00
|
|
|
{
|
|
|
|
|
if (e->button() == Qt::LeftButton && !(e->modifiers() & Qt::ShiftModifier)) {
|
|
|
|
|
QTextCursor cursor = cursorForPosition(e->pos());
|
|
|
|
|
jumpToOriginalFile(cursor);
|
2013-07-01 11:38:16 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
2013-04-26 11:50:10 +02:00
|
|
|
}
|
2014-02-13 16:43:28 +01:00
|
|
|
SelectableTextEditorWidget::mouseDoubleClickEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-30 08:53:27 +03:00
|
|
|
void SideDiffEditorWidget::keyPressEvent(QKeyEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
|
|
|
|
jumpToOriginalFile(textCursor());
|
|
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SelectableTextEditorWidget::keyPressEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-13 16:43:28 +01:00
|
|
|
void SideDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
|
|
|
|
{
|
|
|
|
|
QPointer<QMenu> menu = createStandardContextMenu();
|
|
|
|
|
|
2017-11-29 21:36:30 +01:00
|
|
|
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();
|
|
|
|
|
|
2014-02-13 16:43:28 +01:00
|
|
|
QTextCursor cursor = cursorForPosition(e->pos());
|
|
|
|
|
const int blockNumber = cursor.blockNumber();
|
|
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
const int fileIndex = m_data.fileIndexForBlockNumber(blockNumber);
|
2022-09-29 19:13:12 +02:00
|
|
|
const int chunkIndex = m_data.m_chunkInfo.chunkIndexForBlockNumber(blockNumber);
|
2017-11-29 21:36:30 +01:00
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
const int selectionStartFileIndex = m_data.fileIndexForBlockNumber(startBlockNumber);
|
2022-09-29 19:13:12 +02:00
|
|
|
const int selectionStartChunkIndex = m_data.m_chunkInfo.chunkIndexForBlockNumber(startBlockNumber);
|
2022-09-26 16:47:39 +02:00
|
|
|
const int selectionEndFileIndex = m_data.fileIndexForBlockNumber(endBlockNumber);
|
2022-09-29 19:13:12 +02:00
|
|
|
const int selectionEndChunkIndex = m_data.m_chunkInfo.chunkIndexForBlockNumber(endBlockNumber);
|
2017-11-29 21:36:30 +01:00
|
|
|
|
|
|
|
|
const int selectionStart = selectionStartFileIndex == fileIndex
|
|
|
|
|
&& selectionStartChunkIndex == chunkIndex
|
2022-09-29 19:13:12 +02:00
|
|
|
? m_data.m_chunkInfo.chunkRowForBlockNumber(startBlockNumber)
|
2017-11-29 21:36:30 +01:00
|
|
|
: 0;
|
|
|
|
|
|
|
|
|
|
const int selectionEnd = selectionEndFileIndex == fileIndex
|
|
|
|
|
&& selectionEndChunkIndex == chunkIndex
|
2022-09-29 19:13:12 +02:00
|
|
|
? m_data.m_chunkInfo.chunkRowForBlockNumber(endBlockNumber)
|
|
|
|
|
: m_data.m_chunkInfo.chunkRowsCountForBlockNumber(blockNumber);
|
2017-11-29 21:36:30 +01:00
|
|
|
|
2019-11-20 16:01:17 +01:00
|
|
|
QList<int> rows;
|
|
|
|
|
for (int i = selectionStart; i <= selectionEnd; ++i)
|
|
|
|
|
rows.append(i);
|
|
|
|
|
|
|
|
|
|
const ChunkSelection selection(rows, rows);
|
2017-11-29 21:36:30 +01:00
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
emit contextMenuRequested(menu, m_data.fileIndexForBlockNumber(blockNumber),
|
2022-09-29 19:13:12 +02:00
|
|
|
m_data.m_chunkInfo.chunkIndexForBlockNumber(blockNumber),
|
2017-11-29 21:36:30 +01:00
|
|
|
selection);
|
2014-02-13 16:43:28 +01:00
|
|
|
|
2015-01-29 08:00:52 +01:00
|
|
|
connect(this, &SideDiffEditorWidget::destroyed, menu.data(), &QMenu::deleteLater);
|
2014-02-13 16:43:28 +01:00
|
|
|
menu->exec(e->globalPos());
|
|
|
|
|
delete menu;
|
2013-04-26 11:50:10 +02:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideDiffEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
|
2013-04-26 11:50:10 +02:00
|
|
|
{
|
2022-09-26 16:47:39 +02:00
|
|
|
if (m_data.m_fileInfo.isEmpty())
|
2013-04-26 11:50:10 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const int blockNumber = cursor.blockNumber();
|
2022-09-26 16:47:39 +02:00
|
|
|
if (!m_data.m_lineNumbers.contains(blockNumber))
|
2013-04-26 11:50:10 +02:00
|
|
|
return;
|
|
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
const int lineNumber = m_data.m_lineNumbers.value(blockNumber);
|
2016-07-13 15:55:18 +02:00
|
|
|
const int columnNumber = cursor.positionInBlock();
|
2013-04-26 11:50:10 +02:00
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
emit jumpToOriginalFileRequested(m_data.fileIndexForBlockNumber(blockNumber),
|
2014-02-13 16:43:28 +01:00
|
|
|
lineNumber, columnNumber);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString skippedText(int skippedNumber)
|
|
|
|
|
{
|
|
|
|
|
if (skippedNumber > 0)
|
2018-05-03 14:48:28 +02:00
|
|
|
return SideBySideDiffEditorWidget::tr("Skipped %n lines...", nullptr, skippedNumber);
|
2014-02-13 16:43:28 +01:00
|
|
|
if (skippedNumber == -2)
|
|
|
|
|
return SideBySideDiffEditorWidget::tr("Binary files differ");
|
|
|
|
|
return SideBySideDiffEditorWidget::tr("Skipped unknown number of lines...");
|
2013-04-26 11:50:10 +02:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideDiffEditorWidget::paintEvent(QPaintEvent *e)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2014-02-13 16:43:28 +01:00
|
|
|
SelectableTextEditorWidget::paintEvent(e);
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2014-02-13 16:43:28 +01:00
|
|
|
QPainter painter(viewport());
|
2022-09-27 15:48:33 +02:00
|
|
|
const QPointF offset = contentOffset();
|
2017-10-24 13:44:33 +02:00
|
|
|
QTextBlock currentBlock = firstVisibleBlock();
|
2013-02-21 17:14:07 +01:00
|
|
|
|
|
|
|
|
while (currentBlock.isValid()) {
|
|
|
|
|
if (currentBlock.isVisible()) {
|
|
|
|
|
qreal top = blockBoundingGeometry(currentBlock).translated(offset).top();
|
|
|
|
|
qreal bottom = top + blockBoundingRect(currentBlock).height();
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2013-02-21 17:14:07 +01:00
|
|
|
if (top > e->rect().bottom())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (bottom >= e->rect().top()) {
|
2013-04-25 17:37:20 +02:00
|
|
|
const int blockNumber = currentBlock.blockNumber();
|
2013-02-21 17:14:07 +01:00
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
auto it = m_data.m_skippedLines.constFind(blockNumber);
|
|
|
|
|
if (it != m_data.m_skippedLines.constEnd()) {
|
2017-06-02 10:13:36 +03:00
|
|
|
QString skippedRowsText = '[' + skippedText(it->first) + ']';
|
|
|
|
|
if (!it->second.isEmpty())
|
|
|
|
|
skippedRowsText += ' ' + it->second;
|
2013-06-05 15:25:42 +02:00
|
|
|
paintSeparator(painter, m_chunkLineForeground,
|
|
|
|
|
skippedRowsText, currentBlock, top);
|
2013-04-25 17:37:20 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-26 16:47:39 +02:00
|
|
|
const DiffFileInfo fileInfo = m_data.m_fileInfo.value(blockNumber);
|
2013-05-07 14:02:08 +02:00
|
|
|
if (!fileInfo.fileName.isEmpty()) {
|
|
|
|
|
const QString fileNameText = fileInfo.typeInfo.isEmpty()
|
|
|
|
|
? fileInfo.fileName
|
2014-02-13 16:43:28 +01:00
|
|
|
: tr("[%1] %2").arg(fileInfo.typeInfo)
|
|
|
|
|
.arg(fileInfo.fileName);
|
2013-06-05 15:25:42 +02:00
|
|
|
paintSeparator(painter, m_fileLineForeground,
|
|
|
|
|
fileNameText, currentBlock, top);
|
2013-02-21 17:14:07 +01:00
|
|
|
}
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
2013-02-21 17:14:07 +01:00
|
|
|
currentBlock = currentBlock.next();
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
2017-10-11 14:14:10 +02:00
|
|
|
|
|
|
|
|
if (m_drawCollapsedBlock.isValid()) {
|
|
|
|
|
// draw it now
|
|
|
|
|
customDrawCollapsedBlockPopup(painter,
|
|
|
|
|
m_drawCollapsedBlock,
|
|
|
|
|
m_drawCollapsedOffset,
|
|
|
|
|
m_drawCollapsedClip);
|
|
|
|
|
// reset the data for the drawing
|
2022-09-27 15:13:33 +02:00
|
|
|
m_drawCollapsedBlock = {};
|
2017-10-11 14:14:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SideDiffEditorWidget::customDrawCollapsedBlockPopup(QPainter &painter,
|
|
|
|
|
const QTextBlock &block,
|
|
|
|
|
QPointF offset,
|
|
|
|
|
const QRect &clip)
|
|
|
|
|
{
|
|
|
|
|
int margin = block.document()->documentMargin();
|
|
|
|
|
qreal maxWidth = 0;
|
|
|
|
|
qreal blockHeight = 0;
|
|
|
|
|
QTextBlock b = block;
|
|
|
|
|
|
|
|
|
|
while (!b.isVisible()) {
|
|
|
|
|
const int blockNumber = b.blockNumber();
|
2022-09-26 16:47:39 +02:00
|
|
|
if (!m_data.m_skippedLines.contains(blockNumber) && !m_data.m_separators.contains(blockNumber)) {
|
2017-10-11 14:14:10 +02:00
|
|
|
b.setVisible(true); // make sure block bounding rect works
|
|
|
|
|
QRectF r = blockBoundingRect(b).translated(offset);
|
|
|
|
|
|
|
|
|
|
QTextLayout *layout = b.layout();
|
2022-09-27 15:13:33 +02:00
|
|
|
for (int i = layout->lineCount() - 1; i >= 0; --i)
|
|
|
|
|
maxWidth = qMax(maxWidth, layout->lineAt(i).naturalTextWidth() + 2 * margin);
|
2017-10-11 14:14:10 +02:00
|
|
|
|
|
|
|
|
blockHeight += r.height();
|
|
|
|
|
|
|
|
|
|
b.setVisible(false); // restore previous state
|
|
|
|
|
b.setLineCount(0); // restore 0 line count for invisible block
|
|
|
|
|
}
|
|
|
|
|
b = b.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
|
painter.translate(.5, .5);
|
|
|
|
|
QBrush brush = palette().base();
|
|
|
|
|
const QTextCharFormat &ifdefedOutFormat
|
|
|
|
|
= textDocument()->fontSettings().toTextCharFormat(C_DISABLED_CODE);
|
|
|
|
|
if (ifdefedOutFormat.hasProperty(QTextFormat::BackgroundBrush))
|
|
|
|
|
brush = ifdefedOutFormat.background();
|
|
|
|
|
painter.setBrush(brush);
|
2022-09-27 15:13:33 +02:00
|
|
|
painter.drawRoundedRect(QRectF(offset.x(), offset.y(), maxWidth, blockHeight)
|
|
|
|
|
.adjusted(0, 0, 0, 0), 3, 3);
|
2017-10-11 14:14:10 +02:00
|
|
|
painter.restore();
|
|
|
|
|
|
|
|
|
|
QTextBlock end = b;
|
|
|
|
|
b = block;
|
|
|
|
|
while (b != end) {
|
|
|
|
|
const int blockNumber = b.blockNumber();
|
2022-09-26 16:47:39 +02:00
|
|
|
if (!m_data.m_skippedLines.contains(blockNumber) && !m_data.m_separators.contains(blockNumber)) {
|
2017-10-11 14:14:10 +02:00
|
|
|
b.setVisible(true); // make sure block bounding rect works
|
2022-09-27 15:48:33 +02:00
|
|
|
const QRectF r = blockBoundingRect(b).translated(offset);
|
2017-10-11 14:14:10 +02:00
|
|
|
QTextLayout *layout = b.layout();
|
2022-09-27 15:48:33 +02:00
|
|
|
layout->draw(&painter, offset, {}, clip);
|
2017-10-11 14:14:10 +02:00
|
|
|
|
|
|
|
|
b.setVisible(false); // restore previous state
|
|
|
|
|
b.setLineCount(0); // restore 0 line count for invisible block
|
|
|
|
|
offset.ry() += r.height();
|
|
|
|
|
}
|
|
|
|
|
b = b.next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SideDiffEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
|
|
|
|
|
const QTextBlock &block,
|
|
|
|
|
QPointF offset,
|
|
|
|
|
const QRect &clip)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(painter)
|
|
|
|
|
// Called from inside SelectableTextEditorWidget::paintEvent().
|
|
|
|
|
// Postpone the drawing for now, do it after our paintEvent's
|
|
|
|
|
// custom painting. Store the data for the future redraw.
|
|
|
|
|
m_drawCollapsedBlock = block;
|
|
|
|
|
m_drawCollapsedOffset = offset;
|
|
|
|
|
m_drawCollapsedClip = clip;
|
2013-04-25 17:37:20 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-02 13:06:38 +01:00
|
|
|
void SideDiffEditorWidget::focusInEvent(QFocusEvent *e)
|
|
|
|
|
{
|
|
|
|
|
SelectableTextEditorWidget::focusInEvent(e);
|
|
|
|
|
emit gotFocus();
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-15 12:49:50 +01:00
|
|
|
//////////////////
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
|
2013-12-16 16:19:40 +01:00
|
|
|
: QWidget(parent)
|
2016-07-13 15:55:18 +02:00
|
|
|
, m_controller(this)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2022-09-29 14:25:34 +02:00
|
|
|
auto setupEditor = [this](DiffSide side) {
|
|
|
|
|
m_editor[side] = new SideDiffEditorWidget(this);
|
|
|
|
|
|
|
|
|
|
connect(m_editor[side], &SideDiffEditorWidget::jumpToOriginalFileRequested,
|
|
|
|
|
this, std::bind(&SideBySideDiffEditorWidget::jumpToOriginalFileRequested, this,
|
|
|
|
|
side, _1, _2, _3));
|
|
|
|
|
connect(m_editor[side], &SideDiffEditorWidget::contextMenuRequested,
|
|
|
|
|
this, std::bind(&SideBySideDiffEditorWidget::contextMenuRequested, this,
|
|
|
|
|
side, _1, _2, _3, _4));
|
|
|
|
|
|
|
|
|
|
connect(m_editor[side]->verticalScrollBar(), &QAbstractSlider::valueChanged,
|
|
|
|
|
this, std::bind(&SideBySideDiffEditorWidget::verticalSliderChanged, this, side));
|
|
|
|
|
connect(m_editor[side]->verticalScrollBar(), &QAbstractSlider::actionTriggered,
|
|
|
|
|
this, std::bind(&SideBySideDiffEditorWidget::verticalSliderChanged, this, side));
|
|
|
|
|
|
|
|
|
|
connect(m_editor[side]->horizontalScrollBar(), &QAbstractSlider::valueChanged,
|
|
|
|
|
this, std::bind(&SideBySideDiffEditorWidget::horizontalSliderChanged, this, side));
|
|
|
|
|
connect(m_editor[side]->horizontalScrollBar(), &QAbstractSlider::actionTriggered,
|
|
|
|
|
this, std::bind(&SideBySideDiffEditorWidget::horizontalSliderChanged, this, side));
|
|
|
|
|
|
|
|
|
|
connect(m_editor[side], &QPlainTextEdit::cursorPositionChanged,
|
|
|
|
|
this, std::bind(&SideBySideDiffEditorWidget::cursorPositionChanged, this, side));
|
|
|
|
|
|
|
|
|
|
connect(m_editor[side]->horizontalScrollBar(), &QAbstractSlider::rangeChanged,
|
|
|
|
|
this, &SideBySideDiffEditorWidget::syncHorizontalScrollBarPolicy);
|
|
|
|
|
|
|
|
|
|
auto context = new IContext(this);
|
|
|
|
|
context->setWidget(m_editor[side]);
|
2022-09-29 17:28:47 +02:00
|
|
|
context->setContext(Context(Id(Constants::SIDE_BY_SIDE_VIEW_ID).withSuffix(side + 1)));
|
2022-09-29 14:25:34 +02:00
|
|
|
ICore::addContextObject(context);
|
2017-11-02 13:06:38 +01:00
|
|
|
};
|
2022-09-29 14:25:34 +02:00
|
|
|
setupEditor(LeftSide);
|
|
|
|
|
setupEditor(RightSide);
|
|
|
|
|
m_editor[LeftSide]->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
|
|
|
|
|
|
auto setupHighlight = [this] {
|
|
|
|
|
HighlightScrollBarController *ctrl = m_editor[LeftSide]->highlightScrollBarController();
|
|
|
|
|
if (ctrl)
|
|
|
|
|
ctrl->setScrollArea(m_editor[RightSide]);
|
|
|
|
|
};
|
|
|
|
|
setupHighlight();
|
|
|
|
|
connect(m_editor[LeftSide], &SideDiffEditorWidget::gotDisplaySettings, this, setupHighlight);
|
2017-11-02 13:06:38 +01:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[RightSide]->verticalScrollBar()->setFocusProxy(m_editor[LeftSide]);
|
|
|
|
|
connect(m_editor[LeftSide], &SideDiffEditorWidget::gotFocus, this, [this] {
|
|
|
|
|
if (m_editor[RightSide]->verticalScrollBar()->focusProxy() == m_editor[LeftSide])
|
2017-11-02 13:06:38 +01:00
|
|
|
return; // We already did it before.
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
// Hack #1. If the left editor got a focus last time we don't want to focus right editor
|
|
|
|
|
// when clicking the right scrollbar.
|
|
|
|
|
m_editor[RightSide]->verticalScrollBar()->setFocusProxy(m_editor[LeftSide]);
|
2017-11-02 13:06:38 +01:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
// Hack #2. If the focus is currently not on the scrollbar's proxy and we click on
|
|
|
|
|
// the scrollbar, the focus will go to the parent of the scrollbar. In order to give
|
|
|
|
|
// the focus to the proxy we need to set a click focus policy on the scrollbar.
|
2017-11-02 13:06:38 +01:00
|
|
|
// See QApplicationPrivate::giveFocusAccordingToFocusPolicy().
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[RightSide]->verticalScrollBar()->setFocusPolicy(Qt::ClickFocus);
|
2017-11-02 13:06:38 +01:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
// Hack #3. Setting the focus policy is not orthogonal to setting the focus proxy and
|
|
|
|
|
// unfortuantely it changes the policy of the proxy too. We bring back the original policy
|
|
|
|
|
// to keep tab focus working.
|
|
|
|
|
m_editor[LeftSide]->setFocusPolicy(Qt::StrongFocus);
|
2017-11-02 13:06:38 +01:00
|
|
|
});
|
2022-09-29 14:25:34 +02:00
|
|
|
connect(m_editor[RightSide], &SideDiffEditorWidget::gotFocus, this, [this] {
|
2017-11-02 13:06:38 +01:00
|
|
|
// Unhack #1.
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[RightSide]->verticalScrollBar()->setFocusProxy(nullptr);
|
2017-11-02 13:06:38 +01:00
|
|
|
// Unhack #2.
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[RightSide]->verticalScrollBar()->setFocusPolicy(Qt::NoFocus);
|
2017-11-02 13:06:38 +01:00
|
|
|
});
|
|
|
|
|
|
2013-09-19 17:59:27 +02:00
|
|
|
connect(TextEditorSettings::instance(),
|
2015-01-28 14:08:42 +01:00
|
|
|
&TextEditorSettings::fontSettingsChanged,
|
|
|
|
|
this, &SideBySideDiffEditorWidget::setFontSettings);
|
2013-09-19 17:59:27 +02:00
|
|
|
setFontSettings(TextEditorSettings::fontSettings());
|
2013-06-05 15:25:42 +02:00
|
|
|
|
2018-05-16 13:28:29 +02:00
|
|
|
syncHorizontalScrollBarPolicy();
|
2017-10-11 14:14:10 +02:00
|
|
|
|
2015-02-03 23:54:47 +02:00
|
|
|
m_splitter = new MiniSplitter(this);
|
2022-09-29 14:25:34 +02:00
|
|
|
m_splitter->addWidget(m_editor[LeftSide]);
|
|
|
|
|
m_splitter->addWidget(m_editor[RightSide]);
|
2013-02-15 12:49:50 +01:00
|
|
|
QVBoxLayout *l = new QVBoxLayout(this);
|
2019-08-29 10:36:01 +02:00
|
|
|
l->setContentsMargins(0, 0, 0, 0);
|
2013-02-15 12:49:50 +01:00
|
|
|
l->addWidget(m_splitter);
|
2022-09-29 14:25:34 +02:00
|
|
|
setFocusProxy(m_editor[LeftSide]);
|
2017-05-09 12:19:11 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-28 14:53:17 +02:00
|
|
|
SideBySideDiffEditorWidget::~SideBySideDiffEditorWidget() = default;
|
2022-09-28 11:32:13 +02:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
TextEditorWidget *SideBySideDiffEditorWidget::sideEditorWidget(DiffSide side) const
|
2017-05-09 12:19:11 +02:00
|
|
|
{
|
2022-09-29 14:25:34 +02:00
|
|
|
return m_editor[side];
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
void SideBySideDiffEditorWidget::setDocument(DiffEditorDocument *document)
|
2013-05-07 14:02:08 +02:00
|
|
|
{
|
2016-07-13 15:55:18 +02:00
|
|
|
m_controller.setDocument(document);
|
2016-11-23 11:20:12 +01:00
|
|
|
clear();
|
|
|
|
|
QList<FileData> diffFileList;
|
2021-08-17 12:16:20 +02:00
|
|
|
if (document)
|
2016-11-23 11:20:12 +01:00
|
|
|
diffFileList = document->diffFiles();
|
2021-08-17 12:16:20 +02:00
|
|
|
setDiff(diffFileList);
|
2016-11-23 11:20:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DiffEditorDocument *SideBySideDiffEditorWidget::diffDocument() const
|
|
|
|
|
{
|
|
|
|
|
return m_controller.document();
|
2013-12-16 16:19:40 +01:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideBySideDiffEditorWidget::clear(const QString &message)
|
2013-05-07 14:02:08 +02:00
|
|
|
{
|
2022-07-22 12:12:33 +02:00
|
|
|
const GuardLocker locker(m_controller.m_ignoreChanges);
|
2022-09-27 15:13:33 +02:00
|
|
|
setDiff({});
|
2022-09-29 14:25:34 +02:00
|
|
|
for (SideDiffEditorWidget *editor : m_editor)
|
|
|
|
|
editor->clearAll(message);
|
2022-10-28 14:53:17 +02:00
|
|
|
if (m_asyncTask) {
|
|
|
|
|
m_asyncTask.reset();
|
2022-09-28 11:32:13 +02:00
|
|
|
m_controller.setBusyShowing(false);
|
|
|
|
|
}
|
2013-05-07 14:02:08 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-17 12:16:20 +02:00
|
|
|
void SideBySideDiffEditorWidget::setDiff(const QList<FileData> &diffFileList)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2022-07-22 12:12:33 +02:00
|
|
|
const GuardLocker locker(m_controller.m_ignoreChanges);
|
2022-09-29 14:25:34 +02:00
|
|
|
for (SideDiffEditorWidget *editor : m_editor)
|
|
|
|
|
editor->clearAll(tr("Waiting for data..."));
|
2015-01-30 16:59:25 +01:00
|
|
|
|
2016-07-13 15:55:18 +02:00
|
|
|
m_controller.m_contextFileData = diffFileList;
|
|
|
|
|
if (m_controller.m_contextFileData.isEmpty()) {
|
2017-07-03 11:26:52 +02:00
|
|
|
const QString msg = tr("No difference.");
|
2022-09-29 14:25:34 +02:00
|
|
|
for (SideDiffEditorWidget *editor : m_editor)
|
|
|
|
|
editor->setPlainText(msg);
|
2015-01-30 16:59:25 +01:00
|
|
|
} else {
|
|
|
|
|
showDiff();
|
|
|
|
|
}
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
void SideBySideDiffEditorWidget::setCurrentDiffFileIndex(int diffFileIndex)
|
2013-05-23 13:36:27 +02:00
|
|
|
{
|
2022-07-22 12:12:33 +02:00
|
|
|
if (m_controller.m_ignoreChanges.isLocked())
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
const int blockNumber = m_editor[LeftSide]->diffData().blockNumberForFileIndex(diffFileIndex);
|
2013-05-23 13:36:27 +02:00
|
|
|
|
2022-07-22 12:12:33 +02:00
|
|
|
const GuardLocker locker(m_controller.m_ignoreChanges);
|
2022-09-29 11:35:17 +02:00
|
|
|
m_controller.setCurrentDiffFileIndex(diffFileIndex);
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
for (SideDiffEditorWidget *editor : m_editor) {
|
|
|
|
|
QTextBlock block = editor->document()->findBlockByNumber(blockNumber);
|
|
|
|
|
QTextCursor cursor = editor->textCursor();
|
|
|
|
|
cursor.setPosition(block.position());
|
|
|
|
|
editor->setTextCursor(cursor);
|
|
|
|
|
editor->verticalScrollBar()->setValue(blockNumber);
|
|
|
|
|
}
|
2013-05-23 13:36:27 +02:00
|
|
|
}
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
void SideBySideDiffEditorWidget::setHorizontalSync(bool sync)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2015-01-30 16:59:25 +01:00
|
|
|
m_horizontalSync = sync;
|
2022-09-29 14:25:34 +02:00
|
|
|
horizontalSliderChanged(RightSide);
|
2015-01-30 16:59:25 +01:00
|
|
|
}
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
void SideBySideDiffEditorWidget::saveState()
|
|
|
|
|
{
|
2022-09-29 14:25:34 +02:00
|
|
|
for (SideDiffEditorWidget *editor : m_editor)
|
|
|
|
|
editor->saveState();
|
2015-01-30 16:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SideBySideDiffEditorWidget::restoreState()
|
|
|
|
|
{
|
2022-09-29 14:25:34 +02:00
|
|
|
for (SideDiffEditorWidget *editor : m_editor)
|
|
|
|
|
editor->restoreState();
|
2015-01-30 16:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SideBySideDiffEditorWidget::showDiff()
|
|
|
|
|
{
|
2022-10-28 14:53:17 +02:00
|
|
|
m_asyncTask.reset(new AsyncTask<ShowResults>());
|
|
|
|
|
m_asyncTask->setFutureSynchronizer(DiffEditorPlugin::futureSynchronizer());
|
2022-09-28 11:32:13 +02:00
|
|
|
m_controller.setBusyShowing(true);
|
|
|
|
|
|
2022-10-28 14:53:17 +02:00
|
|
|
connect(m_asyncTask.get(), &AsyncTaskBase::done, this, [this] {
|
|
|
|
|
if (m_asyncTask->isCanceled()) {
|
2022-09-29 14:25:34 +02:00
|
|
|
for (SideDiffEditorWidget *editor : m_editor)
|
|
|
|
|
editor->clearAll(tr("Retrieving data failed."));
|
2022-09-28 11:32:13 +02:00
|
|
|
} else {
|
2022-10-28 14:53:17 +02:00
|
|
|
const ShowResults results = m_asyncTask->result();
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[LeftSide]->setDiffData(results[LeftSide].diffData);
|
|
|
|
|
m_editor[RightSide]->setDiffData(results[RightSide].diffData);
|
2022-09-28 11:32:13 +02:00
|
|
|
TextDocumentPtr leftDoc(results[LeftSide].textDocument);
|
|
|
|
|
TextDocumentPtr rightDoc(results[RightSide].textDocument);
|
|
|
|
|
{
|
|
|
|
|
const GuardLocker locker(m_controller.m_ignoreChanges);
|
|
|
|
|
// TextDocument was living in no thread, so it's safe to pull it
|
|
|
|
|
leftDoc->moveToThread(thread());
|
|
|
|
|
rightDoc->moveToThread(thread());
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[LeftSide]->setTextDocument(leftDoc);
|
|
|
|
|
m_editor[RightSide]->setTextDocument(rightDoc);
|
2022-09-28 11:32:13 +02:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[LeftSide]->setReadOnly(true);
|
|
|
|
|
m_editor[RightSide]->setReadOnly(true);
|
2022-09-28 11:32:13 +02:00
|
|
|
}
|
2022-10-20 12:03:03 +02:00
|
|
|
auto leftDocumentLayout = qobject_cast<TextDocumentLayout*>(
|
|
|
|
|
m_editor[LeftSide]->document()->documentLayout());
|
|
|
|
|
auto rightDocumentLayout = qobject_cast<TextDocumentLayout*>(
|
|
|
|
|
m_editor[RightSide]->document()->documentLayout());
|
|
|
|
|
if (leftDocumentLayout && rightDocumentLayout) {
|
|
|
|
|
connect(leftDocumentLayout, &TextDocumentLayout::foldChanged,
|
|
|
|
|
m_editor[RightSide], &SideDiffEditorWidget::setFolded);
|
|
|
|
|
connect(rightDocumentLayout, &TextDocumentLayout::foldChanged,
|
|
|
|
|
m_editor[LeftSide], &SideDiffEditorWidget::setFolded);
|
|
|
|
|
}
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[LeftSide]->setSelections(results[LeftSide].selections);
|
|
|
|
|
m_editor[RightSide]->setSelections(results[RightSide].selections);
|
2022-09-29 11:35:17 +02:00
|
|
|
setCurrentDiffFileIndex(m_controller.currentDiffFileIndex());
|
2022-09-28 11:32:13 +02:00
|
|
|
}
|
2022-10-28 14:53:17 +02:00
|
|
|
m_asyncTask.release()->deleteLater();
|
2022-09-28 11:32:13 +02:00
|
|
|
m_controller.setBusyShowing(false);
|
|
|
|
|
});
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2022-09-28 11:32:13 +02:00
|
|
|
const DiffEditorInput input(&m_controller);
|
|
|
|
|
|
|
|
|
|
auto getDocument = [input](QFutureInterface<ShowResults> &futureInterface) {
|
|
|
|
|
auto cleanup = qScopeGuard([&futureInterface] {
|
|
|
|
|
if (futureInterface.isCanceled())
|
|
|
|
|
futureInterface.reportCanceled();
|
|
|
|
|
});
|
2022-09-28 17:28:16 +02:00
|
|
|
const int firstPartMax = 20; // diffOutput is about 4 times quicker than filling document
|
2022-09-28 11:32:13 +02:00
|
|
|
const int leftPartMax = 60;
|
|
|
|
|
const int rightPartMax = 100;
|
|
|
|
|
futureInterface.setProgressRange(0, rightPartMax);
|
|
|
|
|
futureInterface.setProgressValue(0);
|
|
|
|
|
QFutureInterface<void> fi = futureInterface;
|
|
|
|
|
const SideBySideDiffOutput output = SideDiffData::diffOutput(fi, 0, firstPartMax, input);
|
|
|
|
|
if (futureInterface.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const ShowResult leftResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
|
|
|
|
|
output.side[LeftSide].diffData, output.side[LeftSide].selections};
|
|
|
|
|
const ShowResult rightResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
|
|
|
|
|
output.side[RightSide].diffData, output.side[RightSide].selections};
|
|
|
|
|
const ShowResults result{leftResult, rightResult};
|
|
|
|
|
|
|
|
|
|
auto propagateDocument = [&output, &fi](DiffSide side, const ShowResult &result,
|
|
|
|
|
int progressMin, int progressMax) {
|
|
|
|
|
// No need to store the change history
|
|
|
|
|
result.textDocument->document()->setUndoRedoEnabled(false);
|
|
|
|
|
|
|
|
|
|
// We could do just:
|
|
|
|
|
// result.textDocument->setPlainText(output.diffText);
|
|
|
|
|
// but this would freeze the thread for couple of seconds without progress reporting
|
|
|
|
|
// and without checking for canceled.
|
|
|
|
|
const int diffSize = output.side[side].diffText.size();
|
|
|
|
|
const int packageSize = 10000;
|
|
|
|
|
int currentPos = 0;
|
|
|
|
|
QTextCursor cursor(result.textDocument->document());
|
|
|
|
|
while (currentPos < diffSize) {
|
|
|
|
|
const QString package = output.side[side].diffText.mid(currentPos, packageSize);
|
|
|
|
|
cursor.insertText(package);
|
|
|
|
|
currentPos += package.size();
|
2022-11-25 17:31:12 +01:00
|
|
|
fi.setProgressValue(MathUtils::interpolate(currentPos, 0, diffSize, progressMin, progressMax));
|
2022-09-28 11:32:13 +02:00
|
|
|
if (fi.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextBlock block = result.textDocument->document()->firstBlock();
|
|
|
|
|
for (int b = 0; block.isValid(); block = block.next(), ++b)
|
|
|
|
|
SelectableTextEditorWidget::setFoldingIndent(block, output.foldingIndent.value(b, 3));
|
2017-10-11 14:14:10 +02:00
|
|
|
|
2022-09-28 11:32:13 +02:00
|
|
|
// If future was canceled, the destructor runs in this thread, so we can't move it
|
|
|
|
|
// to caller's thread. We push it to no thread (make object to have no thread affinity),
|
|
|
|
|
// and later, in the caller's thread, we pull it back to the caller's thread.
|
|
|
|
|
result.textDocument->moveToThread(nullptr);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
propagateDocument(LeftSide, leftResult, firstPartMax, leftPartMax);
|
|
|
|
|
if (fi.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
propagateDocument(RightSide, rightResult, leftPartMax, rightPartMax);
|
|
|
|
|
if (fi.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
futureInterface.reportResult(result);
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-28 14:53:17 +02:00
|
|
|
m_asyncTask->setAsyncCallData(getDocument);
|
|
|
|
|
m_asyncTask->start();
|
|
|
|
|
ProgressManager::addTask(m_asyncTask->future(), tr("Rendering diff"), "DiffEditor");
|
2013-06-05 15:25:42 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-27 15:48:33 +02:00
|
|
|
void SideBySideDiffEditorWidget::setFontSettings(const FontSettings &fontSettings)
|
2013-06-05 15:25:42 +02:00
|
|
|
{
|
2016-07-13 15:55:18 +02:00
|
|
|
m_controller.setFontSettings(fontSettings);
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
void SideBySideDiffEditorWidget::jumpToOriginalFileRequested(DiffSide side, int diffFileIndex,
|
|
|
|
|
int lineNumber, int columnNumber)
|
2013-07-01 13:15:27 +02:00
|
|
|
{
|
2016-07-13 15:55:18 +02:00
|
|
|
if (diffFileIndex < 0 || diffFileIndex >= m_controller.m_contextFileData.count())
|
2013-07-01 13:15:27 +02:00
|
|
|
return;
|
|
|
|
|
|
2016-07-13 15:55:18 +02:00
|
|
|
const FileData fileData = m_controller.m_contextFileData.at(diffFileIndex);
|
2022-09-29 14:25:34 +02:00
|
|
|
const QString fileName = fileData.fileInfo[side].fileName;
|
|
|
|
|
const DiffSide otherSide = oppositeSide(side);
|
|
|
|
|
const QString otherFileName = fileData.fileInfo[otherSide].fileName;
|
|
|
|
|
if (side == RightSide || fileName != otherFileName) {
|
2013-07-01 13:15:27 +02:00
|
|
|
// different file (e.g. in Tools | Diff...)
|
2022-09-29 14:25:34 +02:00
|
|
|
m_controller.jumpToOriginalFile(fileName, lineNumber, columnNumber);
|
|
|
|
|
return;
|
2013-07-01 13:15:27 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
// The same file (e.g. in git diff), jump to the line number taken from the right editor.
|
|
|
|
|
// Warning: git show SHA^ vs SHA or git diff HEAD vs Index
|
|
|
|
|
// (when Working tree has changed in meantime) will not work properly.
|
|
|
|
|
for (const ChunkData &chunkData : fileData.chunks) {
|
2013-07-01 13:15:27 +02:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
int thisLineNumber = chunkData.startingLineNumber[side];
|
|
|
|
|
int otherLineNumber = chunkData.startingLineNumber[otherSide];
|
2013-07-01 13:15:27 +02:00
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
for (int j = 0; j < chunkData.rows.count(); j++) {
|
|
|
|
|
const RowData rowData = chunkData.rows.at(j);
|
|
|
|
|
if (rowData.line[side].textLineType == TextLineData::TextLine)
|
|
|
|
|
thisLineNumber++;
|
|
|
|
|
if (rowData.line[otherSide].textLineType == TextLineData::TextLine)
|
|
|
|
|
otherLineNumber++;
|
|
|
|
|
if (thisLineNumber == lineNumber) {
|
|
|
|
|
int colNr = rowData.equal ? columnNumber : 0;
|
|
|
|
|
m_controller.jumpToOriginalFile(fileName, otherLineNumber, colNr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
void SideBySideDiffEditorWidget::contextMenuRequested(DiffSide side, QMenu *menu, int fileIndex,
|
|
|
|
|
int chunkIndex, const ChunkSelection &selection)
|
2014-02-13 16:43:28 +01:00
|
|
|
{
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
|
2022-09-29 15:31:27 +02:00
|
|
|
const PatchAction patchAction = side == LeftSide ? PatchAction::Apply : PatchAction::Revert;
|
2018-02-15 10:29:14 +01:00
|
|
|
m_controller.addCodePasterAction(menu, fileIndex, chunkIndex);
|
2022-09-29 15:31:27 +02:00
|
|
|
m_controller.addPatchAction(menu, fileIndex, chunkIndex, patchAction);
|
2017-11-29 21:36:30 +01:00
|
|
|
m_controller.addExtraActions(menu, fileIndex, chunkIndex, selection);
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
void SideBySideDiffEditorWidget::verticalSliderChanged(DiffSide side)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2022-07-22 12:12:33 +02:00
|
|
|
if (m_controller.m_ignoreChanges.isLocked())
|
2018-02-14 12:02:56 +01:00
|
|
|
return;
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[oppositeSide(side)]->verticalScrollBar()->setValue(
|
|
|
|
|
m_editor[side]->verticalScrollBar()->value());
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
void SideBySideDiffEditorWidget::horizontalSliderChanged(DiffSide side)
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
2022-09-29 14:25:34 +02:00
|
|
|
if (m_controller.m_ignoreChanges.isLocked() || !m_horizontalSync)
|
2018-02-14 12:02:56 +01:00
|
|
|
return;
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
m_editor[oppositeSide(side)]->horizontalScrollBar()->setValue(
|
|
|
|
|
m_editor[side]->horizontalScrollBar()->value());
|
2013-02-15 12:49:50 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
void SideBySideDiffEditorWidget::cursorPositionChanged(DiffSide side)
|
2013-05-22 16:33:44 +02:00
|
|
|
{
|
2022-07-22 12:12:33 +02:00
|
|
|
if (m_controller.m_ignoreChanges.isLocked())
|
2018-02-14 12:02:56 +01:00
|
|
|
return;
|
|
|
|
|
|
2022-09-29 14:25:34 +02:00
|
|
|
handlePositionChange(m_editor[side], m_editor[oppositeSide(side)]);
|
|
|
|
|
verticalSliderChanged(side);
|
|
|
|
|
horizontalSliderChanged(side);
|
2017-11-02 13:06:38 +01:00
|
|
|
}
|
2013-12-16 16:19:40 +01:00
|
|
|
|
2018-05-16 13:28:29 +02:00
|
|
|
void SideBySideDiffEditorWidget::syncHorizontalScrollBarPolicy()
|
|
|
|
|
{
|
2022-09-29 14:25:34 +02:00
|
|
|
const bool alwaysOn = m_editor[LeftSide]->horizontalScrollBar()->maximum()
|
|
|
|
|
|| m_editor[RightSide]->horizontalScrollBar()->maximum();
|
|
|
|
|
const Qt::ScrollBarPolicy newPolicy = alwaysOn ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAsNeeded;
|
|
|
|
|
for (SideDiffEditorWidget *editor : m_editor) {
|
|
|
|
|
if (editor->horizontalScrollBarPolicy() != newPolicy)
|
|
|
|
|
editor->setHorizontalScrollBarPolicy(newPolicy);
|
|
|
|
|
}
|
2018-05-16 13:28:29 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-02 13:06:38 +01:00
|
|
|
void SideBySideDiffEditorWidget::handlePositionChange(SideDiffEditorWidget *source, SideDiffEditorWidget *dest)
|
|
|
|
|
{
|
2022-07-22 12:12:33 +02:00
|
|
|
if (m_controller.m_ignoreChanges.isLocked())
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
|
|
|
|
|
2022-09-28 11:32:13 +02:00
|
|
|
const int fileIndex = source->diffData().fileIndexForBlockNumber(source->textCursor().blockNumber());
|
|
|
|
|
if (fileIndex < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-07-22 12:12:33 +02:00
|
|
|
const GuardLocker locker(m_controller.m_ignoreChanges);
|
2017-11-02 13:06:38 +01:00
|
|
|
syncCursor(source, dest);
|
2022-09-29 11:35:17 +02:00
|
|
|
m_controller.setCurrentDiffFileIndex(fileIndex);
|
2022-09-28 11:32:13 +02:00
|
|
|
emit currentDiffFileIndexChanged(fileIndex);
|
2013-05-23 13:36:27 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-02 13:06:38 +01:00
|
|
|
void SideBySideDiffEditorWidget::syncCursor(SideDiffEditorWidget *source, SideDiffEditorWidget *dest)
|
|
|
|
|
{
|
2018-02-14 12:02:56 +01:00
|
|
|
const int oldHSliderPos = dest->horizontalScrollBar()->value();
|
|
|
|
|
|
2017-11-02 13:06:38 +01:00
|
|
|
const QTextCursor sourceCursor = source->textCursor();
|
|
|
|
|
const int sourceLine = sourceCursor.blockNumber();
|
|
|
|
|
const int sourceColumn = sourceCursor.positionInBlock();
|
|
|
|
|
QTextCursor destCursor = dest->textCursor();
|
|
|
|
|
const QTextBlock destBlock = dest->document()->findBlockByNumber(sourceLine);
|
|
|
|
|
const int destColumn = qMin(sourceColumn, destBlock.length());
|
|
|
|
|
const int destPosition = destBlock.position() + destColumn;
|
|
|
|
|
destCursor.setPosition(destPosition);
|
|
|
|
|
dest->setTextCursor(destCursor);
|
|
|
|
|
|
2018-02-14 12:02:56 +01:00
|
|
|
dest->horizontalScrollBar()->setValue(oldHSliderPos);
|
2017-11-02 13:06:38 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-30 14:55:56 +01:00
|
|
|
} // namespace Internal
|
2013-02-21 17:03:00 +01:00
|
|
|
} // namespace DiffEditor
|
2013-02-15 12:49:50 +01:00
|
|
|
|
2014-01-30 14:27:50 +01:00
|
|
|
#include "sidebysidediffeditorwidget.moc"
|