Add file descriptions, enable file folding

Change-Id: Ie90ac9fb74e9c4b8cfba16d065d1b8751125e83c
Reviewed-by: David Schulz <david.schulz@digia.com>
This commit is contained in:
jkobus
2013-04-25 17:37:20 +02:00
committed by Jarek Kobus
parent 325c0beda5
commit e844bee1da
5 changed files with 468 additions and 209 deletions

View File

@@ -328,7 +328,15 @@ void DiffEditorPlugin::diff()
const QString text1 = getFileContents(fileName1, editorWidget->codec()); const QString text1 = getFileContents(fileName1, editorWidget->codec());
const QString text2 = getFileContents(fileName2, editorWidget->codec()); const QString text2 = getFileContents(fileName2, editorWidget->codec());
editorWidget->setDiff(text1, text2); DiffEditorWidget::DiffFilesContents dfc;
dfc.leftFileName = fileName1;
dfc.leftText = text1;
dfc.rightFileName = fileName2;
dfc.rightText = text2;
QList<DiffEditorWidget::DiffFilesContents> list;
list.append(dfc);
editorWidget->setDiff(list);
} }
} }

View File

@@ -46,6 +46,10 @@
#include <texteditor/basetextdocument.h> #include <texteditor/basetextdocument.h>
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
static const int BASE_LEVEL = 0;
static const int FILE_LEVEL = 1;
static const int CHUNK_LEVEL = 2;
using namespace TextEditor; using namespace TextEditor;
namespace DiffEditor { namespace DiffEditor {
@@ -83,11 +87,12 @@ public:
QMap<int, int> skippedLines() const { return m_skippedLines; } QMap<int, int> skippedLines() const { return m_skippedLines; }
void setLineNumber(int blockNumber, const QString &lineNumber); void setLineNumber(int blockNumber, const QString &lineNumber);
void setSkippedLines(int blockNumber, int skippedLines) { m_skippedLines[blockNumber] = skippedLines; } void setFileName(int blockNumber, const QString &fileName) { m_fileNames[blockNumber] = fileName; setSeparator(blockNumber, true); }
void setSkippedLines(int blockNumber, int skippedLines) { m_skippedLines[blockNumber] = skippedLines; setSeparator(blockNumber, true); }
void setSeparator(int blockNumber, bool separator) { m_separators[blockNumber] = separator; } void setSeparator(int blockNumber, bool separator) { m_separators[blockNumber] = separator; }
void clearLineNumbers(); bool isFileLine(int blockNumber) const { return m_fileNames.contains(blockNumber); }
void clearSkippedLines() { m_skippedLines.clear(); } bool isChunkLine(int blockNumber) const { return m_skippedLines.contains(blockNumber); }
void clearSeparators() { m_separators.clear(); } void clearAllData();
QTextBlock firstVisibleBlock() const { return SnippetEditorWidget::firstVisibleBlock(); } QTextBlock firstVisibleBlock() const { return SnippetEditorWidget::firstVisibleBlock(); }
protected: protected:
@@ -97,21 +102,31 @@ protected:
virtual int lineNumberDigits() const; virtual int lineNumberDigits() const;
virtual bool selectionVisible(int blockNumber) const; virtual bool selectionVisible(int blockNumber) const;
virtual bool replacementVisible(int blockNumber) const; virtual bool replacementVisible(int blockNumber) const;
QString plainTextFromSelection(const QTextCursor &cursor) const; virtual QString plainTextFromSelection(const QTextCursor &cursor) const;
virtual void drawCollapsedBlockPopup(QPainter &painter,
const QTextBlock &block,
QPointF offset,
const QRect &clip);
virtual void paintEvent(QPaintEvent *e); virtual void paintEvent(QPaintEvent *e);
virtual void scrollContentsBy(int dx, int dy); virtual void scrollContentsBy(int dx, int dy);
private: private:
void paintCollapsedBlockPopup(QPainter &painter, const QRect &clipRect);
void paintSeparator(QPainter &painter, const QString &text, const QTextBlock &block, int top);
QMap<int, QString> m_lineNumbers; QMap<int, QString> m_lineNumbers;
int m_lineNumberDigits; int m_lineNumberDigits;
// block number, fileName
QMap<int, QString> m_fileNames;
// block number, skipped lines // block number, skipped lines
QMap<int, int> m_skippedLines; QMap<int, int> m_skippedLines;
// block number, separator. Separator used as lines alignment and inside skipped lines // block number, separator. Separator used as lines alignment and inside skipped lines
QMap<int, bool> m_separators; QMap<int, bool> m_separators;
bool m_inPaintEvent;
}; };
DiffViewEditorWidget::DiffViewEditorWidget(QWidget *parent) DiffViewEditorWidget::DiffViewEditorWidget(QWidget *parent)
: SnippetEditorWidget(parent), m_lineNumberDigits(1) : SnippetEditorWidget(parent), m_lineNumberDigits(1), m_inPaintEvent(false)
{ {
setLineNumbersVisible(true); setLineNumbersVisible(true);
setCodeFoldingSupported(true); setCodeFoldingSupported(true);
@@ -135,7 +150,8 @@ bool DiffViewEditorWidget::selectionVisible(int blockNumber) const
bool DiffViewEditorWidget::replacementVisible(int blockNumber) const bool DiffViewEditorWidget::replacementVisible(int blockNumber) const
{ {
return m_skippedLines.value(blockNumber); return isChunkLine(blockNumber) || (isFileLine(blockNumber)
&& TextEditor::BaseTextDocumentLayout::isFolded(document()->findBlockByNumber(blockNumber)));
} }
QString DiffViewEditorWidget::plainTextFromSelection(const QTextCursor &cursor) const QString DiffViewEditorWidget::plainTextFromSelection(const QTextCursor &cursor) const
@@ -179,10 +195,13 @@ void DiffViewEditorWidget::setLineNumber(int blockNumber, const QString &lineNum
m_lineNumberDigits = qMax(m_lineNumberDigits, lineNumber.count()); m_lineNumberDigits = qMax(m_lineNumberDigits, lineNumber.count());
} }
void DiffViewEditorWidget::clearLineNumbers() void DiffViewEditorWidget::clearAllData()
{ {
m_lineNumbers.clear();
m_lineNumberDigits = 1; m_lineNumberDigits = 1;
m_lineNumbers.clear();
m_fileNames.clear();
m_skippedLines.clear();
m_separators.clear();
} }
void DiffViewEditorWidget::scrollContentsBy(int dx, int dy) void DiffViewEditorWidget::scrollContentsBy(int dx, int dy)
@@ -192,17 +211,43 @@ void DiffViewEditorWidget::scrollContentsBy(int dx, int dy)
viewport()->update(); viewport()->update();
} }
void DiffViewEditorWidget::paintSeparator(QPainter &painter, const QString &text, const QTextBlock &block, int top)
{
QPointF offset = contentOffset();
painter.save();
painter.setPen(palette().foreground().color());
const QString replacementText = QLatin1String(" {")
+ foldReplacementText(block)
+ QLatin1String("}; ");
const int replacementTextWidth = fontMetrics().width(replacementText) + 24;
int x = replacementTextWidth + offset.x();
if (x < document()->documentMargin() || !TextEditor::BaseTextDocumentLayout::isFolded(block))
x = document()->documentMargin();
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();
}
void DiffViewEditorWidget::paintEvent(QPaintEvent *e) void DiffViewEditorWidget::paintEvent(QPaintEvent *e)
{ {
m_inPaintEvent = true;
SnippetEditorWidget::paintEvent(e); SnippetEditorWidget::paintEvent(e);
m_inPaintEvent = false;
QPainter painter(viewport()); QPainter painter(viewport());
QPointF offset = contentOffset(); QPointF offset = contentOffset();
QTextBlock firstBlock = firstVisibleBlock(); QTextBlock firstBlock = firstVisibleBlock();
QTextBlock currentBlock = firstBlock; QTextBlock currentBlock = firstBlock;
QMap<int, int> skipped = skippedLines();
while (currentBlock.isValid()) { while (currentBlock.isValid()) {
if (currentBlock.isVisible()) { if (currentBlock.isVisible()) {
qreal top = blockBoundingGeometry(currentBlock).translated(offset).top(); qreal top = blockBoundingGeometry(currentBlock).translated(offset).top();
@@ -212,29 +257,126 @@ void DiffViewEditorWidget::paintEvent(QPaintEvent *e)
break; break;
if (bottom >= e->rect().top()) { if (bottom >= e->rect().top()) {
QTextLayout *layout = currentBlock.layout(); const int blockNumber = currentBlock.blockNumber();
int skippedBefore = skipped.value(currentBlock.blockNumber()); const int skippedBefore = m_skippedLines.value(blockNumber);
if (skippedBefore) { if (skippedBefore) {
painter.save(); const QString skippedRowsText = tr("Skipped %n lines...", 0, skippedBefore);
painter.setPen(palette().foreground().color()); paintSeparator(painter, skippedRowsText, currentBlock, top);
QTextLine textLine = layout->lineAt(0); }
QRectF lineRect = textLine.naturalTextRect().translated(0, top);
QString skippedRowsText = tr("Skipped %n lines...", 0, skippedBefore); const QString fileName = m_fileNames.value(blockNumber);
QFontMetrics fm(font()); if (!fileName.isEmpty()) {
const int textWidth = fm.width(skippedRowsText); paintSeparator(painter, fileName, currentBlock, top);
painter.drawText(QPointF(lineRect.right()
+ (viewport()->width() - textWidth) / 2.0,
lineRect.top() + textLine.ascent()),
skippedRowsText);
painter.restore();
} }
} }
} }
currentBlock = currentBlock.next(); currentBlock = currentBlock.next();
} }
paintCollapsedBlockPopup(painter, e->rect());
} }
void DiffViewEditorWidget::paintCollapsedBlockPopup(QPainter &painter, const QRect &clipRect)
{
QPointF offset(contentOffset());
QRect viewportRect = viewport()->rect();
QTextBlock block = firstVisibleBlock();
QTextBlock visibleCollapsedBlock;
QPointF visibleCollapsedBlockOffset;
while (block.isValid()) {
QRectF r = blockBoundingRect(block).translated(offset);
offset.ry() += r.height();
if (offset.y() > viewportRect.height())
break;
block = block.next();
if (!block.isVisible()) {
if (block.blockNumber() == visibleFoldedBlockNumber()) {
visibleCollapsedBlock = block;
visibleCollapsedBlockOffset = offset + QPointF(0,1);
break;
}
// invisible blocks do have zero line count
block = document()->findBlockByLineNumber(block.firstLineNumber());
}
}
if (visibleCollapsedBlock.isValid()) {
drawCollapsedBlockPopup(painter,
visibleCollapsedBlock,
visibleCollapsedBlockOffset,
clipRect);
}
}
void DiffViewEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
const QTextBlock &block,
QPointF offset,
const QRect &clip)
{
// We ignore the call coming from the BaseTextEditorWidget::paintEvent()
// since we will draw it later, after custom drawings of this paintEvent.
// We need to draw it after our custom drawings, otherwise custom
// drawings will appear in front of block popup.
if (m_inPaintEvent)
return;
int margin = block.document()->documentMargin();
qreal maxWidth = 0;
qreal blockHeight = 0;
QTextBlock b = block;
while (!b.isVisible()) {
if (!m_separators.contains(b.blockNumber())) {
b.setVisible(true); // make sure block bounding rect works
QRectF r = blockBoundingRect(b).translated(offset);
QTextLayout *layout = b.layout();
for (int i = layout->lineCount()-1; i >= 0; --i)
maxWidth = qMax(maxWidth, layout->lineAt(i).naturalTextWidth() + 2*margin);
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();
painter.setBrush(brush);
painter.drawRoundedRect(QRectF(offset.x(),
offset.y(),
maxWidth, blockHeight).adjusted(0, 0, 0, 0), 3, 3);
painter.restore();
QTextBlock end = b;
b = block;
while (b != end) {
if (!m_separators.contains(b.blockNumber())) {
b.setVisible(true); // make sure block bounding rect works
QRectF r = blockBoundingRect(b).translated(offset);
QTextLayout *layout = b.layout();
QVector<QTextLayout::FormatRange> selections;
layout->draw(&painter, offset, selections, clip);
b.setVisible(false); // restore previous state
b.setLineCount(0); // restore 0 line count for invisible block
offset.ry() += r.height();
}
b = b.next();
}
}
////////////////// //////////////////
DiffEditorWidget::DiffEditorWidget(QWidget *parent) DiffEditorWidget::DiffEditorWidget(QWidget *parent)
@@ -287,25 +429,36 @@ DiffEditorWidget::~DiffEditorWidget()
} }
void DiffEditorWidget::setDiff(const QString &leftText, const QString &rightText) void DiffEditorWidget::setDiff(const QList<DiffFilesContents> &diffFileList)
{ {
// QTime time;
// time.start();
Differ differ; Differ differ;
QList<Diff> list = differ.cleanupSemantics(differ.diff(leftText, rightText)); QList<DiffList> diffList;
// int ela = time.elapsed(); for (int i = 0; i < diffFileList.count(); i++) {
// qDebug() << "Time spend in diff:" << ela; DiffFilesContents dfc = diffFileList.at(i);
setDiff(list); DiffList dl;
dl.leftFileName = dfc.leftFileName;
dl.rightFileName = dfc.rightFileName;
dl.diffList = differ.cleanupSemantics(differ.diff(dfc.leftText, dfc.rightText));
diffList.append(dl);
}
setDiff(diffList);
} }
void DiffEditorWidget::setDiff(const QList<Diff> &diffList) void DiffEditorWidget::setDiff(const QList<DiffList> &diffList)
{ {
m_diffList = diffList; m_diffList = diffList;
m_originalChunkData.clear();
m_contextFileData.clear();
QList<Diff> transformedDiffList = m_diffList; for (int i = 0; i < m_diffList.count(); i++) {
const DiffList &dl = m_diffList.at(i);
m_originalChunkData = calculateOriginalData(transformedDiffList); ChunkData chunkData = calculateOriginalData(dl.diffList);
m_contextFileData = calculateContextData(m_originalChunkData); m_originalChunkData.append(chunkData);
FileData fileData = calculateContextData(chunkData);
fileData.leftFileName = dl.leftFileName;
fileData.rightFileName = dl.rightFileName;
m_contextFileData.append(fileData);
}
showDiff(); showDiff();
} }
@@ -315,7 +468,14 @@ void DiffEditorWidget::setContextLinesNumber(int lines)
return; return;
m_contextLinesNumber = lines; m_contextLinesNumber = lines;
m_contextFileData = calculateContextData(m_originalChunkData); for (int i = 0; i < m_diffList.count(); i++) {
const FileData oldFileData = m_contextFileData.at(i);
FileData newFileData = calculateContextData(m_originalChunkData.at(i));
newFileData.leftFileName = oldFileData.leftFileName;
newFileData.rightFileName = oldFileData.rightFileName;
m_contextFileData[i] = newFileData;
}
showDiff(); showDiff();
} }
@@ -526,11 +686,9 @@ ChunkData DiffEditorWidget::calculateOriginalData(const QList<Diff> &diffList) c
if (currentLeftLineOffset < currentRightLineOffset) { if (currentLeftLineOffset < currentRightLineOffset) {
const int spans = currentRightLineOffset - currentLeftLineOffset; const int spans = currentRightLineOffset - currentLeftLineOffset;
leftLineSpans[currentLeftLine] = spans; leftLineSpans[currentLeftLine] = spans;
// currentLeftPos += spans;
} else if (currentRightLineOffset < currentLeftLineOffset) { } else if (currentRightLineOffset < currentLeftLineOffset) {
const int spans = currentLeftLineOffset - currentRightLineOffset; const int spans = currentLeftLineOffset - currentRightLineOffset;
rightLineSpans[currentRightLine] = spans; rightLineSpans[currentRightLine] = spans;
// currentRightPos += spans;
} }
currentLeftLineOffset = 0; currentLeftLineOffset = 0;
currentRightLineOffset = 0; currentRightLineOffset = 0;
@@ -560,11 +718,9 @@ ChunkData DiffEditorWidget::calculateOriginalData(const QList<Diff> &diffList) c
if (currentLeftLineOffset < currentRightLineOffset) { if (currentLeftLineOffset < currentRightLineOffset) {
const int spans = currentRightLineOffset - currentLeftLineOffset; const int spans = currentRightLineOffset - currentLeftLineOffset;
leftLineSpans[currentLeftLine] = spans; leftLineSpans[currentLeftLine] = spans;
// currentLeftPos += spans;
} else if (currentRightLineOffset < currentLeftLineOffset) { } else if (currentRightLineOffset < currentLeftLineOffset) {
const int spans = currentLeftLineOffset - currentRightLineOffset; const int spans = currentLeftLineOffset - currentRightLineOffset;
rightLineSpans[currentRightLine] = spans; rightLineSpans[currentRightLine] = spans;
// currentRightPos += spans;
} }
} }
if (lastLeftLineEqual && lastRightLineEqual) { if (lastLeftLineEqual && lastRightLineEqual) {
@@ -675,7 +831,7 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c
const int startPos = leftChangedIt.key(); const int startPos = leftChangedIt.key();
const int endPos = leftChangedIt.value(); const int endPos = leftChangedIt.value();
chunkData.changedLeftPositions.insert(startPos, endPos); chunkData.changedLeftPositions.insert(startPos - leftOffset, endPos - leftOffset);
leftChangedIt++; leftChangedIt++;
} }
while (rightChangedIt != originalData.changedRightPositions.constEnd()) { while (rightChangedIt != originalData.changedRightPositions.constEnd()) {
@@ -685,7 +841,7 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c
const int startPos = rightChangedIt.key(); const int startPos = rightChangedIt.key();
const int endPos = rightChangedIt.value(); const int endPos = rightChangedIt.value();
chunkData.changedRightPositions.insert(startPos, endPos); chunkData.changedRightPositions.insert(startPos - rightOffset, endPos - rightOffset);
rightChangedIt++; rightChangedIt++;
} }
fileData.chunks.append(chunkData); fileData.chunks.append(chunkData);
@@ -722,34 +878,37 @@ void DiffEditorWidget::showDiff()
m_rightEditor->setBlockSelection(false); m_rightEditor->setBlockSelection(false);
m_leftEditor->clear(); m_leftEditor->clear();
m_rightEditor->clear(); m_rightEditor->clear();
m_leftEditor->clearLineNumbers(); m_leftEditor->clearAllData();
m_rightEditor->clearLineNumbers(); m_rightEditor->clearAllData();
m_leftEditor->clearSkippedLines();
m_rightEditor->clearSkippedLines();
m_leftEditor->clearSeparators();
m_rightEditor->clearSeparators();
// int ela1 = time.elapsed(); // int ela1 = time.elapsed();
QString leftText, rightText; QString leftText, rightText;
int leftLineNumber = 0;
int rightLineNumber = 0;
int blockNumber = 0; int blockNumber = 0;
QChar separator = QLatin1Char('\n'); QChar separator = QLatin1Char('\n');
for (int i = 0; i < m_contextFileData.chunks.count(); i++) { for (int i = 0; i < m_contextFileData.count(); i++) {
ChunkData chunkData = m_contextFileData.chunks.at(i); const FileData &contextFileData = m_contextFileData.at(i);
int leftLineNumber = 0;
int rightLineNumber = 0;
m_leftEditor->setFileName(blockNumber, contextFileData.leftFileName);
m_rightEditor->setFileName(blockNumber, contextFileData.rightFileName);
leftText += separator;
rightText += separator;
blockNumber++;
for (int j = 0; j < contextFileData.chunks.count(); j++) {
ChunkData chunkData = contextFileData.chunks.at(j);
if (chunkData.contextChunk) { if (chunkData.contextChunk) {
const int skippedLines = chunkData.rows.count(); const int skippedLines = chunkData.rows.count();
m_leftEditor->setSkippedLines(blockNumber, skippedLines); m_leftEditor->setSkippedLines(blockNumber, skippedLines);
m_rightEditor->setSkippedLines(blockNumber, skippedLines); m_rightEditor->setSkippedLines(blockNumber, skippedLines);
m_leftEditor->setSeparator(blockNumber, true);
m_rightEditor->setSeparator(blockNumber, true);
leftText += separator; leftText += separator;
rightText += separator; rightText += separator;
blockNumber++; blockNumber++;
} }
for (int j = 0; j < chunkData.rows.count(); j++) { for (int k = 0; k < chunkData.rows.count(); k++) {
RowData rowData = chunkData.rows.at(j); RowData rowData = chunkData.rows.at(k);
TextLineData leftLineData = rowData.leftLine; TextLineData leftLineData = rowData.leftLine;
TextLineData rightLineData = rowData.rightLine; TextLineData rightLineData = rowData.rightLine;
if (leftLineData.textLineType == TextLineData::TextLine) { if (leftLineData.textLineType == TextLineData::TextLine) {
@@ -772,6 +931,7 @@ void DiffEditorWidget::showDiff()
blockNumber++; blockNumber++;
} }
} }
}
// int ela2 = time.elapsed(); // int ela2 = time.elapsed();
m_leftEditor->setPlainText(leftText); m_leftEditor->setPlainText(leftText);
@@ -782,27 +942,35 @@ void DiffEditorWidget::showDiff()
colorDiff(m_contextFileData); colorDiff(m_contextFileData);
blockNumber = 0; QTextBlock leftBlock = m_leftEditor->document()->firstBlock();
for (int i = 0; i < m_contextFileData.chunks.count(); i++) { QTextBlock rightBlock = m_rightEditor->document()->firstBlock();
ChunkData chunkData = m_contextFileData.chunks.at(i); for (int i = 0; i < m_contextFileData.count(); i++) {
if (chunkData.contextChunk) { const FileData &contextFileData = m_contextFileData.at(i);
blockNumber++;
QTextBlock leftBlock = m_leftEditor->document()->findBlockByNumber(blockNumber);
for (int j = 0; j < chunkData.rows.count(); j++) {
TextEditor::BaseTextDocumentLayout::setFoldingIndent(leftBlock, 1);
leftBlock = leftBlock.next(); leftBlock = leftBlock.next();
rightBlock = rightBlock.next();
for (int j = 0; j < contextFileData.chunks.count(); j++) {
ChunkData chunkData = contextFileData.chunks.at(j);
if (chunkData.contextChunk) {
TextEditor::BaseTextDocumentLayout::setFoldingIndent(leftBlock, FILE_LEVEL);
TextEditor::BaseTextDocumentLayout::setFoldingIndent(rightBlock, FILE_LEVEL);
leftBlock = leftBlock.next();
rightBlock = rightBlock.next();
} }
QTextBlock rightBlock = m_rightEditor->document()->findBlockByNumber(blockNumber); const int indent = chunkData.contextChunk ? CHUNK_LEVEL : FILE_LEVEL;
for (int j = 0; j < chunkData.rows.count(); j++) { for (int k = 0; k < chunkData.rows.count(); k++) {
TextEditor::BaseTextDocumentLayout::setFoldingIndent(rightBlock, 1); TextEditor::BaseTextDocumentLayout::setFoldingIndent(leftBlock, indent);
TextEditor::BaseTextDocumentLayout::setFoldingIndent(rightBlock, indent);
leftBlock = leftBlock.next();
rightBlock = rightBlock.next(); rightBlock = rightBlock.next();
} }
} }
blockNumber += chunkData.rows.count();
} }
blockNumber = 0; blockNumber = 0;
for (int i = 0; i < m_contextFileData.chunks.count(); i++) { for (int i = 0; i < m_contextFileData.count(); i++) {
ChunkData chunkData = m_contextFileData.chunks.at(i); const FileData &contextFileData = m_contextFileData.at(i);
blockNumber++;
for (int j = 0; j < contextFileData.chunks.count(); j++) {
ChunkData chunkData = contextFileData.chunks.at(j);
if (chunkData.contextChunk) { if (chunkData.contextChunk) {
QTextBlock leftBlock = m_leftEditor->document()->findBlockByNumber(blockNumber); QTextBlock leftBlock = m_leftEditor->document()->findBlockByNumber(blockNumber);
TextEditor::BaseTextDocumentLayout::doFoldOrUnfold(leftBlock, false); TextEditor::BaseTextDocumentLayout::doFoldOrUnfold(leftBlock, false);
@@ -812,6 +980,7 @@ void DiffEditorWidget::showDiff()
} }
blockNumber += chunkData.rows.count(); blockNumber += chunkData.rows.count();
} }
}
m_foldingBlocker = true; m_foldingBlocker = true;
BaseTextDocumentLayout *leftLayout = qobject_cast<BaseTextDocumentLayout *>(m_leftEditor->document()->documentLayout()); BaseTextDocumentLayout *leftLayout = qobject_cast<BaseTextDocumentLayout *>(m_leftEditor->document()->documentLayout());
if (leftLayout) { if (leftLayout) {
@@ -833,6 +1002,8 @@ void DiffEditorWidget::showDiff()
m_rightEditor->horizontalScrollBar()->setValue(rightHorizontalValue); m_rightEditor->horizontalScrollBar()->setValue(rightHorizontalValue);
// int ela6 = time.elapsed(); // int ela6 = time.elapsed();
// qDebug() << ela1 << ela2 << ela3 << ela4 << ela5 << ela6; // qDebug() << ela1 << ela2 << ela3 << ela4 << ela5 << ela6;
m_leftEditor->updateFoldingHighlight(QPoint(-1, -1));
m_rightEditor->updateFoldingHighlight(QPoint(-1, -1));
} }
QList<QTextEdit::ExtraSelection> DiffEditorWidget::colorPositions( QList<QTextEdit::ExtraSelection> DiffEditorWidget::colorPositions(
@@ -858,7 +1029,7 @@ QList<QTextEdit::ExtraSelection> DiffEditorWidget::colorPositions(
return lineSelections; return lineSelections;
} }
void DiffEditorWidget::colorDiff(const FileData &fileData) void DiffEditorWidget::colorDiff(const QList<FileData> &fileDataList)
{ {
QTextCharFormat leftLineFormat; QTextCharFormat leftLineFormat;
leftLineFormat.setBackground(QColor(255, 223, 223)); leftLineFormat.setBackground(QColor(255, 223, 223));
@@ -886,6 +1057,11 @@ void DiffEditorWidget::colorDiff(const FileData &fileData)
chunkLineFormat.setBackground(QColor(175, 215, 231)); chunkLineFormat.setBackground(QColor(175, 215, 231));
chunkLineFormat.setProperty(QTextFormat::FullWidthSelection, true); chunkLineFormat.setProperty(QTextFormat::FullWidthSelection, true);
QTextCharFormat fileLineFormat;
fileLineFormat.setBackground(QColor(255, 255, 0));
// fileLineFormat.setBackground(QColor(130, 60, 0));
fileLineFormat.setProperty(QTextFormat::FullWidthSelection, true);
int leftPos = 0; int leftPos = 0;
int rightPos = 0; int rightPos = 0;
// startPos, endPos // startPos, endPos
@@ -897,21 +1073,30 @@ void DiffEditorWidget::colorDiff(const FileData &fileData)
QMap<int, int> rightSkippedPos; QMap<int, int> rightSkippedPos;
QMap<int, int> leftChunkPos; QMap<int, int> leftChunkPos;
QMap<int, int> rightChunkPos; QMap<int, int> rightChunkPos;
QMap<int, int> leftFilePos;
QMap<int, int> rightFilePos;
int leftLastDiffBlockStartPos = 0; int leftLastDiffBlockStartPos = 0;
int rightLastDiffBlockStartPos = 0; int rightLastDiffBlockStartPos = 0;
int leftLastSkippedBlockStartPos = 0; int leftLastSkippedBlockStartPos = 0;
int rightLastSkippedBlockStartPos = 0; int rightLastSkippedBlockStartPos = 0;
int chunkOffset = 0;
for (int i = 0; i < fileData.chunks.count(); i++) { for (int i = 0; i < fileDataList.count(); i++) {
ChunkData chunkData = fileData.chunks.at(i); leftFilePos[leftPos] = leftPos + 1;
rightFilePos[rightPos] = rightPos + 1;
leftPos++; // for file line
rightPos++; // for file line
const FileData &fileData = fileDataList.at(i);
for (int j = 0; j < fileData.chunks.count(); j++) {
ChunkData chunkData = fileData.chunks.at(j);
if (chunkData.contextChunk) { if (chunkData.contextChunk) {
leftChunkPos[leftPos] = leftPos + 1; leftChunkPos[leftPos] = leftPos + 1;
rightChunkPos[rightPos] = rightPos + 1; rightChunkPos[rightPos] = rightPos + 1;
leftPos++; // for chunk line leftPos++; // for chunk line
rightPos++; // for chunk line rightPos++; // for chunk line
chunkOffset++;
} }
const int leftFileOffset = leftPos;
const int rightFileOffset = rightPos;
leftLastDiffBlockStartPos = leftPos; leftLastDiffBlockStartPos = leftPos;
rightLastDiffBlockStartPos = rightPos; rightLastDiffBlockStartPos = rightPos;
leftLastSkippedBlockStartPos = leftPos; leftLastSkippedBlockStartPos = leftPos;
@@ -921,18 +1106,18 @@ void DiffEditorWidget::colorDiff(const FileData &fileData)
while (itLeft.hasNext()) { while (itLeft.hasNext()) {
itLeft.next(); itLeft.next();
leftCharPos[itLeft.key() + chunkOffset] = itLeft.value() + chunkOffset; leftCharPos[itLeft.key() + leftFileOffset] = itLeft.value() + leftFileOffset;
} }
QMapIterator<int, int> itRight(chunkData.changedRightPositions); QMapIterator<int, int> itRight(chunkData.changedRightPositions);
while (itRight.hasNext()) { while (itRight.hasNext()) {
itRight.next(); itRight.next();
rightCharPos[itRight.key() + chunkOffset] = itRight.value() + chunkOffset; rightCharPos[itRight.key() + rightFileOffset] = itRight.value() + rightFileOffset;
} }
for (int j = 0; j < chunkData.rows.count(); j++) { for (int k = 0; k < chunkData.rows.count(); k++) {
RowData rowData = chunkData.rows.at(j); RowData rowData = chunkData.rows.at(k);
leftPos += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it 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 rightPos += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
@@ -960,6 +1145,7 @@ void DiffEditorWidget::colorDiff(const FileData &fileData)
} }
} }
} }
}
QTextCursor leftCursor = m_leftEditor->textCursor(); QTextCursor leftCursor = m_leftEditor->textCursor();
QTextCursor rightCursor = m_rightEditor->textCursor(); QTextCursor rightCursor = m_rightEditor->textCursor();
@@ -970,6 +1156,8 @@ void DiffEditorWidget::colorDiff(const FileData &fileData)
+= colorPositions(spanLineFormat, leftCursor, leftSkippedPos); += colorPositions(spanLineFormat, leftCursor, leftSkippedPos);
leftSelections leftSelections
+= colorPositions(chunkLineFormat, leftCursor, leftChunkPos); += colorPositions(chunkLineFormat, leftCursor, leftChunkPos);
leftSelections
+= colorPositions(fileLineFormat, leftCursor, leftFilePos);
leftSelections leftSelections
+= colorPositions(leftCharFormat, leftCursor, leftCharPos); += colorPositions(leftCharFormat, leftCursor, leftCharPos);
@@ -979,6 +1167,8 @@ void DiffEditorWidget::colorDiff(const FileData &fileData)
+= colorPositions(spanLineFormat, rightCursor, rightSkippedPos); += colorPositions(spanLineFormat, rightCursor, rightSkippedPos);
rightSelections rightSelections
+= colorPositions(chunkLineFormat, rightCursor, rightChunkPos); += colorPositions(chunkLineFormat, rightCursor, rightChunkPos);
rightSelections
+= colorPositions(fileLineFormat, rightCursor, rightFilePos);
rightSelections rightSelections
+= colorPositions(rightCharFormat, rightCursor, rightCharPos); += colorPositions(rightCharFormat, rightCursor, rightCharPos);
@@ -1010,6 +1200,39 @@ void DiffEditorWidget::rightDocumentSizeChanged()
synchronizeFoldings(m_rightEditor, m_leftEditor); synchronizeFoldings(m_rightEditor, m_leftEditor);
} }
/* Special version of that method (original: TextEditor::BaseTextDocumentLayout::doFoldOrUnfold())
The hack lies in fact, that when unfolding all direct sub-blocks are made visible,
while some of them need to stay invisible (i.e. unfolded chunk lines)
*/
static void doFoldOrUnfold(DiffViewEditorWidget *editor, const QTextBlock &block, bool unfold)
{
if (!TextEditor::BaseTextDocumentLayout::canFold(block))
return;
QTextBlock b = block.next();
int indent = TextEditor::BaseTextDocumentLayout::foldingIndent(block);
while (b.isValid() && TextEditor::BaseTextDocumentLayout::foldingIndent(b) > indent && (unfold || b.next().isValid())) {
if (unfold && editor->isChunkLine(b.blockNumber()) && !TextEditor::BaseTextDocumentLayout::isFolded(b)) {
b.setVisible(false);
b.setLineCount(0);
} else {
b.setVisible(unfold);
b.setLineCount(unfold ? qMax(1, b.layout()->lineCount()) : 0);
}
if (unfold) { // do not unfold folded sub-blocks
if (TextEditor::BaseTextDocumentLayout::isFolded(b) && b.next().isValid()) {
int jndent = TextEditor::BaseTextDocumentLayout::foldingIndent(b);
b = b.next();
while (b.isValid() && TextEditor::BaseTextDocumentLayout::foldingIndent(b) > jndent)
b = b.next();
continue;
}
}
b = b.next();
}
TextEditor::BaseTextDocumentLayout::setFolded(block, !unfold);
}
void DiffEditorWidget::synchronizeFoldings(DiffViewEditorWidget *source, DiffViewEditorWidget *destination) void DiffEditorWidget::synchronizeFoldings(DiffViewEditorWidget *source, DiffViewEditorWidget *destination)
{ {
if (m_foldingBlocker) if (m_foldingBlocker)
@@ -1023,14 +1246,19 @@ void DiffEditorWidget::synchronizeFoldings(DiffViewEditorWidget *source, DiffVie
const bool isSourceFolded = TextEditor::BaseTextDocumentLayout::isFolded(sourceBlock); const bool isSourceFolded = TextEditor::BaseTextDocumentLayout::isFolded(sourceBlock);
const bool isDestinationFolded = TextEditor::BaseTextDocumentLayout::isFolded(destinationBlock); const bool isDestinationFolded = TextEditor::BaseTextDocumentLayout::isFolded(destinationBlock);
if (isSourceFolded != isDestinationFolded) { if (isSourceFolded != isDestinationFolded) {
if (isSourceFolded) { // we fold the destination if (source->isFileLine(sourceBlock.blockNumber())) {
doFoldOrUnfold(source, sourceBlock, !isSourceFolded);
doFoldOrUnfold(destination, destinationBlock, !isSourceFolded);
} else {
if (isSourceFolded) { // we fold the destination (shrinking)
QTextBlock previousSource = sourceBlock.previous(); // skippedLines QTextBlock previousSource = sourceBlock.previous(); // skippedLines
QTextBlock previousDestination = destinationBlock.previous(); // skippedLines QTextBlock previousDestination = destinationBlock.previous(); // skippedLines
if (source->isChunkLine(previousSource.blockNumber())) {
QTextBlock firstVisibleDestinationBlock = destination->firstVisibleBlock(); QTextBlock firstVisibleDestinationBlock = destination->firstVisibleBlock();
QTextBlock firstDestinationBlock = destination->document()->firstBlock(); QTextBlock firstDestinationBlock = destination->document()->firstBlock();
TextEditor::BaseTextDocumentLayout::doFoldOrUnfold(destinationBlock, !isSourceFolded); TextEditor::BaseTextDocumentLayout::doFoldOrUnfold(destinationBlock, !isSourceFolded);
TextEditor::BaseTextDocumentLayout::setFoldingIndent(sourceBlock, 1); TextEditor::BaseTextDocumentLayout::setFoldingIndent(sourceBlock, CHUNK_LEVEL);
TextEditor::BaseTextDocumentLayout::setFoldingIndent(destinationBlock, 1); TextEditor::BaseTextDocumentLayout::setFoldingIndent(destinationBlock, CHUNK_LEVEL);
previousSource.setVisible(true); previousSource.setVisible(true);
previousSource.setLineCount(1); previousSource.setLineCount(1);
previousDestination.setVisible(true); previousDestination.setVisible(true);
@@ -1058,12 +1286,14 @@ void DiffEditorWidget::synchronizeFoldings(DiffViewEditorWidget *source, DiffVie
destination->verticalScrollBar()->setValue(0); destination->verticalScrollBar()->setValue(0);
} }
} }
} else { // we unfold the destination }
} else { // we unfold the destination (expanding)
if (source->isChunkLine(sourceBlock.blockNumber())) {
QTextBlock nextSource = sourceBlock.next(); QTextBlock nextSource = sourceBlock.next();
QTextBlock nextDestination = destinationBlock.next(); QTextBlock nextDestination = destinationBlock.next();
TextEditor::BaseTextDocumentLayout::doFoldOrUnfold(destinationBlock, !isSourceFolded); TextEditor::BaseTextDocumentLayout::doFoldOrUnfold(destinationBlock, !isSourceFolded);
TextEditor::BaseTextDocumentLayout::setFoldingIndent(nextSource, 0); TextEditor::BaseTextDocumentLayout::setFoldingIndent(nextSource, FILE_LEVEL);
TextEditor::BaseTextDocumentLayout::setFoldingIndent(nextDestination, 0); TextEditor::BaseTextDocumentLayout::setFoldingIndent(nextDestination, FILE_LEVEL);
sourceBlock.setVisible(false); sourceBlock.setVisible(false);
sourceBlock.setLineCount(0); sourceBlock.setLineCount(0);
destinationBlock.setVisible(false); destinationBlock.setVisible(false);
@@ -1071,6 +1301,8 @@ void DiffEditorWidget::synchronizeFoldings(DiffViewEditorWidget *source, DiffVie
TextEditor::BaseTextDocumentLayout::setFolded(nextSource, false); TextEditor::BaseTextDocumentLayout::setFolded(nextSource, false);
TextEditor::BaseTextDocumentLayout::setFolded(nextDestination, false); TextEditor::BaseTextDocumentLayout::setFolded(nextDestination, false);
} }
}
}
break; // only one should be synchronized break; // only one should be synchronized
} }
} }

