2013-02-15 12:49:50 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
|
|
|
|
** Contact: http://www.qt-project.org/legal
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "diffeditorwidget.h"
|
|
|
|
|
#include <QPlainTextEdit>
|
|
|
|
|
#include <QSplitter>
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
#include <QPlainTextDocumentLayout>
|
|
|
|
|
#include <QTextBlock>
|
|
|
|
|
#include <QScrollBar>
|
|
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QTime>
|
|
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <texteditor/basetexteditor.h>
|
|
|
|
|
#include <texteditor/snippets/snippeteditor.h>
|
|
|
|
|
#include <texteditor/basetextdocumentlayout.h>
|
|
|
|
|
#include <texteditor/syntaxhighlighter.h>
|
|
|
|
|
#include <texteditor/basetextdocument.h>
|
|
|
|
|
#include <texteditor/texteditorsettings.h>
|
|
|
|
|
|
|
|
|
|
using namespace TextEditor;
|
|
|
|
|
|
2013-02-21 17:03:00 +01:00
|
|
|
namespace DiffEditor {
|
2013-02-15 12:49:50 +01:00
|
|
|
|
|
|
|
|
//////////////////////
|
|
|
|
|
|
2013-02-22 11:32:13 +01:00
|
|
|
class DiffViewEditorEditable : public BaseTextEditor
|
2013-02-15 12:49:50 +01:00
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
public:
|
2013-02-22 11:32:13 +01:00
|
|
|
DiffViewEditorEditable(BaseTextEditorWidget *editorWidget) : BaseTextEditor(editorWidget) {}
|
2013-02-15 12:49:50 +01:00
|
|
|
virtual Core::Id id() const { return "DiffViewEditor"; }
|
|
|
|
|
virtual bool duplicateSupported() const { return false; }
|
|
|
|
|
virtual IEditor *duplicate(QWidget *parent) { Q_UNUSED(parent) return 0; }
|
|
|
|
|
virtual bool isTemporary() const { return false; }
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////
|
|
|
|
|
|
|
|
|
|
class DiffViewEditorWidget : public SnippetEditorWidget
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
public:
|
|
|
|
|
DiffViewEditorWidget(QWidget *parent = 0);
|
|
|
|
|
|
|
|
|
|
void setSyntaxHighlighter(SyntaxHighlighter *sh) {
|
|
|
|
|
baseTextDocument()->setSyntaxHighlighter(sh);
|
|
|
|
|
}
|
|
|
|
|
QTextCodec *codec() const {
|
|
|
|
|
return const_cast<QTextCodec *>(baseTextDocument()->codec());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QMap<int, int> skippedLines() const { return m_skippedLines; }
|
|
|
|
|
|
|
|
|
|
void setLineNumber(int blockNumber, const QString &lineNumber);
|
|
|
|
|
void setSkippedLines(int visualLineNumber, int skippedLines) { m_skippedLines.insert(visualLineNumber, skippedLines); }
|
|
|
|
|
void clearLineNumbers();
|
|
|
|
|
void clearSkippedLines() { m_skippedLines.clear(); }
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual int extraAreaWidth(int *markWidthPtr = 0) const { return BaseTextEditorWidget::extraAreaWidth(markWidthPtr); }
|
2013-02-22 11:32:13 +01:00
|
|
|
BaseTextEditor *createEditor() { return new DiffViewEditorEditable(this); }
|
2013-02-15 12:49:50 +01:00
|
|
|
virtual QString lineNumber(int blockNumber) const;
|
|
|
|
|
int lineNumberTopPositionOffset(int blockNumber) const;
|
|
|
|
|
virtual int lineNumberDigits() const;
|
|
|
|
|
virtual void paintEvent(QPaintEvent *e);
|
|
|
|
|
virtual void scrollContentsBy(int dx, int dy);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QMap<int, QString> m_lineNumbers;
|
|
|
|
|
int m_lineNumberDigits;
|
|
|
|
|
QMap<int, int> m_skippedLines;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DiffViewEditorWidget::DiffViewEditorWidget(QWidget *parent)
|
|
|
|
|
: SnippetEditorWidget(parent), m_lineNumberDigits(1)
|
|
|
|
|
{
|
|
|
|
|
setLineNumbersVisible(true);
|
|
|
|
|
setFrameStyle(QFrame::NoFrame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DiffViewEditorWidget::lineNumber(int blockNumber) const
|
|
|
|
|
{
|
|
|
|
|
return m_lineNumbers.value(blockNumber);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int DiffViewEditorWidget::lineNumberTopPositionOffset(int blockNumber) const
|
|
|
|
|
{
|
|
|
|
|
int offset = 0;
|
|
|
|
|
const QFontMetrics fm(extraArea()->font());
|
|
|
|
|
int i = 0;
|
|
|
|
|
const QString text = document()->findBlockByNumber(blockNumber).text();
|
|
|
|
|
while (i < text.count()) {
|
|
|
|
|
if (text.at(i) != QChar::LineSeparator)
|
|
|
|
|
break;
|
|
|
|
|
offset += fm.height();
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
return offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int DiffViewEditorWidget::lineNumberDigits() const
|
|
|
|
|
{
|
|
|
|
|
return m_lineNumberDigits;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffViewEditorWidget::setLineNumber(int blockNumber, const QString &lineNumber)
|
|
|
|
|
{
|
|
|
|
|
m_lineNumbers.insert(blockNumber, lineNumber);
|
|
|
|
|
m_lineNumberDigits = qMax(m_lineNumberDigits, lineNumber.count());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffViewEditorWidget::clearLineNumbers()
|
|
|
|
|
{
|
|
|
|
|
m_lineNumbers.clear();
|
|
|
|
|
m_lineNumberDigits = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffViewEditorWidget::scrollContentsBy(int dx, int dy)
|
|
|
|
|
{
|
|
|
|
|
SnippetEditorWidget::scrollContentsBy(dx, dy);
|
|
|
|
|
// TODO: update only chunk lines
|
|
|
|
|
viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffViewEditorWidget::paintEvent(QPaintEvent *e)
|
|
|
|
|
{
|
|
|
|
|
SnippetEditorWidget::paintEvent(e);
|
|
|
|
|
|
|
|
|
|
QPainter painter(viewport());
|
|
|
|
|
|
|
|
|
|
QPointF offset = contentOffset();
|
|
|
|
|
QTextBlock firstBlock = firstVisibleBlock();
|
|
|
|
|
|
|
|
|
|
QMap<int, int> skipped = skippedLines();
|
|
|
|
|
QMapIterator<int, int> itChunk(skipped);
|
|
|
|
|
while (itChunk.hasNext()) {
|
|
|
|
|
itChunk.next();
|
|
|
|
|
const int line = itChunk.key();
|
|
|
|
|
const int skipped = itChunk.value();
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.movePosition(QTextCursor::Start);
|
|
|
|
|
cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, line);
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
qreal top = blockBoundingGeometry(block).translated(offset).top();
|
|
|
|
|
qreal bottom = top + blockBoundingRect(block).height();
|
|
|
|
|
|
|
|
|
|
if (block.blockNumber() < firstBlock.blockNumber()
|
|
|
|
|
|| top > e->rect().bottom())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (block.isVisible() && bottom >= e->rect().top()) {
|
|
|
|
|
QTextLayout *layout = block.layout();
|
|
|
|
|
const int blockLine = line - block.firstLineNumber();
|
|
|
|
|
const int blockLineCount = layout->lineCount();
|
|
|
|
|
if (blockLine < blockLineCount) {
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.setPen(palette().foreground().color());
|
|
|
|
|
QTextLine textLine = layout->lineAt(blockLine);
|
|
|
|
|
// QRectF lineRect = textLine.naturalTextRect().translated(offset.x(), top);
|
|
|
|
|
QRectF lineRect = textLine.naturalTextRect().translated(0, top);
|
|
|
|
|
QString skippedRowsText = tr("Skipped %n lines...", 0, skipped);
|
|
|
|
|
QFontMetrics fm(font());
|
|
|
|
|
const int textWidth = fm.width(skippedRowsText);
|
|
|
|
|
painter.drawText(QPointF(lineRect.right()
|
|
|
|
|
+ (viewport()->width() - textWidth) / 2.0,
|
|
|
|
|
lineRect.top() + textLine.ascent()),
|
|
|
|
|
skippedRowsText);
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////
|
|
|
|
|
|
|
|
|
|
DiffEditorWidget::DiffEditorWidget(QWidget *parent)
|
|
|
|
|
: QWidget(parent),
|
|
|
|
|
m_contextLinesNumber(1),
|
|
|
|
|
m_ignoreWhitespaces(true),
|
|
|
|
|
m_leftSafePosHack(-1),
|
|
|
|
|
m_rightSafePosHack(-1)
|
|
|
|
|
{
|
|
|
|
|
TextEditor::TextEditorSettings *settings = TextEditorSettings::instance();
|
|
|
|
|
|
|
|
|
|
m_leftEditor = new DiffViewEditorWidget(this);
|
|
|
|
|
m_leftEditor->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
|
m_leftEditor->setReadOnly(true);
|
|
|
|
|
m_leftEditor->setHighlightCurrentLine(false);
|
|
|
|
|
m_leftEditor->setWordWrapMode(QTextOption::NoWrap);
|
|
|
|
|
m_leftEditor->setFontSettings(settings->fontSettings());
|
|
|
|
|
|
|
|
|
|
m_rightEditor = new DiffViewEditorWidget(this);
|
|
|
|
|
m_rightEditor->setReadOnly(true);
|
|
|
|
|
m_rightEditor->setHighlightCurrentLine(false);
|
|
|
|
|
m_rightEditor->setWordWrapMode(QTextOption::NoWrap);
|
|
|
|
|
m_rightEditor->setFontSettings(settings->fontSettings());
|
|
|
|
|
|
|
|
|
|
connect(m_leftEditor->verticalScrollBar(), SIGNAL(valueChanged(int)),
|
|
|
|
|
this, SLOT(leftSliderChanged()));
|
|
|
|
|
connect(m_leftEditor->verticalScrollBar(), SIGNAL(actionTriggered(int)),
|
|
|
|
|
this, SLOT(leftSliderChanged()));
|
|
|
|
|
connect(m_leftEditor, SIGNAL(cursorPositionChanged()),
|
|
|
|
|
this, SLOT(leftSliderChanged()));
|
|
|
|
|
connect(m_rightEditor->verticalScrollBar(), SIGNAL(valueChanged(int)),
|
|
|
|
|
this, SLOT(rightSliderChanged()));
|
|
|
|
|
connect(m_rightEditor->verticalScrollBar(), SIGNAL(actionTriggered(int)),
|
|
|
|
|
this, SLOT(rightSliderChanged()));
|
|
|
|
|
connect(m_rightEditor, SIGNAL(cursorPositionChanged()),
|
|
|
|
|
this, SLOT(rightSliderChanged()));
|
|
|
|
|
|
|
|
|
|
m_splitter = new QSplitter(this);
|
|
|
|
|
m_splitter->addWidget(m_leftEditor);
|
|
|
|
|
m_splitter->addWidget(m_rightEditor);
|
|
|
|
|
QVBoxLayout *l = new QVBoxLayout(this);
|
|
|
|
|
l->addWidget(m_splitter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DiffEditorWidget::~DiffEditorWidget()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidget::setDiff(const QString &leftText, const QString &rightText)
|
|
|
|
|
{
|
|
|
|
|
// QTime time;
|
|
|
|
|
// time.start();
|
|
|
|
|
Differ diffGenerator;
|
|
|
|
|
QList<Diff> list = diffGenerator.diff(leftText, rightText);
|
|
|
|
|
// int ela = time.elapsed();
|
|
|
|
|
// qDebug() << "Time spend in diff:" << ela;
|
|
|
|
|
setDiff(list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidget::setDiff(const QList<Diff> &diffList)
|
|
|
|
|
{
|
|
|
|
|
m_diffList = diffList;
|
|
|
|
|
|
|
|
|
|
QList<Diff> transformedDiffList = m_diffList;
|
|
|
|
|
|
|
|
|
|
m_originalChunkData = calculateOriginalData(transformedDiffList);
|
|
|
|
|
m_contextFileData = calculateContextData(m_originalChunkData);
|
|
|
|
|
showDiff();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidget::setContextLinesNumber(int lines)
|
|
|
|
|
{
|
|
|
|
|
if (m_contextLinesNumber == lines)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_contextLinesNumber = lines;
|
|
|
|
|
m_contextFileData = calculateContextData(m_originalChunkData);
|
|
|
|
|
showDiff();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidget::setIgnoreWhitespaces(bool ignore)
|
|
|
|
|
{
|
|
|
|
|
if (m_ignoreWhitespaces == ignore)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_ignoreWhitespaces = ignore;
|
|
|
|
|
setDiff(m_diffList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCodec *DiffEditorWidget::codec() const
|
|
|
|
|
{
|
|
|
|
|
return const_cast<QTextCodec *>(m_leftEditor->codec());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SnippetEditorWidget *DiffEditorWidget::leftEditor() const
|
|
|
|
|
{
|
|
|
|
|
return m_leftEditor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SnippetEditorWidget *DiffEditorWidget::rightEditor() const
|
|
|
|
|
{
|
|
|
|
|
return m_rightEditor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DiffEditorWidget::isWhitespace(const QChar &c) const
|
|
|
|
|
{
|
|
|
|
|
if (c == QLatin1Char(' ') || c == QLatin1Char('\t'))
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DiffEditorWidget::isWhitespace(const Diff &diff) const
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < diff.text.count(); i++) {
|
|
|
|
|
if (!isWhitespace(diff.text.at(i)))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DiffEditorWidget::isEqual(const QList<Diff> &diffList, int diffNumber) const
|
|
|
|
|
{
|
|
|
|
|
const Diff &diff = diffList.at(diffNumber);
|
|
|
|
|
if (diff.command == Diff::Equal)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (diff.text.count() == 0)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (!m_ignoreWhitespaces)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (isWhitespace(diff) == false)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (diffNumber == 0 || diffNumber == diffList.count() - 1)
|
|
|
|
|
return false; // it's a Diff start or end
|
|
|
|
|
|
|
|
|
|
// Examine previous diff
|
|
|
|
|
if (diffNumber > 0) {
|
|
|
|
|
const Diff &previousDiff = diffList.at(diffNumber - 1);
|
|
|
|
|
if (previousDiff.command == Diff::Equal) {
|
|
|
|
|
const int previousDiffCount = previousDiff.text.count();
|
|
|
|
|
if (previousDiffCount && isWhitespace(previousDiff.text.at(previousDiffCount - 1)))
|
|
|
|
|
return true;
|
|
|
|
|
} else if (diff.command != previousDiff.command
|
|
|
|
|
&& isWhitespace(previousDiff)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Examine next diff
|
|
|
|
|
if (diffNumber < diffList.count() - 1) {
|
|
|
|
|
const Diff &nextDiff = diffList.at(diffNumber + 1);
|
|
|
|
|
if (nextDiff.command == Diff::Equal) {
|
|
|
|
|
const int nextDiffCount = nextDiff.text.count();
|
|
|
|
|
if (nextDiffCount && isWhitespace(nextDiff.text.at(0)))
|
|
|
|
|
return true;
|
|
|
|
|
} else if (diff.command != nextDiff.command
|
|
|
|
|
&& isWhitespace(nextDiff)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ChunkData DiffEditorWidget::calculateOriginalData(const QList<Diff> &diffList) const
|
|
|
|
|
{
|
|
|
|
|
ChunkData chunkData;
|
|
|
|
|
|
|
|
|
|
QStringList leftLines;
|
|
|
|
|
QStringList rightLines;
|
|
|
|
|
leftLines.append(QString());
|
|
|
|
|
rightLines.append(QString());
|
|
|
|
|
QMap<int, int> leftLineSpans;
|
|
|
|
|
QMap<int, int> rightLineSpans;
|
|
|
|
|
QMap<int, int> leftChangedPositions;
|
|
|
|
|
QMap<int, int> rightChangedPositions;
|
|
|
|
|
QList<int> leftEqualLines;
|
|
|
|
|
QList<int> rightEqualLines;
|
|
|
|
|
|
|
|
|
|
int currentLeftLine = 0;
|
|
|
|
|
int currentLeftPos = 0;
|
|
|
|
|
int currentLeftLineOffset = 0;
|
|
|
|
|
int currentRightLine = 0;
|
|
|
|
|
int currentRightPos = 0;
|
|
|
|
|
int currentRightLineOffset = 0;
|
|
|
|
|
int lastAlignedLeftLine = -1;
|
|
|
|
|
int lastAlignedRightLine = -1;
|
|
|
|
|
bool lastLeftLineEqual = true;
|
|
|
|
|
bool lastRightLineEqual = true;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < diffList.count(); i++) {
|
|
|
|
|
Diff diff = diffList.at(i);
|
|
|
|
|
|
|
|
|
|
const QStringList lines = diff.text.split(QLatin1Char('\n'));
|
|
|
|
|
|
|
|
|
|
const bool equal = isEqual(diffList, i);
|
|
|
|
|
if (diff.command == Diff::Insert) {
|
|
|
|
|
lastRightLineEqual = lastRightLineEqual ? equal : false;
|
|
|
|
|
} else if (diff.command == Diff::Delete) {
|
|
|
|
|
lastLeftLineEqual = lastLeftLineEqual ? equal : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int lastLeftPos = currentLeftPos;
|
|
|
|
|
const int lastRightPos = currentRightPos;
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < lines.count(); j++) {
|
|
|
|
|
const QString line = lines.at(j);
|
|
|
|
|
|
|
|
|
|
if (j > 0) {
|
|
|
|
|
if (diff.command == Diff::Equal) {
|
|
|
|
|
if (lastLeftLineEqual && lastRightLineEqual) {
|
|
|
|
|
leftEqualLines.append(currentLeftLine);
|
|
|
|
|
rightEqualLines.append(currentRightLine);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (diff.command != Diff::Insert) {
|
|
|
|
|
currentLeftLine++;
|
|
|
|
|
currentLeftLineOffset++;
|
|
|
|
|
leftLines.append(QString());
|
|
|
|
|
currentLeftPos++;
|
|
|
|
|
lastLeftLineEqual = line.count() ? equal : true;
|
|
|
|
|
}
|
|
|
|
|
if (diff.command != Diff::Delete) {
|
|
|
|
|
currentRightLine++;
|
|
|
|
|
currentRightLineOffset++;
|
|
|
|
|
rightLines.append(QString());
|
|
|
|
|
currentRightPos++;
|
|
|
|
|
lastRightLineEqual = line.count() ? equal : true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (diff.command == Diff::Delete) {
|
|
|
|
|
leftLines.last() += line;
|
|
|
|
|
currentLeftPos += line.count();
|
|
|
|
|
}
|
|
|
|
|
else if (diff.command == Diff::Insert) {
|
|
|
|
|
rightLines.last() += line;
|
|
|
|
|
currentRightPos += line.count();
|
|
|
|
|
}
|
|
|
|
|
else if (diff.command == Diff::Equal) {
|
|
|
|
|
if ((line.count() || (j && j < lines.count() - 1)) && // don't treat empty ending line as a line to be aligned unless a line is a one char '/n' only.
|
|
|
|
|
currentLeftLine != lastAlignedLeftLine &&
|
|
|
|
|
currentRightLine != lastAlignedRightLine) {
|
|
|
|
|
// apply line spans before the current lines
|
|
|
|
|
if (currentLeftLineOffset < currentRightLineOffset) {
|
|
|
|
|
const int spans = currentRightLineOffset - currentLeftLineOffset;
|
|
|
|
|
leftLineSpans[currentLeftLine] = spans;
|
|
|
|
|
// currentLeftPos += spans;
|
|
|
|
|
} else if (currentRightLineOffset < currentLeftLineOffset) {
|
|
|
|
|
const int spans = currentLeftLineOffset - currentRightLineOffset;
|
|
|
|
|
rightLineSpans[currentRightLine] = spans;
|
|
|
|
|
// currentRightPos += spans;
|
|
|
|
|
}
|
|
|
|
|
currentLeftLineOffset = 0;
|
|
|
|
|
currentRightLineOffset = 0;
|
|
|
|
|
lastAlignedLeftLine = currentLeftLine;
|
|
|
|
|
lastAlignedRightLine = currentRightLine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leftLines.last() += line;
|
|
|
|
|
rightLines.last() += line;
|
|
|
|
|
currentLeftPos += line.count();
|
|
|
|
|
currentRightPos += line.count();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!equal) {
|
|
|
|
|
if (diff.command == Diff::Delete && lastLeftPos != currentLeftPos)
|
|
|
|
|
leftChangedPositions.insert(lastLeftPos, currentLeftPos);
|
|
|
|
|
else if (diff.command == Diff::Insert && lastRightPos != currentRightPos)
|
|
|
|
|
rightChangedPositions.insert(lastRightPos, currentRightPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (diffList.count() && diffList.last().command == Diff::Equal) {
|
|
|
|
|
if (lastLeftLineEqual && lastRightLineEqual) {
|
|
|
|
|
leftEqualLines.append(currentLeftLine);
|
|
|
|
|
rightEqualLines.append(currentRightLine);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<TextLineData> leftData;
|
|
|
|
|
int spanOffset = 0;
|
|
|
|
|
int pos = 0;
|
|
|
|
|
QMap<int, int>::ConstIterator leftChangedIt = leftChangedPositions.constBegin();
|
|
|
|
|
for (int i = 0; i < leftLines.count(); i++) {
|
|
|
|
|
for (int j = 0; j < leftLineSpans.value(i); j++) {
|
|
|
|
|
leftData.append(TextLineData(TextLineData::Separator));
|
|
|
|
|
spanOffset++;
|
|
|
|
|
}
|
|
|
|
|
const int textLength = leftLines.at(i).count() + 1;
|
|
|
|
|
pos += textLength;
|
|
|
|
|
leftData.append(leftLines.at(i));
|
|
|
|
|
while (leftChangedIt != leftChangedPositions.constEnd()) {
|
|
|
|
|
if (leftChangedIt.key() >= pos)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const int startPos = leftChangedIt.key() + spanOffset;
|
|
|
|
|
const int endPos = leftChangedIt.value() + spanOffset;
|
|
|
|
|
chunkData.changedLeftPositions.insert(startPos, endPos);
|
|
|
|
|
leftChangedIt++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (leftChangedIt != leftChangedPositions.constEnd()) {
|
|
|
|
|
if (leftChangedIt.key() >= pos)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const int startPos = leftChangedIt.key() + spanOffset;
|
|
|
|
|
const int endPos = leftChangedIt.value() + spanOffset;
|
|
|
|
|
chunkData.changedLeftPositions.insert(startPos, endPos);
|
|
|
|
|
leftChangedIt++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<TextLineData> rightData;
|
|
|
|
|
spanOffset = 0;
|
|
|
|
|
pos = 0;
|
|
|
|
|
QMap<int, int>::ConstIterator rightChangedIt = rightChangedPositions.constBegin();
|
|
|
|
|
for (int i = 0; i < rightLines.count(); i++) {
|
|
|
|
|
for (int j = 0; j < rightLineSpans.value(i); j++) {
|
|
|
|
|
rightData.append(TextLineData(TextLineData::Separator));
|
|
|
|
|
spanOffset++;
|
|
|
|
|
}
|
|
|
|
|
const int textLength = rightLines.at(i).count() + 1;
|
|
|
|
|
pos += textLength;
|
|
|
|
|
rightData.append(rightLines.at(i));
|
|
|
|
|
while (rightChangedIt != rightChangedPositions.constEnd()) {
|
|
|
|
|
if (rightChangedIt.key() >= pos)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const int startPos = rightChangedIt.key() + spanOffset;
|
|
|
|
|
const int endPos = rightChangedIt.value() + spanOffset;
|
|
|
|
|
chunkData.changedRightPositions.insert(startPos, endPos);
|
|
|
|
|
rightChangedIt++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (rightChangedIt != rightChangedPositions.constEnd()) {
|
|
|
|
|
if (rightChangedIt.key() >= pos)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const int startPos = rightChangedIt.key() + spanOffset;
|
|
|
|
|
const int endPos = rightChangedIt.value() + spanOffset;
|
|
|
|
|
chunkData.changedRightPositions.insert(startPos, endPos);
|
|
|
|
|
rightChangedIt++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fill ending separators
|
|
|
|
|
for (int i = leftData.count(); i < rightData.count(); i++)
|
|
|
|
|
leftData.append(TextLineData(TextLineData::Separator));
|
|
|
|
|
for (int i = rightData.count(); i < leftData.count(); i++)
|
|
|
|
|
rightData.append(TextLineData(TextLineData::Separator));
|
|
|
|
|
|
|
|
|
|
const int visualLineCount = leftData.count();
|
|
|
|
|
int l = 0;
|
|
|
|
|
int r = 0;
|
|
|
|
|
for (int i = 0; i < visualLineCount; i++) {
|
|
|
|
|
RowData row(leftData.at(i), rightData.at(i));
|
|
|
|
|
if (row.leftLine.textLineType == TextLineData::Separator
|
|
|
|
|
&& row.rightLine.textLineType == TextLineData::Separator)
|
|
|
|
|
row.equal = true;
|
|
|
|
|
if (row.leftLine.textLineType == TextLineData::TextLine
|
|
|
|
|
&& row.rightLine.textLineType == TextLineData::TextLine
|
|
|
|
|
&& leftEqualLines.contains(l)
|
|
|
|
|
&& rightEqualLines.contains(r))
|
|
|
|
|
row.equal = true;
|
|
|
|
|
chunkData.rows.append(row);
|
|
|
|
|
if (leftData.at(i).textLineType == TextLineData::TextLine)
|
|
|
|
|
l++;
|
|
|
|
|
if (rightData.at(i).textLineType == TextLineData::TextLine)
|
|
|
|
|
r++;
|
|
|
|
|
}
|
|
|
|
|
return chunkData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) const
|
|
|
|
|
{
|
|
|
|
|
if (m_contextLinesNumber < 0)
|
|
|
|
|
return FileData(originalData);
|
|
|
|
|
|
|
|
|
|
const int joinChunkThreshold = 0;
|
|
|
|
|
|
|
|
|
|
FileData fileData;
|
|
|
|
|
QMap<int, bool> hiddenRows;
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (i < originalData.rows.count()) {
|
|
|
|
|
const RowData &row = originalData.rows[i];
|
|
|
|
|
if (row.equal) {
|
|
|
|
|
// count how many equal
|
|
|
|
|
int equalRowStart = i;
|
|
|
|
|
i++;
|
|
|
|
|
while (i < originalData.rows.count()) {
|
|
|
|
|
if (!originalData.rows.at(i).equal)
|
|
|
|
|
break;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
const bool first = equalRowStart == 0; // includes first line?
|
|
|
|
|
const bool last = i == originalData.rows.count(); // includes last line?
|
|
|
|
|
|
|
|
|
|
const int firstLine = first ? 0 : equalRowStart + m_contextLinesNumber;
|
|
|
|
|
const int lastLine = last ? originalData.rows.count() : i - m_contextLinesNumber;
|
|
|
|
|
|
|
|
|
|
if (firstLine < lastLine - joinChunkThreshold) {
|
|
|
|
|
for (int j = firstLine; j < lastLine; j++) {
|
|
|
|
|
hiddenRows.insert(j, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// iterate to the next row
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i = 0;
|
|
|
|
|
int skippedLines = 0;
|
|
|
|
|
int leftCharCounter = 0;
|
|
|
|
|
int rightCharCounter = 0;
|
|
|
|
|
int leftCharSkipped = 0;
|
|
|
|
|
int rightCharSkipped = 0;
|
|
|
|
|
int chunkOffset = 0;
|
|
|
|
|
QMap<int, int>::ConstIterator leftChangedIt = originalData.changedLeftPositions.constBegin();
|
|
|
|
|
QMap<int, int>::ConstIterator rightChangedIt = originalData.changedRightPositions.constBegin();
|
|
|
|
|
while (i < originalData.rows.count()) {
|
|
|
|
|
if (!hiddenRows.contains(i)) {
|
|
|
|
|
ChunkData chunkData;
|
|
|
|
|
if (skippedLines)
|
|
|
|
|
chunkOffset++; // for chunk line
|
|
|
|
|
chunkData.leftOffset = leftCharCounter;
|
|
|
|
|
chunkData.rightOffset = rightCharCounter;
|
|
|
|
|
chunkData.skippedLinesBefore = skippedLines;
|
|
|
|
|
while (i < originalData.rows.count()) {
|
|
|
|
|
if (hiddenRows.contains(i))
|
|
|
|
|
break;
|
|
|
|
|
RowData rowData = originalData.rows.at(i);
|
|
|
|
|
chunkData.rows.append(rowData);
|
|
|
|
|
|
|
|
|
|
leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
|
|
|
|
|
rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
while (leftChangedIt != originalData.changedLeftPositions.constEnd()) {
|
|
|
|
|
if (leftChangedIt.key() < chunkData.leftOffset
|
|
|
|
|
|| leftChangedIt.key() > leftCharCounter)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const int startPos = leftChangedIt.key() - leftCharSkipped + chunkOffset;
|
|
|
|
|
const int endPos = leftChangedIt.value() - leftCharSkipped + chunkOffset;
|
|
|
|
|
chunkData.changedLeftPositions.insert(startPos, endPos);
|
|
|
|
|
leftChangedIt++;
|
|
|
|
|
}
|
|
|
|
|
while (rightChangedIt != originalData.changedRightPositions.constEnd()) {
|
|
|
|
|
if (rightChangedIt.key() < chunkData.rightOffset
|
|
|
|
|
|| rightChangedIt.key() > rightCharCounter)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const int startPos = rightChangedIt.key() - rightCharSkipped + chunkOffset;
|
|
|
|
|
const int endPos = rightChangedIt.value() - rightCharSkipped + chunkOffset;
|
|
|
|
|
chunkData.changedRightPositions.insert(startPos, endPos);
|
|
|
|
|
rightChangedIt++;
|
|
|
|
|
}
|
|
|
|
|
fileData.chunks.append(chunkData);
|
|
|
|
|
skippedLines = 0;
|
|
|
|
|
} else {
|
|
|
|
|
const int leftChars = originalData.rows.at(i).leftLine.text.count() + 1;
|
|
|
|
|
const int rightChars = originalData.rows.at(i).rightLine.text.count() + 1;
|
|
|
|
|
leftCharCounter += leftChars;
|
|
|
|
|
rightCharCounter += rightChars;
|
|
|
|
|
leftCharSkipped += leftChars;
|
|
|
|
|
rightCharSkipped += rightChars;
|
|
|
|
|
i++;
|
|
|
|
|
skippedLines++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (skippedLines) {
|
|
|
|
|
ChunkData chunkData;
|
|
|
|
|
chunkData.leftOffset = leftCharCounter;
|
|
|
|
|
chunkData.rightOffset = rightCharCounter;
|
|
|
|
|
chunkData.skippedLinesBefore = skippedLines;
|
|
|
|
|
fileData.chunks.append(chunkData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fileData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidget::showDiff()
|
|
|
|
|
{
|
|
|
|
|
QTime time;
|
|
|
|
|
time.start();
|
|
|
|
|
const int verticalValue = m_leftEditor->verticalScrollBar()->value();
|
|
|
|
|
const int leftHorizontalValue = m_leftEditor->horizontalScrollBar()->value();
|
|
|
|
|
const int rightHorizontalValue = m_rightEditor->horizontalScrollBar()->value();
|
|
|
|
|
|
|
|
|
|
// Ugly hack starts here
|
|
|
|
|
// When the cursor stays on the line with QChar::LineSeparator
|
|
|
|
|
// the new inserted text will have the background taken from that line.
|
|
|
|
|
if (m_leftSafePosHack >= 0) {
|
|
|
|
|
QTextCursor cursor = m_leftEditor->textCursor();
|
|
|
|
|
cursor.setPosition(m_leftSafePosHack);
|
|
|
|
|
m_leftEditor->setTextCursor(cursor);
|
|
|
|
|
m_leftSafePosHack = -1;
|
|
|
|
|
}
|
|
|
|
|
if (m_rightSafePosHack >= 0) {
|
|
|
|
|
QTextCursor cursor = m_rightEditor->textCursor();
|
|
|
|
|
cursor.setPosition(m_rightSafePosHack);
|
|
|
|
|
m_rightEditor->setTextCursor(cursor);
|
|
|
|
|
m_rightSafePosHack = -1;
|
|
|
|
|
}
|
|
|
|
|
// Ugly hack ends here
|
|
|
|
|
|
|
|
|
|
m_leftEditor->clear();
|
|
|
|
|
m_rightEditor->clear();
|
|
|
|
|
m_leftEditor->clearLineNumbers();
|
|
|
|
|
m_rightEditor->clearLineNumbers();
|
|
|
|
|
m_leftEditor->clearSkippedLines();
|
|
|
|
|
m_rightEditor->clearSkippedLines();
|
|
|
|
|
int ela1 = time.elapsed();
|
|
|
|
|
|
|
|
|
|
QString leftText, rightText;
|
|
|
|
|
bool leftStartWithNewLine = false;
|
|
|
|
|
bool rightStartWithNewLine = false;
|
|
|
|
|
int leftLineNumber = 0;
|
|
|
|
|
int leftBlockNumber = 0;
|
|
|
|
|
int rightLineNumber = 0;
|
|
|
|
|
int rightBlockNumber = 0;
|
|
|
|
|
int line = 0;
|
|
|
|
|
int leftPos = 0;
|
|
|
|
|
int rightPos = 0;
|
|
|
|
|
for (int i = 0; i < m_contextFileData.chunks.count(); i++) {
|
|
|
|
|
ChunkData chunkData = m_contextFileData.chunks.at(i);
|
|
|
|
|
int leftSeparatorCount = 0;
|
|
|
|
|
int rightSeparatorCount = 0;
|
|
|
|
|
leftLineNumber += chunkData.skippedLinesBefore;
|
|
|
|
|
rightLineNumber += chunkData.skippedLinesBefore;
|
|
|
|
|
|
|
|
|
|
if (chunkData.skippedLinesBefore) {
|
|
|
|
|
if (leftStartWithNewLine) {
|
|
|
|
|
leftText += QLatin1Char('\n');
|
|
|
|
|
leftStartWithNewLine = false;
|
|
|
|
|
leftPos++;
|
|
|
|
|
}
|
|
|
|
|
leftText += QChar::LineSeparator; // for chunk description
|
|
|
|
|
leftPos++;
|
|
|
|
|
m_leftEditor->setSkippedLines(line, chunkData.skippedLinesBefore);
|
|
|
|
|
if (rightStartWithNewLine) {
|
|
|
|
|
rightText += QLatin1Char('\n');
|
|
|
|
|
rightStartWithNewLine = false;
|
|
|
|
|
rightPos++;
|
|
|
|
|
}
|
|
|
|
|
rightText += QChar::LineSeparator; // for chunk description
|
|
|
|
|
rightPos++;
|
|
|
|
|
m_rightEditor->setSkippedLines(line, chunkData.skippedLinesBefore);
|
|
|
|
|
|
|
|
|
|
line++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < chunkData.rows.count(); j++) {
|
|
|
|
|
line++;
|
|
|
|
|
RowData rowData = chunkData.rows.at(j);
|
|
|
|
|
TextLineData leftLineData = rowData.leftLine;
|
|
|
|
|
TextLineData rightLineData = rowData.rightLine;
|
|
|
|
|
|
|
|
|
|
if (leftLineData.textLineType == TextLineData::TextLine) {
|
|
|
|
|
if (leftStartWithNewLine) {
|
|
|
|
|
leftText += QLatin1Char('\n');
|
|
|
|
|
leftPos++;
|
|
|
|
|
}
|
|
|
|
|
if (leftSeparatorCount) {
|
|
|
|
|
QString separators;
|
|
|
|
|
separators.fill(QChar::LineSeparator, leftSeparatorCount);
|
|
|
|
|
leftText += separators;
|
|
|
|
|
leftSeparatorCount = 0;
|
|
|
|
|
leftPos += leftSeparatorCount;
|
|
|
|
|
}
|
|
|
|
|
if (m_leftSafePosHack < 0)
|
|
|
|
|
m_leftSafePosHack = leftPos;
|
|
|
|
|
leftPos += leftLineData.text.count();
|
|
|
|
|
leftText += leftLineData.text;
|
|
|
|
|
leftStartWithNewLine = true;
|
|
|
|
|
leftLineNumber++;
|
|
|
|
|
m_leftEditor->setLineNumber(leftBlockNumber, QString::number(leftLineNumber));
|
|
|
|
|
leftBlockNumber++;
|
|
|
|
|
} else if (leftLineData.textLineType == TextLineData::Separator) {
|
|
|
|
|
leftSeparatorCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (j == chunkData.rows.count() - 1 && leftSeparatorCount) {
|
|
|
|
|
if (leftStartWithNewLine) {
|
|
|
|
|
leftText += QLatin1Char('\n');
|
|
|
|
|
leftPos++;
|
|
|
|
|
}
|
|
|
|
|
QString separators;
|
|
|
|
|
separators.fill(QChar::LineSeparator, leftSeparatorCount);
|
|
|
|
|
leftText += separators;
|
|
|
|
|
leftStartWithNewLine = false;
|
|
|
|
|
leftPos += leftSeparatorCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rightLineData.textLineType == TextLineData::TextLine) {
|
|
|
|
|
if (rightStartWithNewLine) {
|
|
|
|
|
rightText += QLatin1Char('\n');
|
|
|
|
|
rightPos++;
|
|
|
|
|
}
|
|
|
|
|
if (rightSeparatorCount) {
|
|
|
|
|
QString separators;
|
|
|
|
|
separators.fill(QChar::LineSeparator, rightSeparatorCount);
|
|
|
|
|
rightText += separators;
|
|
|
|
|
rightSeparatorCount = 0;
|
|
|
|
|
rightPos += rightSeparatorCount;
|
|
|
|
|
}
|
|
|
|
|
if (m_rightSafePosHack < 0)
|
|
|
|
|
m_rightSafePosHack = rightPos;
|
|
|
|
|
rightPos += leftLineData.text.count();
|
|
|
|
|
rightText += rightLineData.text;
|
|
|
|
|
rightStartWithNewLine = true;
|
|
|
|
|
rightLineNumber++;
|
|
|
|
|
m_rightEditor->setLineNumber(rightBlockNumber, QString::number(rightLineNumber));
|
|
|
|
|
rightBlockNumber++;
|
|
|
|
|
} else if (rightLineData.textLineType == TextLineData::Separator) {
|
|
|
|
|
rightSeparatorCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (j == chunkData.rows.count() - 1 && rightSeparatorCount) {
|
|
|
|
|
if (rightStartWithNewLine) {
|
|
|
|
|
rightText += QLatin1Char('\n');
|
|
|
|
|
rightPos++;
|
|
|
|
|
}
|
|
|
|
|
QString separators;
|
|
|
|
|
separators.fill(QChar::LineSeparator, rightSeparatorCount);
|
|
|
|
|
rightText += separators;
|
|
|
|
|
rightStartWithNewLine = false;
|
|
|
|
|
rightPos += rightSeparatorCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int ela2 = time.elapsed();
|
|
|
|
|
|
|
|
|
|
m_leftEditor->setPlainText(leftText + QLatin1String("\n"));
|
|
|
|
|
m_rightEditor->setPlainText(rightText + QLatin1String("\n"));
|
|
|
|
|
int ela3 = time.elapsed();
|
|
|
|
|
|
|
|
|
|
QTextBlock bl;
|
|
|
|
|
bl = m_leftEditor->document()->lastBlock();
|
|
|
|
|
bl.setVisible(false);
|
|
|
|
|
bl.setLineCount(0);
|
|
|
|
|
bl = m_rightEditor->document()->lastBlock();
|
|
|
|
|
bl.setVisible(false);
|
|
|
|
|
bl.setLineCount(0);
|
|
|
|
|
|
|
|
|
|
int ela4 = time.elapsed();
|
|
|
|
|
|
|
|
|
|
colorDiff(m_contextFileData);
|
|
|
|
|
int ela5 = time.elapsed();
|
|
|
|
|
|
|
|
|
|
m_leftEditor->verticalScrollBar()->setValue(verticalValue);
|
|
|
|
|
m_rightEditor->verticalScrollBar()->setValue(verticalValue);
|
|
|
|
|
m_leftEditor->horizontalScrollBar()->setValue(leftHorizontalValue);
|
|
|
|
|
m_rightEditor->horizontalScrollBar()->setValue(rightHorizontalValue);
|
|
|
|
|
int ela6 = time.elapsed();
|
|
|
|
|
qDebug() << ela1 << ela2 << ela3 << ela4 << ela5 << ela6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<QTextEdit::ExtraSelection> DiffEditorWidget::colorPositions(
|
|
|
|
|
const QTextCharFormat &format,
|
|
|
|
|
QTextCursor &cursor,
|
|
|
|
|
const QMap<int, int> &positions) const
|
|
|
|
|
{
|
|
|
|
|
QList<QTextEdit::ExtraSelection> lineSelections;
|
|
|
|
|
|
|
|
|
|
cursor.setPosition(0);
|
|
|
|
|
QMapIterator<int, int> itPositions(positions);
|
|
|
|
|
while (itPositions.hasNext()) {
|
|
|
|
|
itPositions.next();
|
|
|
|
|
|
|
|
|
|
cursor.setPosition(itPositions.key());
|
|
|
|
|
cursor.setPosition(itPositions.value(), QTextCursor::KeepAnchor);
|
|
|
|
|
|
|
|
|
|
QTextEdit::ExtraSelection selection;
|
|
|
|
|
selection.cursor = cursor;
|
|
|
|
|
selection.format = format;
|
|
|
|
|
lineSelections.append(selection);
|
|
|
|
|
}
|
|
|
|
|
return lineSelections;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidget::colorDiff(const FileData &fileData)
|
|
|
|
|
{
|
|
|
|
|
QTextCharFormat leftLineFormat;
|
|
|
|
|
leftLineFormat.setBackground(QColor(255, 223, 223));
|
|
|
|
|
leftLineFormat.setProperty(QTextFormat::FullWidthSelection, true);
|
|
|
|
|
|
|
|
|
|
QTextCharFormat leftCharFormat;
|
|
|
|
|
leftCharFormat.setBackground(QColor(255, 175, 175));
|
|
|
|
|
leftCharFormat.setProperty(QTextFormat::FullWidthSelection, true);
|
|
|
|
|
|
|
|
|
|
QTextCharFormat rightLineFormat;
|
|
|
|
|
rightLineFormat.setBackground(QColor(223, 255, 223));
|
|
|
|
|
rightLineFormat.setProperty(QTextFormat::FullWidthSelection, true);
|
|
|
|
|
|
|
|
|
|
QTextCharFormat rightCharFormat;
|
|
|
|
|
rightCharFormat.setBackground(QColor(175, 255, 175));
|
|
|
|
|
rightCharFormat.setProperty(QTextFormat::FullWidthSelection, true);
|
|
|
|
|
|
|
|
|
|
QPalette pal = m_leftEditor->extraArea()->palette();
|
|
|
|
|
pal.setCurrentColorGroup(QPalette::Active);
|
|
|
|
|
QTextCharFormat spanLineFormat;
|
|
|
|
|
spanLineFormat.setBackground(pal.color(QPalette::Background));
|
|
|
|
|
spanLineFormat.setProperty(QTextFormat::FullWidthSelection, true);
|
|
|
|
|
|
|
|
|
|
QTextCharFormat chunkLineFormat;
|
|
|
|
|
chunkLineFormat.setBackground(QColor(175, 215, 231));
|
|
|
|
|
chunkLineFormat.setProperty(QTextFormat::FullWidthSelection, true);
|
|
|
|
|
|
|
|
|
|
int leftPos = 0;
|
|
|
|
|
int rightPos = 0;
|
|
|
|
|
// startPos, endPos
|
|
|
|
|
QMap<int, int> leftChangedPos;
|
|
|
|
|
QMap<int, int> rightChangedPos;
|
|
|
|
|
QMap<int, int> leftSkippedPos;
|
|
|
|
|
QMap<int, int> rightSkippedPos;
|
|
|
|
|
QMap<int, int> leftChunkPos;
|
|
|
|
|
QMap<int, int> rightChunkPos;
|
|
|
|
|
int leftLastDiffBlockStartPos = 0;
|
|
|
|
|
int rightLastDiffBlockStartPos = 0;
|
|
|
|
|
int leftLastSkippedBlockStartPos = 0;
|
|
|
|
|
int rightLastSkippedBlockStartPos = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < fileData.chunks.count(); i++) {
|
|
|
|
|
ChunkData chunkData = fileData.chunks.at(i);
|
|
|
|
|
if (chunkData.skippedLinesBefore) {
|
|
|
|
|
leftChunkPos[leftPos] = leftPos + 1;
|
|
|
|
|
rightChunkPos[rightPos] = rightPos + 1;
|
|
|
|
|
leftPos++; // for chunk line
|
|
|
|
|
rightPos++; // for chunk line
|
|
|
|
|
}
|
|
|
|
|
leftLastDiffBlockStartPos = leftPos;
|
|
|
|
|
rightLastDiffBlockStartPos = rightPos;
|
|
|
|
|
leftLastSkippedBlockStartPos = leftPos;
|
|
|
|
|
rightLastSkippedBlockStartPos = rightPos;
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < chunkData.rows.count(); j++) {
|
|
|
|
|
RowData rowData = chunkData.rows.at(j);
|
|
|
|
|
|
|
|
|
|
leftPos += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
|
|
|
|
|
rightPos += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
|
|
|
|
|
|
|
|
|
|
if (!rowData.equal) {
|
|
|
|
|
if (rowData.leftLine.textLineType == TextLineData::TextLine) {
|
|
|
|
|
leftChangedPos[leftLastDiffBlockStartPos] = leftPos;
|
|
|
|
|
leftLastSkippedBlockStartPos = leftPos;
|
|
|
|
|
} else {
|
|
|
|
|
leftSkippedPos[leftLastSkippedBlockStartPos] = leftPos;
|
|
|
|
|
leftLastDiffBlockStartPos = leftPos;
|
|
|
|
|
}
|
|
|
|
|
if (rowData.rightLine.textLineType == TextLineData::TextLine) {
|
|
|
|
|
rightChangedPos[rightLastDiffBlockStartPos] = rightPos;
|
|
|
|
|
rightLastSkippedBlockStartPos = rightPos;
|
|
|
|
|
} else {
|
|
|
|
|
rightSkippedPos[rightLastSkippedBlockStartPos] = rightPos;
|
|
|
|
|
rightLastDiffBlockStartPos = rightPos;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
leftLastDiffBlockStartPos = leftPos;
|
|
|
|
|
rightLastDiffBlockStartPos = rightPos;
|
|
|
|
|
leftLastSkippedBlockStartPos = leftPos;
|
|
|
|
|
rightLastSkippedBlockStartPos = rightPos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor leftCursor = m_leftEditor->textCursor();
|
|
|
|
|
QTextCursor rightCursor = m_rightEditor->textCursor();
|
|
|
|
|
|
|
|
|
|
QList<QTextEdit::ExtraSelection> leftSelections
|
|
|
|
|
= colorPositions(leftLineFormat, leftCursor, leftChangedPos);
|
|
|
|
|
leftSelections
|
|
|
|
|
+= colorPositions(spanLineFormat, leftCursor, leftSkippedPos);
|
|
|
|
|
leftSelections
|
|
|
|
|
+= colorPositions(chunkLineFormat, leftCursor, leftChunkPos);
|
|
|
|
|
|
|
|
|
|
QList<QTextEdit::ExtraSelection> rightSelections
|
|
|
|
|
= colorPositions(rightLineFormat, rightCursor, rightChangedPos);
|
|
|
|
|
rightSelections
|
|
|
|
|
+= colorPositions(spanLineFormat, rightCursor, rightSkippedPos);
|
|
|
|
|
rightSelections
|
|
|
|
|
+= colorPositions(chunkLineFormat, rightCursor, rightChunkPos);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < fileData.chunks.count(); i++) {
|
|
|
|
|
ChunkData chunkData = fileData.chunks.at(i);
|
|
|
|
|
leftSelections
|
|
|
|
|
+= colorPositions(leftCharFormat, leftCursor, chunkData.changedLeftPositions);
|
|
|
|
|
rightSelections
|
|
|
|
|
+= colorPositions(rightCharFormat, rightCursor, chunkData.changedRightPositions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_leftEditor->setExtraSelections(BaseTextEditorWidget::OtherSelection,
|
|
|
|
|
m_leftEditor->extraSelections(BaseTextEditorWidget::OtherSelection)
|
|
|
|
|
+ leftSelections);
|
|
|
|
|
m_rightEditor->setExtraSelections(BaseTextEditorWidget::OtherSelection,
|
|
|
|
|
m_rightEditor->extraSelections(BaseTextEditorWidget::OtherSelection)
|
|
|
|
|
+ rightSelections);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidget::leftSliderChanged()
|
|
|
|
|
{
|
|
|
|
|
m_rightEditor->verticalScrollBar()->setValue(m_leftEditor->verticalScrollBar()->value());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidget::rightSliderChanged()
|
|
|
|
|
{
|
|
|
|
|
m_leftEditor->verticalScrollBar()->setValue(m_rightEditor->verticalScrollBar()->value());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-02-21 17:03:00 +01:00
|
|
|
} // namespace DiffEditor
|
2013-02-15 12:49:50 +01:00
|
|
|
|
|
|
|
|
#include "diffeditorwidget.moc"
|