Commit 8cad9453 authored by jkobus's avatar jkobus Committed by Jarek Kobus

Implement unified diff editor

Change-Id: I93e0bfd71a8a650afbe2ca9e0f1f3dbfc9d57db0
Reviewed-by: default avatarJarek Kobus <jaroslaw.kobus@digia.com>
parent 8cb25f9e
......@@ -42,7 +42,6 @@
<file>images/replace.png</file>
<file>images/reset.png</file>
<file>images/sidebaricon.png</file>
<file>images/topbaricon.png</file>
<file>images/splitbutton_horizontal.png</file>
<file>images/splitbutton_horizontal@2x.png</file>
<file>images/statusbar.png</file>
......@@ -89,8 +88,6 @@
<file>images/splitbutton_vertical.png</file>
<file>images/splitbutton_vertical@2x.png</file>
<file>images/panel_manage_button.png</file>
<file>images/sidebysidediff.png</file>
<file>images/textdiff.png</file>
<file>images/pause.png</file>
</qresource>
</RCC>
......@@ -197,7 +197,6 @@ const char ICON_CLEAR[] = ":/core/images/clear.png";
const char ICON_RESET[] = ":/core/images/reset.png";
const char ICON_MAGNIFIER[] = ":/core/images/magnifier.png";
const char ICON_TOGGLE_SIDEBAR[] = ":/core/images/sidebaricon.png";
const char ICON_TOGGLE_TOPBAR[] = ":/core/images/topbaricon.png";
const char ICON_CLOSE_DOCUMENT[] = ":/core/images/button_close.png";
const char ICON_CLOSE[] = ":/core/images/closebutton.png";
const char ICON_CLOSE_DARK[] = ":/core/images/darkclosebutton.png";
......@@ -207,8 +206,6 @@ const char ICON_CLOSE_SPLIT_TOP[] = ":/core/images/splitbutton_closetop.png";
const char ICON_CLOSE_SPLIT_BOTTOM[] = ":/core/images/splitbutton_closebottom.png";
const char ICON_CLOSE_SPLIT_LEFT[] = ":/core/images/splitbutton_closeleft.png";
const char ICON_CLOSE_SPLIT_RIGHT[] = ":/core/images/splitbutton_closeright.png";
const char ICON_SIDE_BY_SIDE_DIFF[] = ":/core/images/sidebysidediff.png";
const char ICON_TEXT_DIFF[] = ":/core/images/textdiff.png";
const char ICON_FILTER[] = ":/core/images/filtericon.png";
const char ICON_LINK[] = ":/core/images/linkicon.png";
const char ICON_PAUSE[] = ":/core/images/pause.png";
......
......@@ -93,7 +93,9 @@ bool PatchTool::runPatch(const QByteArray &input, const QString &workingDirector
QProcess patchProcess;
if (!workingDirectory.isEmpty())
patchProcess.setWorkingDirectory(workingDirectory);
QStringList args(QLatin1String("-p") + QString::number(strip));
QStringList args;
if (strip >= 0)
args << (QLatin1String("-p") + QString::number(strip));
if (reverse)
args << QLatin1String("-R");
MessageManager::write(QApplication::translate("Core::PatchTool", "Executing in %1: %2 %3").
......
This diff is collapsed.
......@@ -37,9 +37,10 @@
#include <coreplugin/idocument.h>
QT_BEGIN_NAMESPACE
class QToolBar;
class QComboBox;
class QToolBar;
class QToolButton;
class QStackedWidget;
QT_END_NAMESPACE
namespace TextEditor { class BaseTextEditorWidget; }
......@@ -49,6 +50,7 @@ namespace DiffEditor {
class DiffEditorDocument;
class DiffEditorGuiController;
class SideBySideDiffEditorWidget;
class UnifiedDiffEditorWidget;
class DIFFEDITOR_EXPORT DiffEditor : public Core::IEditor
{
......@@ -64,7 +66,9 @@ public:
// Core::IEditor
Core::IEditor *duplicate();
bool open(QString *errorString, const QString &fileName, const QString &realFileName);
bool open(QString *errorString,
const QString &fileName,
const QString &realFileName);
Core::IDocument *document();
QWidget *toolBar();
......@@ -74,24 +78,34 @@ public slots:
private slots:
void slotCleared(const QString &message);
void slotDiffContentsChanged(const QList<DiffEditorController::DiffFilesContents> &diffFileList,
const QString &workingDirectory);
void slotDiffFilesChanged(const QList<FileData> &diffFileList,
const QString &workingDirectory);
void entryActivated(int index);
void slotDescriptionChanged(const QString &description);
void slotDescriptionVisibilityChanged();
void slotDiffEditorSwitched();
private:
void ctor();
void updateEntryToolTip();
void showDiffEditor(QWidget *newEditor);
void updateDiffEditorSwitcher();
QWidget *readLegacyCurrentDiffEditorSetting();
QWidget *readCurrentDiffEditorSetting();
void writeCurrentDiffEditorSetting(QWidget *currentEditor);
QSharedPointer<DiffEditorDocument> m_document;
TextEditor::BaseTextEditorWidget *m_descriptionWidget;
SideBySideDiffEditorWidget *m_diffWidget;
QStackedWidget *m_stackedWidget;
SideBySideDiffEditorWidget *m_sideBySideEditor;
UnifiedDiffEditorWidget *m_unifiedEditor;
QWidget *m_currentEditor;
DiffEditorController *m_controller;
DiffEditorGuiController *m_guiController;
QToolBar *m_toolBar;
QComboBox *m_entriesComboBox;
QAction *m_toggleDescriptionAction;
QToolButton *m_diffEditorSwitcher;
};
} // namespace DiffEditor
......
......@@ -10,9 +10,12 @@ HEADERS += diffeditor_global.h \
diffeditorguicontroller.h \
diffeditormanager.h \
diffeditorplugin.h \
diffeditorreloader.h \
differ.h \
diffutils.h \
sidebysidediffeditorwidget.h
selectabletexteditorwidget.h \
sidebysidediffeditorwidget.h \
unifieddiffeditorwidget.h
SOURCES += diffeditor.cpp \
diffeditorcontroller.cpp \
......@@ -21,8 +24,11 @@ SOURCES += diffeditor.cpp \
diffeditorguicontroller.cpp \
diffeditormanager.cpp \
diffeditorplugin.cpp \
diffeditorreloader.cpp \
differ.cpp \
diffutils.cpp \
sidebysidediffeditorwidget.cpp
selectabletexteditorwidget.cpp \
sidebysidediffeditorwidget.cpp \
unifieddiffeditorwidget.cpp
RESOURCES +=
RESOURCES += diffeditor.qrc
......@@ -15,6 +15,7 @@ QtcPlugin {
files: [
"diffeditor.cpp",
"diffeditor.h",
"diffeditor.qrc",
"diffeditor_global.h",
"diffeditorconstants.h",
"diffeditorcontroller.cpp",
......@@ -29,12 +30,16 @@ QtcPlugin {
"diffeditormanager.h",
"diffeditorplugin.cpp",
"diffeditorplugin.h",
"diffeditorreloader.cpp",
"diffeditorreloader.h",
"differ.cpp",
"differ.h",
"diffutils.cpp",
"diffutils.h",
"sidebysidediffeditorwidget.cpp",
"sidebysidediffeditorwidget.h",
"unifieddiffeditorwidget.cpp",
"unifieddiffeditorwidget.h",
]
}
<RCC>
<qresource prefix="/diffeditor">
<file>images/reload.png</file>
<file>images/sidebysidediff.png</file>
<file>images/unifieddiff.png</file>
<file>images/topbar.png</file>
</qresource>
</RCC>
......@@ -37,8 +37,14 @@ namespace Constants {
const char DIFF_EDITOR_ID[] = "Diff Editor";
const char DIFF_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("DiffEditor", "Diff Editor");
const char DIFF_EDITOR_MIMETYPE[] = "text/x-patch";
const char G_TOOLS_DIFF[] = "QtCreator.Group.Tools.Options";
const char ICON_SIDE_BY_SIDE_DIFF[] = ":/diffeditor/images/sidebysidediff.png";
const char ICON_UNIFIED_DIFF[] = ":/diffeditor/images/unifieddiff.png";
const char ICON_RELOAD[] = ":/diffeditor/images/reload.png";
const char ICON_TOP_BAR[] = ":/diffeditor/images/topbar.png";
} // namespace Constants
} // namespace DiffEditor
......
......@@ -29,12 +29,28 @@
#include "diffeditorcontroller.h"
#include <coreplugin/icore.h>
static const char settingsGroupC[] = "DiffEditor";
static const char contextLineNumbersKeyC[] = "ContextLineNumbers";
static const char ignoreWhitespaceKeyC[] = "IgnoreWhitespace";
namespace DiffEditor {
DiffEditorController::DiffEditorController(QObject *parent)
: QObject(parent),
m_descriptionEnabled(false)
m_descriptionEnabled(false),
m_contextLinesNumber(3),
m_ignoreWhitespace(true)
{
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
m_contextLinesNumber = s->value(QLatin1String(contextLineNumbersKeyC),
m_contextLinesNumber).toInt();
m_ignoreWhitespace = s->value(QLatin1String(ignoreWhitespaceKeyC),
m_ignoreWhitespace).toBool();
s->endGroup();
clear();
}
......@@ -48,9 +64,9 @@ QString DiffEditorController::clearMessage() const
return m_clearMessage;
}
QList<DiffEditorController::DiffFilesContents> DiffEditorController::diffContents() const
QList<FileData> DiffEditorController::diffFiles() const
{
return m_diffFileList;
return m_diffFiles;
}
QString DiffEditorController::workingDirectory() const
......@@ -68,6 +84,43 @@ bool DiffEditorController::isDescriptionEnabled() const
return m_descriptionEnabled;
}
int DiffEditorController::contextLinesNumber() const
{
return m_contextLinesNumber;
}
bool DiffEditorController::isIgnoreWhitespace() const
{
return m_ignoreWhitespace;
}
QString DiffEditorController::makePatch(int diffFileIndex,
int chunkIndex,
bool revert) const
{
if (diffFileIndex < 0 || chunkIndex < 0)
return QString();
if (diffFileIndex >= m_diffFiles.count())
return QString();
const FileData fileData = m_diffFiles.at(diffFileIndex);
if (chunkIndex >= fileData.chunks.count())
return QString();
const ChunkData chunkData = fileData.chunks.at(chunkIndex);
const bool lastChunk = (chunkIndex == fileData.chunks.count() - 1);
const QString fileName = revert
? fileData.rightFileInfo.fileName
: fileData.leftFileInfo.fileName;
return DiffUtils::makePatch(chunkData,
fileName,
fileName,
lastChunk && fileData.lastChunkAtTheEndOfFile);
}
void DiffEditorController::clear()
{
clear(tr("No difference"));
......@@ -75,16 +128,18 @@ void DiffEditorController::clear()
void DiffEditorController::clear(const QString &message)
{
setDescription(QString());
setDiffFiles(QList<FileData>());
m_clearMessage = message;
emit cleared(message);
}
void DiffEditorController::setDiffContents(const QList<DiffFilesContents> &diffFileList,
const QString &workingDirectory)
void DiffEditorController::setDiffFiles(const QList<FileData> &diffFileList,
const QString &workingDirectory)
{
m_diffFileList = diffFileList;
m_diffFiles = diffFileList;
m_workingDirectory = workingDirectory;
emit diffContentsChanged(diffFileList, workingDirectory);
emit diffFilesChanged(diffFileList, workingDirectory);
}
void DiffEditorController::setDescription(const QString &description)
......@@ -105,4 +160,40 @@ void DiffEditorController::setDescriptionEnabled(bool on)
emit descriptionEnablementChanged(on);
}
void DiffEditorController::setContextLinesNumber(int lines)
{
const int l = qMax(lines, 1);
if (m_contextLinesNumber == l)
return;
m_contextLinesNumber = l;
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
s->setValue(QLatin1String(contextLineNumbersKeyC), m_contextLinesNumber);
s->endGroup();
emit contextLinesNumberChanged(l);
}
void DiffEditorController::setIgnoreWhitespace(bool ignore)
{
if (m_ignoreWhitespace == ignore)
return;
m_ignoreWhitespace = ignore;
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
s->setValue(QLatin1String(ignoreWhitespaceKeyC), m_ignoreWhitespace);
s->endGroup();
emit ignoreWhitespaceChanged(ignore);
}
void DiffEditorController::requestReload()
{
emit reloadRequested();
}
} // namespace DiffEditor
......@@ -31,6 +31,7 @@
#define DIFFEDITORCONTROLLER_H
#include "diffeditor_global.h"
#include "diffutils.h"
#include <QObject>
......@@ -40,54 +41,55 @@ class DIFFEDITOR_EXPORT DiffEditorController : public QObject
{
Q_OBJECT
public:
class DiffFileInfo {
public:
DiffFileInfo() {}
DiffFileInfo(const QString &file) : fileName(file) {}
DiffFileInfo(const QString &file, const QString &type) : fileName(file), typeInfo(type) {}
QString fileName;
QString typeInfo;
};
class DiffFilesContents {
public:
DiffFileInfo leftFileInfo;
QString leftText;
DiffFileInfo rightFileInfo;
QString rightText;
};
DiffEditorController(QObject *parent = 0);
~DiffEditorController();
QString clearMessage() const;
QList<DiffFilesContents> diffContents() const;
QList<FileData> diffFiles() const;
QString workingDirectory() const;
QString description() const;
bool isDescriptionEnabled() const;
int contextLinesNumber() const;
bool isIgnoreWhitespace() const;
QString makePatch(int diffFileIndex, int chunkIndex, bool revert) const;
signals:
void chunkActionsRequested(QMenu *menu,
int diffFileIndex,
int chunkIndex);
public slots:
void clear();
void clear(const QString &message);
void setDiffContents(const QList<DiffEditorController::DiffFilesContents> &diffFileList,
const QString &workingDirectory = QString());
void setDiffFiles(const QList<FileData> &diffFileList,
const QString &workingDirectory = QString());
void setDescription(const QString &description);
void setDescriptionEnabled(bool on);
void setContextLinesNumber(int lines);
void setIgnoreWhitespace(bool ignore);
void requestReload();
signals:
void cleared(const QString &message);
void diffContentsChanged(const QList<DiffEditorController::DiffFilesContents> &diffFileList, const QString &workingDirectory);
void diffFilesChanged(const QList<FileData> &diffFileList,
const QString &workingDirectory);
void descriptionChanged(const QString &description);
void descriptionEnablementChanged(bool on);
void contextLinesNumberChanged(int lines);
void ignoreWhitespaceChanged(bool ignore);
void reloadRequested();
private:
QString m_clearMessage;
QList<DiffFilesContents> m_diffFileList;
QList<FileData> m_diffFiles;
QString m_workingDirectory;
QString m_description;
bool m_descriptionEnabled;
int m_contextLinesNumber;
bool m_ignoreWhitespace;
};
} // namespace DiffEditor
......
......@@ -43,6 +43,7 @@ DiffEditorFactory::DiffEditorFactory(QObject *parent)
{
setId(Constants::DIFF_EDITOR_ID);
setDisplayName(qApp->translate("DiffEditorFactory", Constants::DIFF_EDITOR_DISPLAY_NAME));
addMimeType(Constants::DIFF_EDITOR_MIMETYPE);
}
Core::IEditor *DiffEditorFactory::createEditor()
......
......@@ -30,19 +30,35 @@
#include "diffeditorguicontroller.h"
#include "diffeditorcontroller.h"
#include <coreplugin/icore.h>
static const char settingsGroupC[] = "DiffEditor";
static const char descriptionVisibleKeyC[] = "DescriptionVisible";
static const char horizontalScrollBarSynchronizationKeyC[] =
"HorizontalScrollBarSynchronization";
namespace DiffEditor {
DiffEditorGuiController::DiffEditorGuiController(DiffEditorController *controller, QObject *parent)
DiffEditorGuiController::DiffEditorGuiController(
DiffEditorController *controller,
QObject *parent)
: QObject(parent),
m_controller(controller),
m_descriptionVisible(true),
m_contextLinesNumber(3),
m_ignoreWhitespaces(true),
m_syncScrollBars(true),
m_currentDiffFileIndex(-1)
{
connect(m_controller, SIGNAL(cleared(QString)), this, SLOT(slotUpdateDiffFileIndex()));
connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
m_descriptionVisible = s->value(QLatin1String(descriptionVisibleKeyC),
m_descriptionVisible).toBool();
m_syncScrollBars = s->value(QLatin1String(horizontalScrollBarSynchronizationKeyC),
m_syncScrollBars).toBool();
s->endGroup();
connect(m_controller, SIGNAL(cleared(QString)),
this, SLOT(slotUpdateDiffFileIndex()));
connect(m_controller, SIGNAL(diffFilesChanged(QList<FileData>,QString)),
this, SLOT(slotUpdateDiffFileIndex()));
slotUpdateDiffFileIndex();
}
......@@ -62,16 +78,6 @@ bool DiffEditorGuiController::isDescriptionVisible() const
return m_descriptionVisible;
}
int DiffEditorGuiController::contextLinesNumber() const
{
return m_contextLinesNumber;
}
bool DiffEditorGuiController::isIgnoreWhitespaces() const
{
return m_ignoreWhitespaces;
}
bool DiffEditorGuiController::horizontalScrollBarSynchronization() const
{
return m_syncScrollBars;
......@@ -84,7 +90,7 @@ int DiffEditorGuiController::currentDiffFileIndex() const
void DiffEditorGuiController::slotUpdateDiffFileIndex()
{
m_currentDiffFileIndex = (m_controller->diffContents().isEmpty() ? -1 : 0);
m_currentDiffFileIndex = (m_controller->diffFiles().isEmpty() ? -1 : 0);
}
void DiffEditorGuiController::setDescriptionVisible(bool on)
......@@ -93,26 +99,13 @@ void DiffEditorGuiController::setDescriptionVisible(bool on)
return;
m_descriptionVisible = on;
emit descriptionVisibilityChanged(on);
}
void DiffEditorGuiController::setContextLinesNumber(int lines)
{
const int l = qMax(lines, -1);
if (m_contextLinesNumber == l)
return;
m_contextLinesNumber = l;
emit contextLinesNumberChanged(l);
}
void DiffEditorGuiController::setIgnoreWhitespaces(bool ignore)
{
if (m_ignoreWhitespaces == ignore)
return;
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
s->setValue(QLatin1String(descriptionVisibleKeyC), m_descriptionVisible);
s->endGroup();
m_ignoreWhitespaces = ignore;
emit ignoreWhitespacesChanged(ignore);
emit descriptionVisibilityChanged(on);
}
void DiffEditorGuiController::setHorizontalScrollBarSynchronization(bool on)
......@@ -121,15 +114,23 @@ void DiffEditorGuiController::setHorizontalScrollBarSynchronization(bool on)
return;
m_syncScrollBars = on;
QSettings *s = Core::ICore::settings();
s->beginGroup(QLatin1String(settingsGroupC));
s->setValue(QLatin1String(horizontalScrollBarSynchronizationKeyC),
m_syncScrollBars);
s->endGroup();
emit horizontalScrollBarSynchronizationChanged(on);
}
void DiffEditorGuiController::setCurrentDiffFileIndex(int diffFileIndex)
{
if (m_controller->diffContents().isEmpty())
if (m_controller->diffFiles().isEmpty())
return; // -1 is the only valid value in this case
const int newIndex = qBound(0, diffFileIndex, m_controller->diffContents().count() - 1);
const int newIndex = qBound(0, diffFileIndex,
m_controller->diffFiles().count() - 1);
if (m_currentDiffFileIndex == newIndex)
return;
......
......@@ -42,28 +42,23 @@ class DIFFEDITOR_EXPORT DiffEditorGuiController : public QObject
{
Q_OBJECT
public:
DiffEditorGuiController(DiffEditorController *controller, QObject *parent = 0);
DiffEditorGuiController(DiffEditorController *controller,
QObject *parent = 0);
~DiffEditorGuiController();
DiffEditorController *controller() const;
bool isDescriptionVisible() const;
int contextLinesNumber() const;
bool isIgnoreWhitespaces() const;
bool horizontalScrollBarSynchronization() const;
int currentDiffFileIndex() const;
public slots:
void setDescriptionVisible(bool on);
void setContextLinesNumber(int lines);
void setIgnoreWhitespaces(bool ignore);
void setHorizontalScrollBarSynchronization(bool on);
void setCurrentDiffFileIndex(int diffFileIndex);
signals:
void descriptionVisibilityChanged(bool on);
void contextLinesNumberChanged(int lines);
void ignoreWhitespacesChanged(bool ignore);
void horizontalScrollBarSynchronizationChanged(bool on);
void currentDiffFileIndexChanged(int diffFileIndex);
......@@ -73,8 +68,6 @@ private slots:
private:
DiffEditorController *m_controller;
bool m_descriptionVisible;
int m_contextLinesNumber;
bool m_ignoreWhitespaces;
bool m_syncScrollBars;
int m_currentDiffFileIndex;
};
......
This diff is collapsed.
......@@ -53,9 +53,12 @@ public:
private slots:
void diff();
private:
QString getFileContents(const QString &fileName) const;
#ifdef WITH_TESTS
void testMakePatch_data();
void testMakePatch();
void testReadPatch_data();
void testReadPatch();
#endif // WITH_TESTS
};
} // namespace Internal
......
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).