2014-02-13 16:43:28 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
2015-01-14 18:07:15 +01:00
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
|
** Contact: http://www.qt.io/licensing
|
2014-02-13 16:43:28 +01:00
|
|
|
**
|
|
|
|
|
** 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
|
2015-01-14 18:07:15 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms and
|
|
|
|
|
** conditions see http://www.qt.io/terms-conditions. For further information
|
2014-10-01 13:21:18 +02:00
|
|
|
** use the contact form at http://www.qt.io/contact-us.
|
2014-02-13 16:43:28 +01:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2014-10-01 13:21:18 +02:00
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2014-02-13 16:43:28 +01:00
|
|
|
**
|
2015-01-14 18:07:15 +01:00
|
|
|
** In addition, as a special exception, The Qt Company gives you certain additional
|
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
2014-02-13 16:43:28 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "unifieddiffeditorwidget.h"
|
2015-03-10 14:19:38 +01:00
|
|
|
#include "diffeditorcontroller.h"
|
|
|
|
|
#include "diffutils.h"
|
2014-02-13 16:43:28 +01:00
|
|
|
#include "diffeditorconstants.h"
|
2015-01-30 16:59:25 +01:00
|
|
|
#include "diffeditordocument.h"
|
2014-02-13 16:43:28 +01:00
|
|
|
|
|
|
|
|
#include <QPlainTextEdit>
|
2015-02-26 13:22:35 +01:00
|
|
|
#include <QMenu>
|
2014-02-13 16:43:28 +01:00
|
|
|
#include <QPlainTextDocumentLayout>
|
|
|
|
|
#include <QTextBlock>
|
2015-03-10 14:19:38 +01:00
|
|
|
#include <QTextCodec>
|
2014-02-13 16:43:28 +01:00
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
|
2015-02-26 13:22:35 +01:00
|
|
|
#include <texteditor/textdocument.h>
|
2014-09-26 09:14:03 +02:00
|
|
|
#include <texteditor/textdocumentlayout.h>
|
2014-02-13 16:43:28 +01:00
|
|
|
#include <texteditor/texteditorsettings.h>
|
|
|
|
|
#include <texteditor/fontsettings.h>
|
|
|
|
|
#include <texteditor/displaysettings.h>
|
|
|
|
|
#include <texteditor/highlighterutils.h>
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/patchtool.h>
|
2015-02-26 13:22:35 +01:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2014-02-13 16:43:28 +01:00
|
|
|
|
|
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/tooltip/tooltip.h>
|
|
|
|
|
|
2014-06-25 15:44:10 +02:00
|
|
|
//static const int FILE_LEVEL = 1;
|
|
|
|
|
//static const int CHUNK_LEVEL = 2;
|
2014-02-13 16:43:28 +01:00
|
|
|
|
|
|
|
|
using namespace Core;
|
|
|
|
|
using namespace TextEditor;
|
|
|
|
|
|
|
|
|
|
namespace DiffEditor {
|
2015-01-30 14:51:44 +01:00
|
|
|
namespace Internal {
|
2015-01-30 14:46:20 +01:00
|
|
|
|
2014-02-13 16:43:28 +01:00
|
|
|
UnifiedDiffEditorWidget::UnifiedDiffEditorWidget(QWidget *parent)
|
2014-09-01 10:35:06 +02:00
|
|
|
: SelectableTextEditorWidget("DiffEditor.UnifiedDiffEditor", parent)
|
2015-01-30 16:59:25 +01:00
|
|
|
, m_document(0)
|
2014-02-13 16:43:28 +01:00
|
|
|
, m_ignoreCurrentIndexChange(false)
|
|
|
|
|
, m_contextMenuFileIndex(-1)
|
|
|
|
|
, m_contextMenuChunkIndex(-1)
|
|
|
|
|
, m_leftLineNumberDigits(1)
|
|
|
|
|
, m_rightLineNumberDigits(1)
|
|
|
|
|
{
|
|
|
|
|
DisplaySettings settings = displaySettings();
|
|
|
|
|
settings.m_textWrapping = false;
|
|
|
|
|
settings.m_displayLineNumbers = true;
|
|
|
|
|
settings.m_highlightCurrentLine = false;
|
|
|
|
|
settings.m_displayFoldingMarkers = true;
|
|
|
|
|
settings.m_markTextChanges = false;
|
|
|
|
|
settings.m_highlightBlocks = false;
|
|
|
|
|
SelectableTextEditorWidget::setDisplaySettings(settings);
|
|
|
|
|
|
|
|
|
|
setReadOnly(true);
|
2014-08-28 17:00:12 +02:00
|
|
|
connect(TextEditorSettings::instance(), &TextEditorSettings::displaySettingsChanged,
|
|
|
|
|
this, &UnifiedDiffEditorWidget::setDisplaySettings);
|
2014-02-13 16:43:28 +01:00
|
|
|
setDisplaySettings(TextEditorSettings::displaySettings());
|
|
|
|
|
setCodeStyle(TextEditorSettings::codeStyle());
|
|
|
|
|
|
2015-01-30 10:56:10 +01:00
|
|
|
connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
|
|
|
|
|
this, &UnifiedDiffEditorWidget::setFontSettings);
|
2014-02-13 16:43:28 +01:00
|
|
|
setFontSettings(TextEditorSettings::fontSettings());
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
clear(tr("No document"));
|
2014-02-13 16:43:28 +01:00
|
|
|
|
2015-01-30 10:56:10 +01:00
|
|
|
connect(this, &QPlainTextEdit::cursorPositionChanged,
|
|
|
|
|
this, &UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor);
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
void UnifiedDiffEditorWidget::setDocument(DiffEditorDocument *document)
|
2014-02-13 16:43:28 +01:00
|
|
|
{
|
2015-01-30 16:59:25 +01:00
|
|
|
m_document = document;
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
void UnifiedDiffEditorWidget::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 UnifiedDiffEditorWidget::restoreState()
|
2014-07-11 12:55:31 +02:00
|
|
|
{
|
|
|
|
|
if (m_state.isNull())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
const bool oldIgnore = m_ignoreCurrentIndexChange;
|
|
|
|
|
m_ignoreCurrentIndexChange = true;
|
|
|
|
|
SelectableTextEditorWidget::restoreState(m_state);
|
2014-07-11 12:55:31 +02:00
|
|
|
m_state.clear();
|
2015-01-30 16:59:25 +01:00
|
|
|
m_ignoreCurrentIndexChange = oldIgnore;
|
2014-07-11 12:55:31 +02:00
|
|
|
}
|
|
|
|
|
|
2014-02-13 16:43:28 +01:00
|
|
|
void UnifiedDiffEditorWidget::setDisplaySettings(const DisplaySettings &ds)
|
|
|
|
|
{
|
|
|
|
|
DisplaySettings settings = displaySettings();
|
|
|
|
|
settings.m_visualizeWhitespace = ds.m_visualizeWhitespace;
|
|
|
|
|
SelectableTextEditorWidget::setDisplaySettings(settings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::setFontSettings(const FontSettings &fontSettings)
|
|
|
|
|
{
|
|
|
|
|
m_fileLineFormat = fontSettings.toTextCharFormat(C_DIFF_FILE_LINE);
|
|
|
|
|
m_chunkLineFormat = fontSettings.toTextCharFormat(C_DIFF_CONTEXT_LINE);
|
|
|
|
|
m_leftLineFormat = fontSettings.toTextCharFormat(C_DIFF_SOURCE_LINE);
|
|
|
|
|
m_leftCharFormat = fontSettings.toTextCharFormat(C_DIFF_SOURCE_CHAR);
|
|
|
|
|
m_rightLineFormat = fontSettings.toTextCharFormat(C_DIFF_DEST_LINE);
|
|
|
|
|
m_rightCharFormat = fontSettings.toTextCharFormat(C_DIFF_DEST_CHAR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor()
|
|
|
|
|
{
|
|
|
|
|
if (m_ignoreCurrentIndexChange)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const bool oldIgnore = m_ignoreCurrentIndexChange;
|
|
|
|
|
m_ignoreCurrentIndexChange = true;
|
2015-01-30 16:59:25 +01:00
|
|
|
emit currentDiffFileIndexChanged(fileIndexForBlockNumber(textCursor().blockNumber()));
|
2014-02-13 16:43:28 +01:00
|
|
|
m_ignoreCurrentIndexChange = oldIgnore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (e->button() == Qt::LeftButton && !(e->modifiers() & Qt::ShiftModifier)) {
|
|
|
|
|
QTextCursor cursor = cursorForPosition(e->pos());
|
|
|
|
|
jumpToOriginalFile(cursor);
|
|
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SelectableTextEditorWidget::mouseDoubleClickEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
|
|
|
|
{
|
|
|
|
|
QPointer<QMenu> menu = createStandardContextMenu();
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor = cursorForPosition(e->pos());
|
|
|
|
|
const int blockNumber = cursor.blockNumber();
|
|
|
|
|
|
|
|
|
|
addContextMenuActions(menu, fileIndexForBlockNumber(blockNumber),
|
|
|
|
|
chunkIndexForBlockNumber(blockNumber));
|
|
|
|
|
|
2015-02-02 07:17:50 +01:00
|
|
|
connect(this, &UnifiedDiffEditorWidget::destroyed, menu.data(), &QMenu::deleteLater);
|
2014-02-13 16:43:28 +01:00
|
|
|
menu->exec(e->globalPos());
|
|
|
|
|
delete menu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::addContextMenuActions(QMenu *menu,
|
|
|
|
|
int diffFileIndex,
|
|
|
|
|
int chunkIndex)
|
|
|
|
|
{
|
2015-01-30 16:59:25 +01:00
|
|
|
if (!m_document || !m_document->controller())
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
QAction *sendChunkToCodePasterAction =
|
|
|
|
|
menu->addAction(tr("Send Chunk to CodePaster..."));
|
2015-01-30 10:56:10 +01:00
|
|
|
connect(sendChunkToCodePasterAction, &QAction::triggered,
|
|
|
|
|
this, &UnifiedDiffEditorWidget::slotSendChunkToCodePaster);
|
2014-02-13 16:43:28 +01:00
|
|
|
QAction *applyAction = menu->addAction(tr("Apply Chunk..."));
|
2015-01-30 10:56:10 +01:00
|
|
|
connect(applyAction, &QAction::triggered, this, &UnifiedDiffEditorWidget::slotApplyChunk);
|
2014-02-13 16:43:28 +01:00
|
|
|
QAction *revertAction = menu->addAction(tr("Revert Chunk..."));
|
2015-01-30 10:56:10 +01:00
|
|
|
connect(revertAction, &QAction::triggered, this, &UnifiedDiffEditorWidget::slotRevertChunk);
|
2014-02-13 16:43:28 +01:00
|
|
|
|
|
|
|
|
m_contextMenuFileIndex = diffFileIndex;
|
|
|
|
|
m_contextMenuChunkIndex = chunkIndex;
|
|
|
|
|
|
|
|
|
|
applyAction->setEnabled(false);
|
|
|
|
|
revertAction->setEnabled(false);
|
|
|
|
|
|
|
|
|
|
if (m_contextMenuFileIndex < 0 || m_contextMenuChunkIndex < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (m_contextMenuFileIndex >= m_contextFileData.count())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const FileData fileData = m_contextFileData.at(m_contextMenuFileIndex);
|
|
|
|
|
if (m_contextMenuChunkIndex >= fileData.chunks.count())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
m_document->chunkActionsRequested(menu, diffFileIndex, chunkIndex);
|
2014-02-13 16:43:28 +01:00
|
|
|
|
|
|
|
|
revertAction->setEnabled(true);
|
|
|
|
|
|
|
|
|
|
if (fileData.leftFileInfo.fileName == fileData.rightFileInfo.fileName)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
applyAction->setEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::slotSendChunkToCodePaster()
|
|
|
|
|
{
|
2015-01-30 16:59:25 +01:00
|
|
|
if (!m_document)
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
const QString patch = m_document->makePatch(m_contextMenuFileIndex, m_contextMenuChunkIndex, false);
|
2014-02-13 16:43:28 +01:00
|
|
|
|
|
|
|
|
if (patch.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Retrieve service by soft dependency.
|
|
|
|
|
QObject *pasteService =
|
|
|
|
|
ExtensionSystem::PluginManager::getObjectByClassName(
|
|
|
|
|
QLatin1String("CodePaster::CodePasterService"));
|
|
|
|
|
if (pasteService) {
|
|
|
|
|
QMetaObject::invokeMethod(pasteService, "postText",
|
|
|
|
|
Q_ARG(QString, patch),
|
|
|
|
|
Q_ARG(QString, QLatin1String(DiffEditor::Constants::DIFF_EDITOR_MIMETYPE)));
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::information(this, tr("Unable to Paste"),
|
|
|
|
|
tr("Code pasting services are not available."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::slotApplyChunk()
|
|
|
|
|
{
|
2014-07-28 23:25:49 +03:00
|
|
|
patch(false);
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::slotRevertChunk()
|
|
|
|
|
{
|
2014-07-28 23:25:49 +03:00
|
|
|
patch(true);
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-28 23:25:49 +03:00
|
|
|
void UnifiedDiffEditorWidget::patch(bool revert)
|
2014-02-13 16:43:28 +01:00
|
|
|
{
|
2015-01-30 16:59:25 +01:00
|
|
|
if (!m_document)
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QString title = revert ? tr("Revert Chunk") : tr("Apply Chunk");
|
|
|
|
|
const QString question = revert
|
|
|
|
|
? tr("Would you like to revert the chunk?")
|
|
|
|
|
: tr("Would you like to apply the chunk?");
|
|
|
|
|
if (QMessageBox::No == QMessageBox::question(this, title, question,
|
|
|
|
|
QMessageBox::Yes
|
|
|
|
|
| QMessageBox::No)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
const int strip = m_document->baseDirectory().isEmpty() ? -1 : 0;
|
2014-02-13 16:43:28 +01:00
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
const FileData fileData = m_contextFileData.at(m_contextMenuFileIndex);
|
2014-02-13 16:43:28 +01:00
|
|
|
const QString fileName = revert
|
|
|
|
|
? fileData.rightFileInfo.fileName
|
|
|
|
|
: fileData.leftFileInfo.fileName;
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
const QString workingDirectory = m_document->baseDirectory().isEmpty()
|
2014-02-13 16:43:28 +01:00
|
|
|
? QFileInfo(fileName).absolutePath()
|
2015-01-30 16:59:25 +01:00
|
|
|
: m_document->baseDirectory();
|
2014-02-13 16:43:28 +01:00
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
const QString patch = m_document->makePatch(m_contextMenuFileIndex, m_contextMenuChunkIndex, revert);
|
2014-02-13 16:43:28 +01:00
|
|
|
if (patch.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
if (PatchTool::runPatch(EditorManager::defaultTextCodec()->fromUnicode(patch),
|
|
|
|
|
workingDirectory, strip, revert))
|
|
|
|
|
m_document->reload();
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::clear(const QString &message)
|
|
|
|
|
{
|
|
|
|
|
m_leftLineNumberDigits = 1;
|
|
|
|
|
m_rightLineNumberDigits = 1;
|
|
|
|
|
m_leftLineNumbers.clear();
|
|
|
|
|
m_rightLineNumbers.clear();
|
|
|
|
|
m_fileInfo.clear();
|
|
|
|
|
m_chunkInfo.clear();
|
|
|
|
|
setSelections(QMap<int, QList<DiffSelection> >());
|
|
|
|
|
|
|
|
|
|
const bool oldIgnore = m_ignoreCurrentIndexChange;
|
|
|
|
|
m_ignoreCurrentIndexChange = true;
|
|
|
|
|
SelectableTextEditorWidget::clear();
|
2015-01-30 16:59:25 +01:00
|
|
|
setDiff(QList<FileData>(), QString());
|
2014-02-13 16:43:28 +01:00
|
|
|
setPlainText(message);
|
|
|
|
|
m_ignoreCurrentIndexChange = oldIgnore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString UnifiedDiffEditorWidget::lineNumber(int blockNumber) const
|
|
|
|
|
{
|
|
|
|
|
QString lineNumberString;
|
|
|
|
|
|
|
|
|
|
const bool leftLineExists = m_leftLineNumbers.contains(blockNumber);
|
|
|
|
|
const bool rightLineExists = m_rightLineNumbers.contains(blockNumber);
|
|
|
|
|
|
|
|
|
|
if (leftLineExists || rightLineExists) {
|
|
|
|
|
const QString leftLine = leftLineExists
|
|
|
|
|
? QString::number(m_leftLineNumbers.value(blockNumber))
|
|
|
|
|
: QString();
|
|
|
|
|
lineNumberString += QString(m_leftLineNumberDigits - leftLine.count(),
|
|
|
|
|
QLatin1Char(' ')) + leftLine;
|
|
|
|
|
|
|
|
|
|
lineNumberString += QLatin1Char('|');
|
|
|
|
|
|
|
|
|
|
const QString rightLine = rightLineExists
|
|
|
|
|
? QString::number(m_rightLineNumbers.value(blockNumber))
|
|
|
|
|
: QString();
|
|
|
|
|
lineNumberString += QString(m_rightLineNumberDigits - rightLine.count(),
|
|
|
|
|
QLatin1Char(' ')) + rightLine;
|
|
|
|
|
}
|
|
|
|
|
return lineNumberString;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int UnifiedDiffEditorWidget::lineNumberDigits() const
|
|
|
|
|
{
|
|
|
|
|
return m_leftLineNumberDigits + m_rightLineNumberDigits + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::setLeftLineNumber(int blockNumber, int lineNumber)
|
|
|
|
|
{
|
|
|
|
|
const QString lineNumberString = QString::number(lineNumber);
|
|
|
|
|
m_leftLineNumbers.insert(blockNumber, lineNumber);
|
|
|
|
|
m_leftLineNumberDigits = qMax(m_leftLineNumberDigits,
|
|
|
|
|
lineNumberString.count());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::setRightLineNumber(int blockNumber, int lineNumber)
|
|
|
|
|
{
|
|
|
|
|
const QString lineNumberString = QString::number(lineNumber);
|
|
|
|
|
m_rightLineNumbers.insert(blockNumber, lineNumber);
|
|
|
|
|
m_rightLineNumberDigits = qMax(m_rightLineNumberDigits,
|
|
|
|
|
lineNumberString.count());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::setFileInfo(int blockNumber,
|
|
|
|
|
const DiffFileInfo &leftFileInfo,
|
|
|
|
|
const DiffFileInfo &rightFileInfo)
|
|
|
|
|
{
|
|
|
|
|
m_fileInfo[blockNumber] = qMakePair(leftFileInfo, rightFileInfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::setChunkIndex(int startBlockNumber,
|
|
|
|
|
int blockCount,
|
|
|
|
|
int chunkIndex)
|
|
|
|
|
{
|
|
|
|
|
m_chunkInfo.insert(startBlockNumber, qMakePair(blockCount, chunkIndex));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::setDiff(const QList<FileData> &diffFileList,
|
|
|
|
|
const QString &workingDirectory)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(workingDirectory)
|
|
|
|
|
|
|
|
|
|
m_contextFileData = diffFileList;
|
|
|
|
|
|
|
|
|
|
showDiff();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData,
|
|
|
|
|
bool lastChunk,
|
|
|
|
|
int *blockNumber,
|
|
|
|
|
int *charNumber,
|
|
|
|
|
QMap<int, QList<DiffSelection> > *selections)
|
|
|
|
|
{
|
|
|
|
|
if (chunkData.contextChunk)
|
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
|
|
QString diffText;
|
|
|
|
|
int leftLineCount = 0;
|
|
|
|
|
int rightLineCount = 0;
|
|
|
|
|
int blockCount = 0;
|
|
|
|
|
int charCount = 0;
|
|
|
|
|
QList<TextLineData> leftBuffer, rightBuffer;
|
|
|
|
|
|
|
|
|
|
(*selections)[*blockNumber].append(DiffSelection(&m_chunkLineFormat));
|
|
|
|
|
|
2014-07-09 12:37:47 +02:00
|
|
|
int lastEqualRow = -1;
|
|
|
|
|
if (lastChunk) {
|
|
|
|
|
for (int i = chunkData.rows.count(); i > 0; i--) {
|
|
|
|
|
if (chunkData.rows.at(i - 1).equal) {
|
|
|
|
|
if (i != chunkData.rows.count())
|
|
|
|
|
lastEqualRow = i - 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-13 16:43:28 +01:00
|
|
|
for (int i = 0; i <= chunkData.rows.count(); i++) {
|
|
|
|
|
const RowData &rowData = i < chunkData.rows.count()
|
|
|
|
|
? chunkData.rows.at(i)
|
|
|
|
|
: RowData(TextLineData(TextLineData::Separator)); // dummy,
|
|
|
|
|
// ensure we process buffers to the end.
|
|
|
|
|
// rowData will be equal
|
2014-07-09 12:37:47 +02:00
|
|
|
if (rowData.equal && i != lastEqualRow) {
|
2014-02-13 16:43:28 +01:00
|
|
|
if (leftBuffer.count()) {
|
|
|
|
|
for (int j = 0; j < leftBuffer.count(); j++) {
|
|
|
|
|
const TextLineData &lineData = leftBuffer.at(j);
|
|
|
|
|
const QString line = DiffUtils::makePatchLine(
|
|
|
|
|
QLatin1Char('-'),
|
|
|
|
|
lineData.text,
|
|
|
|
|
lastChunk,
|
|
|
|
|
i == chunkData.rows.count()
|
|
|
|
|
&& j == leftBuffer.count() - 1);
|
|
|
|
|
|
|
|
|
|
const int blockDelta = line.count(QLatin1Char('\n')); // no new line
|
|
|
|
|
// could have been added
|
|
|
|
|
for (int k = 0; k < blockDelta; k++)
|
|
|
|
|
(*selections)[*blockNumber + blockCount + 1 + k].append(&m_leftLineFormat);
|
|
|
|
|
QMapIterator<int, int> itPos(lineData.changedPositions);
|
|
|
|
|
while (itPos.hasNext()) {
|
|
|
|
|
itPos.next();
|
|
|
|
|
const int startPos = itPos.key() < 0
|
|
|
|
|
? 1 : itPos.key() + 1;
|
|
|
|
|
const int endPos = itPos.value() < 0
|
|
|
|
|
? itPos.value() : itPos.value() + 1;
|
|
|
|
|
(*selections)[*blockNumber + blockCount + 1].append(
|
|
|
|
|
DiffSelection(startPos, endPos, &m_leftCharFormat));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!line.isEmpty()) {
|
|
|
|
|
setLeftLineNumber(*blockNumber + blockCount + 1,
|
|
|
|
|
chunkData.leftStartingLineNumber
|
|
|
|
|
+ leftLineCount + 1);
|
|
|
|
|
blockCount += blockDelta;
|
|
|
|
|
++leftLineCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
diffText += line;
|
|
|
|
|
|
|
|
|
|
charCount += line.count();
|
|
|
|
|
}
|
|
|
|
|
leftBuffer.clear();
|
|
|
|
|
}
|
|
|
|
|
if (rightBuffer.count()) {
|
|
|
|
|
for (int j = 0; j < rightBuffer.count(); j++) {
|
|
|
|
|
const TextLineData &lineData = rightBuffer.at(j);
|
|
|
|
|
const QString line = DiffUtils::makePatchLine(
|
|
|
|
|
QLatin1Char('+'),
|
|
|
|
|
lineData.text,
|
|
|
|
|
lastChunk,
|
|
|
|
|
i == chunkData.rows.count()
|
|
|
|
|
&& j == rightBuffer.count() - 1);
|
|
|
|
|
|
|
|
|
|
const int blockDelta = line.count(QLatin1Char('\n')); // no new line
|
|
|
|
|
// could have been added
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < blockDelta; k++)
|
|
|
|
|
(*selections)[*blockNumber + blockCount + 1 + k].append(&m_rightLineFormat);
|
|
|
|
|
QMapIterator<int, int> itPos(lineData.changedPositions);
|
|
|
|
|
while (itPos.hasNext()) {
|
|
|
|
|
itPos.next();
|
|
|
|
|
const int startPos = itPos.key() < 0
|
|
|
|
|
? 1 : itPos.key() + 1;
|
|
|
|
|
const int endPos = itPos.value() < 0
|
|
|
|
|
? itPos.value() : itPos.value() + 1;
|
|
|
|
|
(*selections)[*blockNumber + blockCount + 1].append
|
|
|
|
|
(DiffSelection(startPos, endPos, &m_rightCharFormat));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!line.isEmpty()) {
|
|
|
|
|
setRightLineNumber(*blockNumber + blockCount + 1,
|
|
|
|
|
chunkData.rightStartingLineNumber
|
|
|
|
|
+ rightLineCount + 1);
|
|
|
|
|
blockCount += blockDelta;
|
|
|
|
|
++rightLineCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
diffText += line;
|
|
|
|
|
|
|
|
|
|
charCount += line.count();
|
|
|
|
|
}
|
|
|
|
|
rightBuffer.clear();
|
|
|
|
|
}
|
|
|
|
|
if (i < chunkData.rows.count()) {
|
|
|
|
|
const QString line = DiffUtils::makePatchLine(QLatin1Char(' '),
|
|
|
|
|
rowData.rightLine.text,
|
|
|
|
|
lastChunk,
|
|
|
|
|
i == chunkData.rows.count() - 1);
|
|
|
|
|
|
|
|
|
|
if (!line.isEmpty()) {
|
|
|
|
|
setLeftLineNumber(*blockNumber + blockCount + 1,
|
|
|
|
|
chunkData.leftStartingLineNumber
|
|
|
|
|
+ leftLineCount + 1);
|
|
|
|
|
setRightLineNumber(*blockNumber + blockCount + 1,
|
|
|
|
|
chunkData.rightStartingLineNumber
|
|
|
|
|
+ rightLineCount + 1);
|
|
|
|
|
blockCount += line.count(QLatin1Char('\n'));
|
|
|
|
|
++leftLineCount;
|
|
|
|
|
++rightLineCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
diffText += line;
|
|
|
|
|
|
|
|
|
|
charCount += line.count();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (rowData.leftLine.textLineType == TextLineData::TextLine)
|
|
|
|
|
leftBuffer.append(rowData.leftLine);
|
|
|
|
|
if (rowData.rightLine.textLineType == TextLineData::TextLine)
|
|
|
|
|
rightBuffer.append(rowData.rightLine);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString chunkLine = QLatin1String("@@ -")
|
|
|
|
|
+ QString::number(chunkData.leftStartingLineNumber + 1)
|
|
|
|
|
+ QLatin1Char(',')
|
|
|
|
|
+ QString::number(leftLineCount)
|
|
|
|
|
+ QLatin1String(" +")
|
|
|
|
|
+ QString::number(chunkData.rightStartingLineNumber+ 1)
|
|
|
|
|
+ QLatin1Char(',')
|
|
|
|
|
+ QString::number(rightLineCount)
|
2014-08-14 17:20:42 +02:00
|
|
|
+ QLatin1String(" @@")
|
|
|
|
|
+ chunkData.contextInfo
|
|
|
|
|
+ QLatin1Char('\n');
|
2014-02-13 16:43:28 +01:00
|
|
|
|
|
|
|
|
diffText.prepend(chunkLine);
|
|
|
|
|
|
|
|
|
|
*blockNumber += blockCount + 1; // +1 for chunk line
|
|
|
|
|
*charNumber += charCount + chunkLine.count();
|
|
|
|
|
return diffText;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::showDiff()
|
|
|
|
|
{
|
|
|
|
|
QString diffText;
|
|
|
|
|
|
|
|
|
|
int blockNumber = 0;
|
|
|
|
|
int charNumber = 0;
|
|
|
|
|
|
|
|
|
|
QMap<int, QList<DiffSelection> > selections;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_contextFileData.count(); i++) {
|
|
|
|
|
const FileData &fileData = m_contextFileData.at(i);
|
|
|
|
|
const QString leftFileInfo = QLatin1String("--- ")
|
|
|
|
|
+ fileData.leftFileInfo.fileName + QLatin1Char('\n');
|
|
|
|
|
const QString rightFileInfo = QLatin1String("+++ ")
|
|
|
|
|
+ fileData.rightFileInfo.fileName + QLatin1Char('\n');
|
|
|
|
|
setFileInfo(blockNumber, fileData.leftFileInfo, fileData.rightFileInfo);
|
|
|
|
|
selections[blockNumber].append(DiffSelection(&m_fileLineFormat));
|
|
|
|
|
blockNumber++;
|
|
|
|
|
selections[blockNumber].append(DiffSelection(&m_fileLineFormat));
|
|
|
|
|
blockNumber++;
|
|
|
|
|
|
|
|
|
|
diffText += leftFileInfo;
|
|
|
|
|
diffText += rightFileInfo;
|
|
|
|
|
charNumber += leftFileInfo.count() + rightFileInfo.count();
|
|
|
|
|
|
|
|
|
|
if (fileData.binaryFiles) {
|
|
|
|
|
selections[blockNumber].append(DiffSelection(&m_chunkLineFormat));
|
|
|
|
|
blockNumber++;
|
|
|
|
|
const QString binaryLine = QLatin1String("Binary files ")
|
|
|
|
|
+ fileData.leftFileInfo.fileName
|
|
|
|
|
+ QLatin1String(" and ")
|
|
|
|
|
+ fileData.rightFileInfo.fileName
|
|
|
|
|
+ QLatin1String(" differ\n");
|
|
|
|
|
diffText += binaryLine;
|
|
|
|
|
charNumber += binaryLine.count();
|
|
|
|
|
} else {
|
|
|
|
|
for (int j = 0; j < fileData.chunks.count(); j++) {
|
|
|
|
|
const int oldBlockNumber = blockNumber;
|
|
|
|
|
diffText += showChunk(fileData.chunks.at(j),
|
|
|
|
|
(j == fileData.chunks.count() - 1)
|
|
|
|
|
&& fileData.lastChunkAtTheEndOfFile,
|
|
|
|
|
&blockNumber,
|
|
|
|
|
&charNumber,
|
|
|
|
|
&selections);
|
|
|
|
|
if (!fileData.chunks.at(j).contextChunk)
|
|
|
|
|
setChunkIndex(oldBlockNumber, blockNumber - oldBlockNumber, j);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
if (diffText.isEmpty()) {
|
|
|
|
|
setPlainText(tr("No difference."));
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
2015-01-30 16:59:25 +01:00
|
|
|
}
|
2014-02-13 16:43:28 +01:00
|
|
|
|
|
|
|
|
diffText.replace(QLatin1Char('\r'), QLatin1Char(' '));
|
|
|
|
|
const bool oldIgnore = m_ignoreCurrentIndexChange;
|
|
|
|
|
m_ignoreCurrentIndexChange = true;
|
|
|
|
|
setPlainText(diffText);
|
|
|
|
|
m_ignoreCurrentIndexChange = oldIgnore;
|
|
|
|
|
|
|
|
|
|
setSelections(selections);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int UnifiedDiffEditorWidget::blockNumberForFileIndex(int fileIndex) const
|
|
|
|
|
{
|
|
|
|
|
if (fileIndex < 0 || fileIndex >= m_fileInfo.count())
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
QMap<int, QPair<DiffFileInfo, DiffFileInfo> >::const_iterator it
|
|
|
|
|
= m_fileInfo.constBegin();
|
|
|
|
|
for (int i = 0; i < fileIndex; i++)
|
|
|
|
|
++it;
|
|
|
|
|
|
|
|
|
|
return it.key();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int UnifiedDiffEditorWidget::fileIndexForBlockNumber(int blockNumber) const
|
|
|
|
|
{
|
|
|
|
|
QMap<int, QPair<DiffFileInfo, DiffFileInfo> >::const_iterator it
|
|
|
|
|
= m_fileInfo.constBegin();
|
|
|
|
|
QMap<int, QPair<DiffFileInfo, DiffFileInfo> >::const_iterator itEnd
|
|
|
|
|
= m_fileInfo.constEnd();
|
|
|
|
|
|
|
|
|
|
int i = -1;
|
|
|
|
|
while (it != itEnd) {
|
|
|
|
|
if (it.key() > blockNumber)
|
|
|
|
|
break;
|
|
|
|
|
++it;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int UnifiedDiffEditorWidget::chunkIndexForBlockNumber(int blockNumber) const
|
|
|
|
|
{
|
|
|
|
|
if (m_chunkInfo.isEmpty())
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
QMap<int, QPair<int, int> >::const_iterator it
|
|
|
|
|
= m_chunkInfo.upperBound(blockNumber);
|
|
|
|
|
if (it == m_chunkInfo.constBegin())
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
--it;
|
|
|
|
|
|
|
|
|
|
if (blockNumber < it.key() + it.value().first)
|
|
|
|
|
return it.value().second;
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
if (m_fileInfo.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const int blockNumber = cursor.blockNumber();
|
2014-07-04 10:51:38 +02:00
|
|
|
const int fileIndex = fileIndexForBlockNumber(blockNumber);
|
|
|
|
|
if (fileIndex < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const FileData fileData = m_contextFileData.at(fileIndex);
|
|
|
|
|
const QString leftFileName = fileData.leftFileInfo.fileName;
|
|
|
|
|
const QString rightFileName = fileData.rightFileInfo.fileName;
|
|
|
|
|
|
2014-02-13 16:43:28 +01:00
|
|
|
const int columnNumber = cursor.positionInBlock() - 1; // -1 for the first character in line
|
|
|
|
|
|
|
|
|
|
const int rightLineNumber = m_rightLineNumbers.value(blockNumber, -1);
|
|
|
|
|
if (rightLineNumber >= 0) {
|
2014-07-04 10:51:38 +02:00
|
|
|
jumpToOriginalFile(rightFileName, rightLineNumber, columnNumber);
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int leftLineNumber = m_leftLineNumbers.value(blockNumber, -1);
|
|
|
|
|
if (leftLineNumber >= 0) {
|
2014-07-04 10:51:38 +02:00
|
|
|
if (leftFileName == rightFileName) {
|
|
|
|
|
for (int i = 0; i < fileData.chunks.count(); i++) {
|
|
|
|
|
const ChunkData chunkData = fileData.chunks.at(i);
|
|
|
|
|
|
|
|
|
|
int newLeftLineNumber = chunkData.leftStartingLineNumber;
|
|
|
|
|
int newRightLineNumber = chunkData.rightStartingLineNumber;
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < chunkData.rows.count(); j++) {
|
|
|
|
|
const RowData rowData = chunkData.rows.at(j);
|
|
|
|
|
if (rowData.leftLine.textLineType == TextLineData::TextLine)
|
|
|
|
|
newLeftLineNumber++;
|
|
|
|
|
if (rowData.rightLine.textLineType == TextLineData::TextLine)
|
|
|
|
|
newRightLineNumber++;
|
|
|
|
|
if (newLeftLineNumber == leftLineNumber) {
|
|
|
|
|
jumpToOriginalFile(leftFileName, newRightLineNumber, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
jumpToOriginalFile(leftFileName, leftLineNumber, columnNumber);
|
|
|
|
|
}
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::jumpToOriginalFile(const QString &fileName,
|
|
|
|
|
int lineNumber,
|
|
|
|
|
int columnNumber)
|
|
|
|
|
{
|
2015-01-30 16:59:25 +01:00
|
|
|
if (!m_document)
|
2014-02-13 16:43:28 +01:00
|
|
|
return;
|
|
|
|
|
|
2015-01-30 16:59:25 +01:00
|
|
|
const QDir dir(m_document->baseDirectory());
|
2014-02-13 16:43:28 +01:00
|
|
|
const QString absoluteFileName = dir.absoluteFilePath(fileName);
|
2014-06-18 12:04:14 +03:00
|
|
|
QFileInfo fi(absoluteFileName);
|
|
|
|
|
if (fi.exists() && !fi.isDir())
|
2015-02-03 23:54:47 +02:00
|
|
|
EditorManager::openEditorAt(absoluteFileName, lineNumber, columnNumber);
|
2014-02-13 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnifiedDiffEditorWidget::setCurrentDiffFileIndex(int diffFileIndex)
|
|
|
|
|
{
|
|
|
|
|
if (m_ignoreCurrentIndexChange)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const bool oldIgnore = m_ignoreCurrentIndexChange;
|
|
|
|
|
m_ignoreCurrentIndexChange = true;
|
|
|
|
|
const int blockNumber = blockNumberForFileIndex(diffFileIndex);
|
|
|
|
|
|
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.setPosition(block.position());
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
centerCursor();
|
|
|
|
|
m_ignoreCurrentIndexChange = oldIgnore;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-30 14:51:44 +01:00
|
|
|
} // namespace Internal
|
2014-02-13 16:43:28 +01:00
|
|
|
} // namespace DiffEditor
|