View File

@@ -81,7 +81,6 @@ struct ChunkData {
ChunkData() : contextChunk(false) {} ChunkData() : contextChunk(false) {}
QList<RowData> rows; QList<RowData> rows;
bool contextChunk; bool contextChunk;
// <absolute position in the file, absolute position in the file>
QMap<int, int> changedLeftPositions; // counting from the beginning of the chunk QMap<int, int> changedLeftPositions; // counting from the beginning of the chunk
QMap<int, int> changedRightPositions; // counting from the beginning of the chunk QMap<int, int> changedRightPositions; // counting from the beginning of the chunk
}; };
@@ -90,6 +89,8 @@ struct FileData {
FileData() {} FileData() {}
FileData(const ChunkData &chunkData) { chunks.append(chunkData); } FileData(const ChunkData &chunkData) { chunks.append(chunkData); }
QList<ChunkData> chunks; QList<ChunkData> chunks;
QString leftFileName;
QString rightFileName;
}; };
struct DiffData { struct DiffData {
@@ -100,10 +101,18 @@ class DIFFEDITOR_EXPORT DiffEditorWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
struct DiffFilesContents {
QString leftFileName;
QString leftText;
QString rightFileName;
QString rightText;
};
DiffEditorWidget(QWidget *parent = 0); DiffEditorWidget(QWidget *parent = 0);
~DiffEditorWidget(); ~DiffEditorWidget();
void setDiff(const QString &leftText, const QString &rightText); void clear();
void setDiff(const QList<DiffFilesContents> &diffFileList);
QTextCodec *codec() const; QTextCodec *codec() const;
public slots: public slots:
@@ -121,14 +130,20 @@ private slots:
void rightDocumentSizeChanged(); void rightDocumentSizeChanged();
private: private:
void setDiff(const QList<Diff> &diffList); struct DiffList {
QString leftFileName;
QString rightFileName;
QList<Diff> diffList;
};
void setDiff(const QList<DiffList> &diffList);
bool isWhitespace(const QChar &c) const; bool isWhitespace(const QChar &c) const;
bool isWhitespace(const Diff &diff) const; bool isWhitespace(const Diff &diff) const;
bool isEqual(const QList<Diff> &diffList, int diffNumber) const; bool isEqual(const QList<Diff> &diffList, int diffNumber) const;
QList<QTextEdit::ExtraSelection> colorPositions(const QTextCharFormat &format, QList<QTextEdit::ExtraSelection> colorPositions(const QTextCharFormat &format,
QTextCursor &cursor, QTextCursor &cursor,
const QMap<int, int> &positions) const; const QMap<int, int> &positions) const;
void colorDiff(const FileData &fileData); void colorDiff(const QList<FileData> &fileDataList);
QList<TextLineData> assemblyRows(const QStringList &lines, QList<TextLineData> assemblyRows(const QStringList &lines,
const QMap<int, int> &lineSpans, const QMap<int, int> &lineSpans,
const QMap<int, int> &changedPositions, const QMap<int, int> &changedPositions,
@@ -142,13 +157,12 @@ private:
DiffViewEditorWidget *m_rightEditor; DiffViewEditorWidget *m_rightEditor;
QSplitter *m_splitter; QSplitter *m_splitter;
QList<Diff> m_diffList; QList<DiffList> m_diffList; // list of original outputs from differ
QList<ChunkData> m_originalChunkData; // one big chunk for every file, ignoreWhitespaces taken into account
QList<FileData> m_contextFileData; // ultimate data to be shown, contextLinesNumber taken into account
int m_contextLinesNumber; int m_contextLinesNumber;
bool m_ignoreWhitespaces; bool m_ignoreWhitespaces;
ChunkData m_originalChunkData;
FileData m_contextFileData;
bool m_foldingBlocker; bool m_foldingBlocker;
}; };

View File

@@ -3649,6 +3649,11 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e)
} }
} }
int BaseTextEditorWidget::visibleFoldedBlockNumber() const
{
return d->visibleFoldedBlockNumber;
}
void BaseTextEditorWidget::drawCollapsedBlockPopup(QPainter &painter, void BaseTextEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
const QTextBlock &block, const QTextBlock &block,
QPointF offset, QPointF offset,
@@ -4617,8 +4622,7 @@ void BaseTextEditorWidget::toggleBlockVisible(const QTextBlock &block)
BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(document()->documentLayout()); BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(document()->documentLayout());
QTC_ASSERT(documentLayout, return); QTC_ASSERT(documentLayout, return);
bool visible = block.next().isVisible(); BaseTextDocumentLayout::doFoldOrUnfold(block, BaseTextDocumentLayout::isFolded(block));
BaseTextDocumentLayout::doFoldOrUnfold(block, !visible);
documentLayout->requestUpdate(); documentLayout->requestUpdate();
documentLayout->emitDocumentSizeChanged(); documentLayout->emitDocumentSizeChanged();
} }

View File

@@ -539,6 +539,12 @@ protected:
Reimplement this function to change the default replacement text. Reimplement this function to change the default replacement text.
*/ */
virtual QString foldReplacementText(const QTextBlock &block) const; virtual QString foldReplacementText(const QTextBlock &block) const;
virtual void drawCollapsedBlockPopup(QPainter &painter,
const QTextBlock &block,
QPointF offset,
const QRect &clip);
int visibleFoldedBlockNumber() const;
protected slots: protected slots:
virtual void slotUpdateExtraArea(); virtual void slotUpdateExtraArea();
@@ -570,11 +576,6 @@ private:
bool active, bool active,
bool hovered) const; bool hovered) const;
void drawCollapsedBlockPopup(QPainter &painter,
const QTextBlock &block,
QPointF offset,
const QRect &clip);
void toggleBlockVisible(const QTextBlock &block); void toggleBlockVisible(const QTextBlock &block);
QRect foldBox(); QRect foldBox();