forked from qt-creator/qt-creator
Implement unified diff editor
Change-Id: I93e0bfd71a8a650afbe2ca9e0f1f3dbfc9d57db0 Reviewed-by: Jarek Kobus <jaroslaw.kobus@digia.com>
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include "diffeditordocument.h"
|
||||
#include "diffeditorguicontroller.h"
|
||||
#include "sidebysidediffeditorwidget.h"
|
||||
#include "unifieddiffeditorwidget.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
@@ -41,6 +42,7 @@
|
||||
#include <texteditor/texteditorsettings.h>
|
||||
#include <texteditor/displaysettings.h>
|
||||
|
||||
#include <QStackedWidget>
|
||||
#include <QToolButton>
|
||||
#include <QSpinBox>
|
||||
#include <QStyle>
|
||||
@@ -49,6 +51,15 @@
|
||||
#include <QToolBar>
|
||||
#include <QComboBox>
|
||||
#include <QFileInfo>
|
||||
#include <QTextCodec>
|
||||
|
||||
static const char settingsGroupC[] = "DiffEditor";
|
||||
static const char diffEditorTypeKeyC[] = "DiffEditorType";
|
||||
static const char sideBySideDiffEditorValueC[] = "SideBySide";
|
||||
static const char unifiedDiffEditorValueC[] = "Unified";
|
||||
|
||||
static const char legacySettingsGroupC[] = "Git";
|
||||
static const char useDiffEditorKeyC[] = "UseDiffEditor";
|
||||
|
||||
using namespace TextEditor;
|
||||
|
||||
@@ -115,12 +126,16 @@ DiffEditor::DiffEditor()
|
||||
: IEditor(0)
|
||||
, m_document(new DiffEditorDocument())
|
||||
, m_descriptionWidget(0)
|
||||
, m_diffWidget(0)
|
||||
, m_stackedWidget(0)
|
||||
, m_sideBySideEditor(0)
|
||||
, m_unifiedEditor(0)
|
||||
, m_currentEditor(0)
|
||||
, m_controller(0)
|
||||
, m_guiController(0)
|
||||
, m_toolBar(0)
|
||||
, m_entriesComboBox(0)
|
||||
, m_toggleDescriptionAction(0)
|
||||
, m_diffEditorSwitcher(0)
|
||||
{
|
||||
ctor();
|
||||
}
|
||||
@@ -129,45 +144,64 @@ DiffEditor::DiffEditor(DiffEditor *other)
|
||||
: IEditor(0)
|
||||
, m_document(other->m_document)
|
||||
, m_descriptionWidget(0)
|
||||
, m_diffWidget(0)
|
||||
, m_stackedWidget(0)
|
||||
, m_sideBySideEditor(0)
|
||||
, m_unifiedEditor(0)
|
||||
, m_currentEditor(0)
|
||||
, m_controller(0)
|
||||
, m_guiController(0)
|
||||
, m_toolBar(0)
|
||||
, m_entriesComboBox(0)
|
||||
, m_toggleDescriptionAction(0)
|
||||
, m_diffEditorSwitcher(0)
|
||||
{
|
||||
ctor();
|
||||
}
|
||||
|
||||
void DiffEditor::ctor()
|
||||
{
|
||||
setDuplicateSupported(true);
|
||||
|
||||
QSplitter *splitter = new Core::MiniSplitter(Qt::Vertical);
|
||||
|
||||
m_descriptionWidget = new Internal::DescriptionEditorWidget(splitter);
|
||||
m_descriptionWidget->setReadOnly(true);
|
||||
splitter->addWidget(m_descriptionWidget);
|
||||
|
||||
m_diffWidget = new SideBySideDiffEditorWidget(splitter);
|
||||
splitter->addWidget(m_diffWidget);
|
||||
m_stackedWidget = new QStackedWidget(splitter);
|
||||
splitter->addWidget(m_stackedWidget);
|
||||
|
||||
m_sideBySideEditor = new SideBySideDiffEditorWidget(m_stackedWidget);
|
||||
m_stackedWidget->addWidget(m_sideBySideEditor);
|
||||
|
||||
m_unifiedEditor = new UnifiedDiffEditorWidget(m_stackedWidget);
|
||||
m_stackedWidget->addWidget(m_unifiedEditor);
|
||||
|
||||
setWidget(splitter);
|
||||
|
||||
connect(TextEditorSettings::instance(), SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings)),
|
||||
m_descriptionWidget, SLOT(setDisplaySettings(TextEditor::DisplaySettings)));
|
||||
connect(TextEditorSettings::instance(), SIGNAL(fontSettingsChanged(TextEditor::FontSettings)),
|
||||
m_descriptionWidget->baseTextDocument(), SLOT(setFontSettings(TextEditor::FontSettings)));
|
||||
m_descriptionWidget->setDisplaySettings(TextEditorSettings::displaySettings());
|
||||
m_descriptionWidget->setCodeStyle(TextEditorSettings::codeStyle());
|
||||
m_descriptionWidget->baseTextDocument()->setFontSettings(TextEditorSettings::fontSettings());
|
||||
connect(TextEditorSettings::instance(),
|
||||
SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings)),
|
||||
m_descriptionWidget,
|
||||
SLOT(setDisplaySettings(TextEditor::DisplaySettings)));
|
||||
connect(TextEditorSettings::instance(),
|
||||
SIGNAL(fontSettingsChanged(TextEditor::FontSettings)),
|
||||
m_descriptionWidget->baseTextDocument(),
|
||||
SLOT(setFontSettings(TextEditor::FontSettings)));
|
||||
|
||||
m_descriptionWidget->setDisplaySettings(
|
||||
TextEditorSettings::displaySettings());
|
||||
m_descriptionWidget->setCodeStyle(
|
||||
TextEditorSettings::codeStyle());
|
||||
m_descriptionWidget->baseTextDocument()->setFontSettings(
|
||||
TextEditorSettings::fontSettings());
|
||||
|
||||
m_controller = m_document->controller();
|
||||
m_guiController = new DiffEditorGuiController(m_controller, this);
|
||||
m_diffWidget->setDiffEditorGuiController(m_guiController);
|
||||
|
||||
connect(m_controller, SIGNAL(cleared(QString)),
|
||||
this, SLOT(slotCleared(QString)));
|
||||
connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),
|
||||
this, SLOT(slotDiffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)));
|
||||
connect(m_controller, SIGNAL(diffFilesChanged(QList<FileData>,QString)),
|
||||
this, SLOT(slotDiffFilesChanged(QList<FileData>,QString)));
|
||||
connect(m_controller, SIGNAL(descriptionChanged(QString)),
|
||||
this, SLOT(slotDescriptionChanged(QString)));
|
||||
connect(m_controller, SIGNAL(descriptionEnablementChanged(bool)),
|
||||
@@ -179,6 +213,10 @@ void DiffEditor::ctor()
|
||||
|
||||
slotDescriptionChanged(m_controller->description());
|
||||
slotDescriptionVisibilityChanged();
|
||||
|
||||
showDiffEditor(readCurrentDiffEditorSetting());
|
||||
|
||||
toolBar();
|
||||
}
|
||||
|
||||
DiffEditor::~DiffEditor()
|
||||
@@ -193,11 +231,36 @@ Core::IEditor *DiffEditor::duplicate()
|
||||
return new DiffEditor(this);
|
||||
}
|
||||
|
||||
bool DiffEditor::open(QString *errorString, const QString &fileName, const QString &realFileName)
|
||||
bool DiffEditor::open(QString *errorString,
|
||||
const QString &fileName,
|
||||
const QString &realFileName)
|
||||
{
|
||||
Q_UNUSED(errorString)
|
||||
Q_UNUSED(fileName)
|
||||
Q_UNUSED(realFileName)
|
||||
|
||||
if (!m_controller)
|
||||
return false;
|
||||
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
*errorString = tr("Could not open patch file \"%1\".").arg(fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString patch = Core::EditorManager::defaultTextCodec()->toUnicode(file.readAll());
|
||||
|
||||
bool ok = false;
|
||||
QList<FileData> fileDataList
|
||||
= DiffUtils::readPatch(patch,
|
||||
m_controller->isIgnoreWhitespace(),
|
||||
&ok);
|
||||
if (!ok) {
|
||||
*errorString = tr("Could not parse patch file \"%1\". "
|
||||
"The contents is not of unified diff format.")
|
||||
.arg(fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_controller->setDiffFiles(fileDataList, QFileInfo(fileName).absolutePath());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -223,7 +286,7 @@ QWidget *DiffEditor::toolBar()
|
||||
return m_toolBar;
|
||||
|
||||
// Create
|
||||
m_toolBar = createToolBar(m_diffWidget);
|
||||
m_toolBar = createToolBar(m_sideBySideEditor);
|
||||
|
||||
m_entriesComboBox = new QComboBox;
|
||||
m_entriesComboBox->setMinimumContentsLength(20);
|
||||
@@ -238,7 +301,7 @@ QWidget *DiffEditor::toolBar()
|
||||
QToolButton *whitespaceButton = new QToolButton(m_toolBar);
|
||||
whitespaceButton->setText(tr("Ignore Whitespace"));
|
||||
whitespaceButton->setCheckable(true);
|
||||
whitespaceButton->setChecked(true);
|
||||
whitespaceButton->setChecked(m_controller->isIgnoreWhitespace());
|
||||
m_toolBar->addWidget(whitespaceButton);
|
||||
|
||||
QLabel *contextLabel = new QLabel(m_toolBar);
|
||||
@@ -247,40 +310,58 @@ QWidget *DiffEditor::toolBar()
|
||||
m_toolBar->addWidget(contextLabel);
|
||||
|
||||
QSpinBox *contextSpinBox = new QSpinBox(m_toolBar);
|
||||
contextSpinBox->setRange(-1, 100);
|
||||
contextSpinBox->setValue(3);
|
||||
contextSpinBox->setRange(1, 100);
|
||||
contextSpinBox->setValue(m_controller->contextLinesNumber());
|
||||
contextSpinBox->setFrame(false);
|
||||
contextSpinBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); // Mac Qt5
|
||||
contextSpinBox->setSizePolicy(QSizePolicy::Minimum,
|
||||
QSizePolicy::Expanding); // Mac Qt5
|
||||
m_toolBar->addWidget(contextSpinBox);
|
||||
|
||||
QToolButton *toggleSync = new QToolButton(m_toolBar);
|
||||
toggleSync->setIcon(QIcon(QLatin1String(Core::Constants::ICON_LINK)));
|
||||
toggleSync->setCheckable(true);
|
||||
toggleSync->setChecked(true);
|
||||
toggleSync->setChecked(m_guiController->horizontalScrollBarSynchronization());
|
||||
toggleSync->setToolTip(tr("Synchronize Horizontal Scroll Bars"));
|
||||
m_toolBar->addWidget(toggleSync);
|
||||
|
||||
QToolButton *toggleDescription = new QToolButton(m_toolBar);
|
||||
toggleDescription->setIcon(QIcon(QLatin1String(Core::Constants::ICON_TOGGLE_TOPBAR)));
|
||||
toggleDescription->setIcon(
|
||||
QIcon(QLatin1String(Constants::ICON_TOP_BAR)));
|
||||
toggleDescription->setCheckable(true);
|
||||
toggleDescription->setChecked(true);
|
||||
toggleDescription->setChecked(m_guiController->isDescriptionVisible());
|
||||
m_toggleDescriptionAction = m_toolBar->addWidget(toggleDescription);
|
||||
slotDescriptionVisibilityChanged();
|
||||
|
||||
QToolButton *reloadButton = new QToolButton(m_toolBar);
|
||||
reloadButton->setIcon(QIcon(QLatin1String(Constants::ICON_RELOAD)));
|
||||
reloadButton->setToolTip(tr("Reload Editor"));
|
||||
m_toolBar->addWidget(reloadButton);
|
||||
|
||||
m_diffEditorSwitcher = new QToolButton(m_toolBar);
|
||||
m_toolBar->addWidget(m_diffEditorSwitcher);
|
||||
updateDiffEditorSwitcher();
|
||||
|
||||
connect(whitespaceButton, SIGNAL(clicked(bool)),
|
||||
m_guiController, SLOT(setIgnoreWhitespaces(bool)));
|
||||
m_controller, SLOT(setIgnoreWhitespace(bool)));
|
||||
connect(m_controller, SIGNAL(ignoreWhitespaceChanged(bool)),
|
||||
whitespaceButton, SLOT(setChecked(bool)));
|
||||
connect(contextSpinBox, SIGNAL(valueChanged(int)),
|
||||
m_guiController, SLOT(setContextLinesNumber(int)));
|
||||
m_controller, SLOT(setContextLinesNumber(int)));
|
||||
connect(m_controller, SIGNAL(contextLinesNumberChanged(int)),
|
||||
contextSpinBox, SLOT(setValue(int)));
|
||||
connect(toggleSync, SIGNAL(clicked(bool)),
|
||||
m_guiController, SLOT(setHorizontalScrollBarSynchronization(bool)));
|
||||
connect(toggleDescription, SIGNAL(clicked(bool)),
|
||||
m_guiController, SLOT(setDescriptionVisible(bool)));
|
||||
// TODO: synchronize in opposite direction too
|
||||
connect(m_diffEditorSwitcher, SIGNAL(clicked()),
|
||||
this, SLOT(slotDiffEditorSwitched()));
|
||||
connect(reloadButton, SIGNAL(clicked()),
|
||||
m_controller, SLOT(requestReload()));
|
||||
|
||||
return m_toolBar;
|
||||
}
|
||||
|
||||
DiffEditorController * DiffEditor::controller() const
|
||||
DiffEditorController *DiffEditor::controller() const
|
||||
{
|
||||
return m_controller;
|
||||
}
|
||||
@@ -306,16 +387,16 @@ void DiffEditor::slotCleared(const QString &message)
|
||||
updateEntryToolTip();
|
||||
}
|
||||
|
||||
void DiffEditor::slotDiffContentsChanged(const QList<DiffEditorController::DiffFilesContents> &diffFileList,
|
||||
const QString &workingDirectory)
|
||||
void DiffEditor::slotDiffFilesChanged(const QList<FileData> &diffFileList,
|
||||
const QString &workingDirectory)
|
||||
{
|
||||
Q_UNUSED(workingDirectory)
|
||||
|
||||
m_entriesComboBox->clear();
|
||||
const int count = diffFileList.count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
const DiffEditorController::DiffFileInfo leftEntry = diffFileList.at(i).leftFileInfo;
|
||||
const DiffEditorController::DiffFileInfo rightEntry = diffFileList.at(i).rightFileInfo;
|
||||
const DiffFileInfo leftEntry = diffFileList.at(i).leftFileInfo;
|
||||
const DiffFileInfo rightEntry = diffFileList.at(i).rightFileInfo;
|
||||
const QString leftShortFileName = QFileInfo(leftEntry.fileName).fileName();
|
||||
const QString rightShortFileName = QFileInfo(rightEntry.fileName).fileName();
|
||||
QString itemText;
|
||||
@@ -327,26 +408,34 @@ void DiffEditor::slotDiffContentsChanged(const QList<DiffEditorController::DiffF
|
||||
itemToolTip = leftEntry.fileName;
|
||||
} else {
|
||||
itemToolTip = tr("[%1] vs. [%2] %3")
|
||||
.arg(leftEntry.typeInfo, rightEntry.typeInfo, leftEntry.fileName);
|
||||
.arg(leftEntry.typeInfo,
|
||||
rightEntry.typeInfo,
|
||||
leftEntry.fileName);
|
||||
}
|
||||
} else {
|
||||
if (leftShortFileName == rightShortFileName) {
|
||||
itemText = leftShortFileName;
|
||||
} else {
|
||||
itemText = tr("%1 vs. %2")
|
||||
.arg(leftShortFileName, rightShortFileName);
|
||||
.arg(leftShortFileName,
|
||||
rightShortFileName);
|
||||
}
|
||||
|
||||
if (leftEntry.typeInfo.isEmpty() && rightEntry.typeInfo.isEmpty()) {
|
||||
itemToolTip = tr("%1 vs. %2")
|
||||
.arg(leftEntry.fileName, rightEntry.fileName);
|
||||
.arg(leftEntry.fileName,
|
||||
rightEntry.fileName);
|
||||
} else {
|
||||
itemToolTip = tr("[%1] %2 vs. [%3] %4")
|
||||
.arg(leftEntry.typeInfo, leftEntry.fileName, rightEntry.typeInfo, rightEntry.fileName);
|
||||
.arg(leftEntry.typeInfo,
|
||||
leftEntry.fileName,
|
||||
rightEntry.typeInfo,
|
||||
rightEntry.fileName);
|
||||
}
|
||||
}
|
||||
m_entriesComboBox->addItem(itemText);
|
||||
m_entriesComboBox->setItemData(m_entriesComboBox->count() - 1, itemToolTip, Qt::ToolTipRole);
|
||||
m_entriesComboBox->setItemData(m_entriesComboBox->count() - 1,
|
||||
itemToolTip, Qt::ToolTipRole);
|
||||
}
|
||||
updateEntryToolTip();
|
||||
}
|
||||
@@ -381,7 +470,114 @@ void DiffEditor::slotDescriptionVisibilityChanged()
|
||||
toggle->setToolTip(tr("Show Change Description"));
|
||||
|
||||
m_toggleDescriptionAction->setVisible(enabled);
|
||||
}
|
||||
|
||||
void DiffEditor::slotDiffEditorSwitched()
|
||||
{
|
||||
QWidget *oldEditor = m_currentEditor;
|
||||
QWidget *newEditor = 0;
|
||||
if (oldEditor == m_sideBySideEditor)
|
||||
newEditor = m_unifiedEditor;
|
||||
else if (oldEditor == m_unifiedEditor)
|
||||
newEditor = m_sideBySideEditor;
|
||||
else
|
||||
newEditor = readCurrentDiffEditorSetting();
|
||||
|
||||
showDiffEditor(newEditor);
|
||||
}
|
||||
|
||||
void DiffEditor::updateDiffEditorSwitcher()
|
||||
{
|
||||
if (!m_diffEditorSwitcher)
|
||||
return;
|
||||
|
||||
QIcon actionIcon;
|
||||
QString actionToolTip;
|
||||
if (m_currentEditor == m_unifiedEditor) {
|
||||
actionIcon = QIcon(QLatin1String(Constants::ICON_SIDE_BY_SIDE_DIFF));
|
||||
actionToolTip = tr("Switch to Side By Side Diff Editor");
|
||||
} else if (m_currentEditor == m_sideBySideEditor) {
|
||||
actionIcon = QIcon(QLatin1String(Constants::ICON_UNIFIED_DIFF));
|
||||
actionToolTip = tr("Switch to Unified Diff Editor");
|
||||
}
|
||||
|
||||
m_diffEditorSwitcher->setIcon(actionIcon);
|
||||
m_diffEditorSwitcher->setToolTip(actionToolTip);
|
||||
}
|
||||
|
||||
void DiffEditor::showDiffEditor(QWidget *newEditor)
|
||||
{
|
||||
if (m_currentEditor == newEditor)
|
||||
return;
|
||||
|
||||
if (m_currentEditor == m_sideBySideEditor)
|
||||
m_sideBySideEditor->setDiffEditorGuiController(0);
|
||||
else if (m_currentEditor == m_unifiedEditor)
|
||||
m_unifiedEditor->setDiffEditorGuiController(0);
|
||||
|
||||
m_currentEditor = newEditor;
|
||||
|
||||
if (m_currentEditor == m_unifiedEditor)
|
||||
m_unifiedEditor->setDiffEditorGuiController(m_guiController);
|
||||
else if (m_currentEditor == m_sideBySideEditor)
|
||||
m_sideBySideEditor->setDiffEditorGuiController(m_guiController);
|
||||
|
||||
m_stackedWidget->setCurrentWidget(m_currentEditor);
|
||||
|
||||
writeCurrentDiffEditorSetting(m_currentEditor);
|
||||
updateDiffEditorSwitcher();
|
||||
}
|
||||
|
||||
QWidget *DiffEditor::readLegacyCurrentDiffEditorSetting()
|
||||
{
|
||||
QSettings *s = Core::ICore::settings();
|
||||
|
||||
s->beginGroup(QLatin1String(legacySettingsGroupC));
|
||||
const bool legacyExists = s->contains(QLatin1String(useDiffEditorKeyC));
|
||||
const bool legacyEditor = s->value(
|
||||
QLatin1String(useDiffEditorKeyC), true).toBool();
|
||||
if (legacyExists)
|
||||
s->remove(QLatin1String(useDiffEditorKeyC));
|
||||
s->endGroup();
|
||||
|
||||
QWidget *currentEditor = m_sideBySideEditor;
|
||||
if (!legacyEditor)
|
||||
currentEditor = m_unifiedEditor;
|
||||
|
||||
if (legacyExists && currentEditor == m_unifiedEditor)
|
||||
writeCurrentDiffEditorSetting(currentEditor);
|
||||
|
||||
return currentEditor;
|
||||
}
|
||||
|
||||
QWidget *DiffEditor::readCurrentDiffEditorSetting()
|
||||
{
|
||||
// replace it with m_sideBySideEditor when dropping legacy stuff
|
||||
QWidget *defaultEditor = readLegacyCurrentDiffEditorSetting();
|
||||
|
||||
QSettings *s = Core::ICore::settings();
|
||||
s->beginGroup(QLatin1String(settingsGroupC));
|
||||
const QString editorString = s->value(
|
||||
QLatin1String(diffEditorTypeKeyC)).toString();
|
||||
s->endGroup();
|
||||
if (editorString == QLatin1String(unifiedDiffEditorValueC))
|
||||
return m_unifiedEditor;
|
||||
|
||||
if (editorString == QLatin1String(sideBySideDiffEditorValueC))
|
||||
return m_sideBySideEditor;
|
||||
|
||||
return defaultEditor;
|
||||
}
|
||||
|
||||
void DiffEditor::writeCurrentDiffEditorSetting(QWidget *currentEditor)
|
||||
{
|
||||
const QString editorString = currentEditor == m_unifiedEditor
|
||||
? QLatin1String(unifiedDiffEditorValueC)
|
||||
: QLatin1String(sideBySideDiffEditorValueC);
|
||||
QSettings *s = Core::ICore::settings();
|
||||
s->beginGroup(QLatin1String(settingsGroupC));
|
||||
s->setValue(QLatin1String(diffEditorTypeKeyC), editorString);
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
} // namespace DiffEditor
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
8
src/plugins/diffeditor/diffeditor.qrc
Normal file
8
src/plugins/diffeditor/diffeditor.qrc
Normal file
@@ -0,0 +1,8 @@
|
||||
<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,43 +99,38 @@ void DiffEditorGuiController::setDescriptionVisible(bool on)
|
||||
return;
|
||||
|
||||
m_descriptionVisible = on;
|
||||
|
||||
QSettings *s = Core::ICore::settings();
|
||||
s->beginGroup(QLatin1String(settingsGroupC));
|
||||
s->setValue(QLatin1String(descriptionVisibleKeyC), m_descriptionVisible);
|
||||
s->endGroup();
|
||||
|
||||
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;
|
||||
|
||||
m_ignoreWhitespaces = ignore;
|
||||
emit ignoreWhitespacesChanged(ignore);
|
||||
}
|
||||
|
||||
void DiffEditorGuiController::setHorizontalScrollBarSynchronization(bool on)
|
||||
{
|
||||
if (m_syncScrollBars == 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;
|
||||
};
|
||||
|
||||
@@ -30,8 +30,11 @@
|
||||
#include "diffeditorplugin.h"
|
||||
#include "diffeditor.h"
|
||||
#include "diffeditorconstants.h"
|
||||
#include "diffeditordocument.h"
|
||||
#include "diffeditorfactory.h"
|
||||
#include "diffeditormanager.h"
|
||||
#include "diffeditorreloader.h"
|
||||
#include "differ.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QTextCodec>
|
||||
@@ -46,6 +49,87 @@
|
||||
namespace DiffEditor {
|
||||
namespace Internal {
|
||||
|
||||
class SimpleDiffEditorReloader : public DiffEditorReloader
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SimpleDiffEditorReloader(QObject *parent,
|
||||
const QString &leftFileName,
|
||||
const QString &rightFileName);
|
||||
|
||||
protected:
|
||||
void reload();
|
||||
|
||||
private:
|
||||
QString getFileContents(const QString &fileName) const;
|
||||
|
||||
QString m_leftFileName;
|
||||
QString m_rightFileName;
|
||||
};
|
||||
|
||||
SimpleDiffEditorReloader::SimpleDiffEditorReloader(QObject *parent,
|
||||
const QString &leftFileName,
|
||||
const QString &rightFileName)
|
||||
: DiffEditorReloader(parent),
|
||||
m_leftFileName(leftFileName),
|
||||
m_rightFileName(rightFileName)
|
||||
{
|
||||
}
|
||||
|
||||
void SimpleDiffEditorReloader::reload()
|
||||
{
|
||||
const QString leftText = getFileContents(m_leftFileName);
|
||||
const QString rightText = getFileContents(m_rightFileName);
|
||||
|
||||
Differ differ;
|
||||
QList<Diff> diffList = differ.cleanupSemantics(
|
||||
differ.diff(leftText, rightText));
|
||||
|
||||
QList<Diff> leftDiffList;
|
||||
QList<Diff> rightDiffList;
|
||||
Differ::splitDiffList(diffList, &leftDiffList, &rightDiffList);
|
||||
QList<Diff> outputLeftDiffList;
|
||||
QList<Diff> outputRightDiffList;
|
||||
|
||||
if (diffEditorController()->isIgnoreWhitespace()) {
|
||||
const QList<Diff> leftIntermediate =
|
||||
Differ::moveWhitespaceIntoEqualities(leftDiffList);
|
||||
const QList<Diff> rightIntermediate =
|
||||
Differ::moveWhitespaceIntoEqualities(rightDiffList);
|
||||
Differ::ignoreWhitespaceBetweenEqualities(leftIntermediate,
|
||||
rightIntermediate,
|
||||
&outputLeftDiffList,
|
||||
&outputRightDiffList);
|
||||
} else {
|
||||
outputLeftDiffList = leftDiffList;
|
||||
outputRightDiffList = rightDiffList;
|
||||
}
|
||||
|
||||
const ChunkData chunkData = DiffUtils::calculateOriginalData(
|
||||
outputLeftDiffList, outputRightDiffList);
|
||||
FileData fileData = DiffUtils::calculateContextData(
|
||||
chunkData, diffEditorController()->contextLinesNumber(), 0);
|
||||
fileData.leftFileInfo.fileName = m_leftFileName;
|
||||
fileData.rightFileInfo.fileName = m_rightFileName;
|
||||
|
||||
QList<FileData> fileDataList;
|
||||
fileDataList << fileData;
|
||||
|
||||
diffEditorController()->setDiffFiles(fileDataList);
|
||||
|
||||
reloadFinished();
|
||||
}
|
||||
|
||||
QString SimpleDiffEditorReloader::getFileContents(const QString &fileName) const
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return Core::EditorManager::defaultTextCodec()->toUnicode(file.readAll());
|
||||
return QString();
|
||||
}
|
||||
|
||||
/////////////////
|
||||
|
||||
DiffEditorPlugin::DiffEditorPlugin()
|
||||
{
|
||||
}
|
||||
@@ -62,7 +146,8 @@ bool DiffEditorPlugin::initialize(const QStringList &arguments, QString *errorMe
|
||||
//register actions
|
||||
Core::ActionContainer *toolsContainer =
|
||||
Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
|
||||
toolsContainer->insertGroup(Core::Constants::G_TOOLS_OPTIONS, Constants::G_TOOLS_DIFF);
|
||||
toolsContainer->insertGroup(Core::Constants::G_TOOLS_OPTIONS,
|
||||
Constants::G_TOOLS_DIFF);
|
||||
|
||||
Core::Context globalcontext(Core::Constants::C_GLOBAL);
|
||||
|
||||
@@ -98,39 +183,396 @@ void DiffEditorPlugin::diff()
|
||||
return;
|
||||
|
||||
|
||||
const Core::Id editorId = Constants::DIFF_EDITOR_ID;
|
||||
//: Editor title
|
||||
QString title = tr("Diff \"%1\", \"%2\"").arg(fileName1).arg(fileName2);
|
||||
DiffEditor *editor = qobject_cast<DiffEditor *>
|
||||
(Core::EditorManager::openEditorWithContents(editorId, &title, QByteArray(),
|
||||
(Core::EditorManager::OpenInOtherSplit
|
||||
| Core::EditorManager::NoNewSplits)));
|
||||
if (!editor)
|
||||
return;
|
||||
const QString documentId = QLatin1String("Diff ") + fileName1
|
||||
+ QLatin1String(", ") + fileName2;
|
||||
DiffEditorDocument *document = DiffEditorManager::find(documentId);
|
||||
if (!document) {
|
||||
QString title = tr("Diff \"%1\", \"%2\"").arg(fileName1).arg(fileName2);
|
||||
document = DiffEditorManager::findOrCreate(documentId, title);
|
||||
if (!document)
|
||||
return;
|
||||
|
||||
const QString text1 = getFileContents(fileName1);
|
||||
const QString text2 = getFileContents(fileName2);
|
||||
DiffEditorController *controller = document->controller();
|
||||
SimpleDiffEditorReloader *reloader =
|
||||
new SimpleDiffEditorReloader(controller, fileName1, fileName2);
|
||||
reloader->setDiffEditorController(controller);
|
||||
}
|
||||
|
||||
DiffEditorController::DiffFilesContents dfc;
|
||||
dfc.leftFileInfo = fileName1;
|
||||
dfc.leftText = text1;
|
||||
dfc.rightFileInfo = fileName2;
|
||||
dfc.rightText = text2;
|
||||
QList<DiffEditorController::DiffFilesContents> list;
|
||||
list.append(dfc);
|
||||
Core::EditorManager::activateEditorForDocument(document);
|
||||
|
||||
editor->controller()->setDiffContents(list);
|
||||
}
|
||||
|
||||
QString DiffEditorPlugin::getFileContents(const QString &fileName) const
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return Core::EditorManager::defaultTextCodec()->toUnicode(file.readAll());
|
||||
return QString();
|
||||
document->controller()->requestReload();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace DiffEditor
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include "diffutils.h"
|
||||
|
||||
Q_DECLARE_METATYPE(DiffEditor::ChunkData)
|
||||
Q_DECLARE_METATYPE(QList<DiffEditor::FileData>)
|
||||
|
||||
void DiffEditor::Internal::DiffEditorPlugin::testMakePatch_data()
|
||||
{
|
||||
QTest::addColumn<ChunkData>("sourceChunk");
|
||||
QTest::addColumn<QString>("leftFileName");
|
||||
QTest::addColumn<QString>("rightFileName");
|
||||
QTest::addColumn<bool>("lastChunk");
|
||||
QTest::addColumn<QString>("patchText");
|
||||
|
||||
const QString fileName = QLatin1String("a.txt");
|
||||
const QString header = QLatin1String("--- ") + fileName
|
||||
+ QLatin1String("\n+++ ") + fileName + QLatin1String("\n");
|
||||
|
||||
QList<RowData> rows;
|
||||
rows << RowData(TextLineData(QLatin1String("ABCD")),
|
||||
TextLineData(TextLineData::Separator));
|
||||
rows << RowData(TextLineData(QLatin1String("EFGH")));
|
||||
rows << RowData(TextLineData(QLatin1String("")));
|
||||
ChunkData chunk;
|
||||
chunk.rows = rows;
|
||||
QString patchText = header + QLatin1String("@@ -1,2 +1,1 @@\n"
|
||||
"-ABCD\n"
|
||||
" EFGH\n");
|
||||
QTest::newRow("Simple") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< true
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
rows.clear();
|
||||
rows << RowData(TextLineData(QLatin1String("ABCD")),
|
||||
TextLineData(QLatin1String("ABCD")));
|
||||
rows << RowData(TextLineData(QLatin1String("")),
|
||||
TextLineData(TextLineData::Separator));
|
||||
chunk.rows = rows;
|
||||
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
|
||||
"-ABCD\n"
|
||||
"+ABCD\n"
|
||||
"\\ No newline at end of file\n");
|
||||
|
||||
QTest::newRow("Last newline removed") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< true
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
// chunk the same here
|
||||
patchText = header + QLatin1String("@@ -1,2 +1,1 @@\n"
|
||||
"-ABCD\n"
|
||||
"-\n"
|
||||
"+ABCD\n");
|
||||
|
||||
QTest::newRow("Not a last newline removed") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< false
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
rows.clear();
|
||||
rows << RowData(TextLineData(QLatin1String("ABCD")),
|
||||
TextLineData(QLatin1String("ABCD")));
|
||||
rows << RowData(TextLineData(TextLineData::Separator),
|
||||
TextLineData(QLatin1String("")));
|
||||
chunk.rows = rows;
|
||||
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
|
||||
"-ABCD\n"
|
||||
"\\ No newline at end of file\n"
|
||||
"+ABCD\n");
|
||||
|
||||
QTest::newRow("Last newline added") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< true
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
// chunk the same here
|
||||
patchText = header + QLatin1String("@@ -1,1 +1,2 @@\n"
|
||||
"-ABCD\n"
|
||||
"+ABCD\n"
|
||||
"+\n");
|
||||
|
||||
QTest::newRow("Not a last newline added") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< false
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
rows.clear();
|
||||
rows << RowData(TextLineData(QLatin1String("ABCD")),
|
||||
TextLineData(QLatin1String("EFGH")));
|
||||
rows << RowData(TextLineData(QLatin1String("")));
|
||||
chunk.rows = rows;
|
||||
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
|
||||
"-ABCD\n"
|
||||
"+EFGH\n");
|
||||
|
||||
QTest::newRow("Last line with a newline modified") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< true
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
// chunk the same here
|
||||
patchText = header + QLatin1String("@@ -1,2 +1,2 @@\n"
|
||||
"-ABCD\n"
|
||||
"+EFGH\n"
|
||||
" \n");
|
||||
QTest::newRow("Not a last line with a newline modified") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< false
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
rows.clear();
|
||||
rows << RowData(TextLineData(QLatin1String("ABCD")),
|
||||
TextLineData(QLatin1String("EFGH")));
|
||||
chunk.rows = rows;
|
||||
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
|
||||
"-ABCD\n"
|
||||
"\\ No newline at end of file\n"
|
||||
"+EFGH\n"
|
||||
"\\ No newline at end of file\n");
|
||||
|
||||
QTest::newRow("Last line without a newline modified") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< true
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
// chunk the same here
|
||||
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
|
||||
"-ABCD\n"
|
||||
"+EFGH\n");
|
||||
QTest::newRow("Not a last line without a newline modified") << chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< false
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
rows.clear();
|
||||
rows << RowData(TextLineData(QLatin1String("ABCD")),
|
||||
TextLineData(QLatin1String("EFGH")));
|
||||
rows << RowData(TextLineData(QLatin1String("IJKL")));
|
||||
chunk.rows = rows;
|
||||
patchText = header + QLatin1String("@@ -1,2 +1,2 @@\n"
|
||||
"-ABCD\n"
|
||||
"+EFGH\n"
|
||||
" IJKL\n"
|
||||
"\\ No newline at end of file\n");
|
||||
|
||||
QTest::newRow("Last but one line modified, last line without a newline")
|
||||
<< chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< true
|
||||
<< patchText;
|
||||
|
||||
///////////
|
||||
|
||||
// chunk the same here
|
||||
patchText = header + QLatin1String("@@ -1,2 +1,2 @@\n"
|
||||
"-ABCD\n"
|
||||
"+EFGH\n"
|
||||
" IJKL\n");
|
||||
|
||||
QTest::newRow("Last but one line modified, last line with a newline")
|
||||
<< chunk
|
||||
<< fileName
|
||||
<< fileName
|
||||
<< false
|
||||
<< patchText;
|
||||
}
|
||||
|
||||
void DiffEditor::Internal::DiffEditorPlugin::testMakePatch()
|
||||
{
|
||||
QFETCH(ChunkData, sourceChunk);
|
||||
QFETCH(QString, leftFileName);
|
||||
QFETCH(QString, rightFileName);
|
||||
QFETCH(bool, lastChunk);
|
||||
QFETCH(QString, patchText);
|
||||
|
||||
QString result = DiffUtils::makePatch(sourceChunk, leftFileName, rightFileName, lastChunk);
|
||||
|
||||
QCOMPARE(patchText, result);
|
||||
}
|
||||
|
||||
void DiffEditor::Internal::DiffEditorPlugin::testReadPatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("sourcePatch");
|
||||
QTest::addColumn<QList<FileData> >("fileDataList");
|
||||
|
||||
QString patch = QLatin1String("diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp\n"
|
||||
"index eab9e9b..082c135 100644\n"
|
||||
"--- a/src/plugins/diffeditor/diffeditor.cpp\n"
|
||||
"+++ b/src/plugins/diffeditor/diffeditor.cpp\n"
|
||||
"@@ -187,9 +187,6 @@ void DiffEditor::ctor()\n"
|
||||
" m_controller = m_document->controller();\n"
|
||||
" m_guiController = new DiffEditorGuiController(m_controller, this);\n"
|
||||
" \n"
|
||||
"-// m_sideBySideEditor->setDiffEditorGuiController(m_guiController);\n"
|
||||
"-// m_unifiedEditor->setDiffEditorGuiController(m_guiController);\n"
|
||||
"-\n"
|
||||
" connect(m_controller, SIGNAL(cleared(QString)),\n"
|
||||
" this, SLOT(slotCleared(QString)));\n"
|
||||
" connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),\n"
|
||||
"diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp\n"
|
||||
"index 2f641c9..f8ff795 100644\n"
|
||||
"--- a/src/plugins/diffeditor/diffutils.cpp\n"
|
||||
"+++ b/src/plugins/diffeditor/diffutils.cpp\n"
|
||||
"@@ -464,5 +464,12 @@ QString DiffUtils::makePatch(const ChunkData &chunkData,\n"
|
||||
" return diffText;\n"
|
||||
" }\n"
|
||||
" \n"
|
||||
"+FileData DiffUtils::makeFileData(const QString &patch)\n"
|
||||
"+{\n"
|
||||
"+ FileData fileData;\n"
|
||||
"+\n"
|
||||
"+ return fileData;\n"
|
||||
"+}\n"
|
||||
"+\n"
|
||||
" } // namespace Internal\n"
|
||||
" } // namespace DiffEditor");
|
||||
|
||||
FileData fileData1;
|
||||
fileData1.leftFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffeditor.cpp"),
|
||||
QLatin1String("eab9e9b"));
|
||||
fileData1.rightFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffeditor.cpp"),
|
||||
QLatin1String("082c135"));
|
||||
ChunkData chunkData1;
|
||||
chunkData1.leftStartingLineNumber = 187;
|
||||
chunkData1.rightStartingLineNumber = 187;
|
||||
QList<RowData> rows1;
|
||||
rows1.append(RowData(TextLineData(QLatin1String(" m_controller = m_document->controller();"))));
|
||||
rows1.append(RowData(TextLineData(QLatin1String(" m_guiController = new DiffEditorGuiController(m_controller, this);"))));
|
||||
rows1.append(RowData(TextLineData(QLatin1String(""))));
|
||||
rows1.append(RowData(TextLineData(QLatin1String("// m_sideBySideEditor->setDiffEditorGuiController(m_guiController);")),
|
||||
TextLineData(TextLineData::Separator)));
|
||||
rows1.append(RowData(TextLineData(QLatin1String("// m_unifiedEditor->setDiffEditorGuiController(m_guiController);")),
|
||||
TextLineData(TextLineData::Separator)));
|
||||
rows1.append(RowData(TextLineData(QLatin1String("")),
|
||||
TextLineData(TextLineData::Separator)));
|
||||
rows1.append(RowData(TextLineData(QLatin1String(" connect(m_controller, SIGNAL(cleared(QString)),"))));
|
||||
rows1.append(RowData(TextLineData(QLatin1String(" this, SLOT(slotCleared(QString)));"))));
|
||||
rows1.append(RowData(TextLineData(QLatin1String(" connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),"))));
|
||||
chunkData1.rows = rows1;
|
||||
fileData1.chunks.append(chunkData1);
|
||||
|
||||
FileData fileData2;
|
||||
fileData2.leftFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffutils.cpp"),
|
||||
QLatin1String("2f641c9"));
|
||||
fileData2.rightFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffutils.cpp"),
|
||||
QLatin1String("f8ff795"));
|
||||
ChunkData chunkData2;
|
||||
chunkData2.leftStartingLineNumber = 464;
|
||||
chunkData2.rightStartingLineNumber = 464;
|
||||
QList<RowData> rows2;
|
||||
rows2.append(RowData(TextLineData(QLatin1String(" return diffText;"))));
|
||||
rows2.append(RowData(TextLineData(QLatin1String("}"))));
|
||||
rows2.append(RowData(TextLineData(QLatin1String(""))));
|
||||
rows2.append(RowData(TextLineData(TextLineData::Separator),
|
||||
TextLineData(QLatin1String("FileData DiffUtils::makeFileData(const QString &patch)"))));
|
||||
rows2.append(RowData(TextLineData(TextLineData::Separator),
|
||||
TextLineData(QLatin1String("{"))));
|
||||
rows2.append(RowData(TextLineData(TextLineData::Separator),
|
||||
TextLineData(QLatin1String(" FileData fileData;"))));
|
||||
rows2.append(RowData(TextLineData(TextLineData::Separator),
|
||||
TextLineData(QLatin1String(""))));
|
||||
rows2.append(RowData(TextLineData(TextLineData::Separator),
|
||||
TextLineData(QLatin1String(" return fileData;"))));
|
||||
rows2.append(RowData(TextLineData(TextLineData::Separator),
|
||||
TextLineData(QLatin1String("}"))));
|
||||
rows2.append(RowData(TextLineData(TextLineData::Separator),
|
||||
TextLineData(QLatin1String(""))));
|
||||
rows2.append(RowData(TextLineData(QLatin1String("} // namespace Internal"))));
|
||||
rows2.append(RowData(TextLineData(QLatin1String("} // namespace DiffEditor"))));
|
||||
chunkData2.rows = rows2;
|
||||
fileData2.chunks.append(chunkData2);
|
||||
|
||||
QList<FileData> fileDataList;
|
||||
fileDataList.append(fileData1);
|
||||
fileDataList.append(fileData2);
|
||||
|
||||
QTest::newRow("Git patch") << patch
|
||||
<< fileDataList;
|
||||
}
|
||||
|
||||
void DiffEditor::Internal::DiffEditorPlugin::testReadPatch()
|
||||
{
|
||||
QFETCH(QString, sourcePatch);
|
||||
QFETCH(QList<FileData>, fileDataList);
|
||||
|
||||
bool ok;
|
||||
QList<FileData> result = DiffUtils::readPatch(sourcePatch, false, &ok);
|
||||
|
||||
QVERIFY(ok);
|
||||
QCOMPARE(fileDataList.count(), result.count());
|
||||
for (int i = 0; i < fileDataList.count(); i++) {
|
||||
const FileData &origFileData = fileDataList.at(i);
|
||||
const FileData &resultFileData = result.at(i);
|
||||
QCOMPARE(origFileData.leftFileInfo.fileName,
|
||||
resultFileData.leftFileInfo.fileName);
|
||||
QCOMPARE(origFileData.leftFileInfo.typeInfo,
|
||||
resultFileData.leftFileInfo.typeInfo);
|
||||
QCOMPARE(origFileData.rightFileInfo.fileName,
|
||||
resultFileData.rightFileInfo.fileName);
|
||||
QCOMPARE(origFileData.rightFileInfo.typeInfo,
|
||||
resultFileData.rightFileInfo.typeInfo);
|
||||
QCOMPARE(origFileData.chunks.count(),
|
||||
resultFileData.chunks.count());
|
||||
for (int j = 0; j < origFileData.chunks.count(); j++) {
|
||||
const ChunkData &origChunkData = origFileData.chunks.at(j);
|
||||
const ChunkData &resultChunkData = resultFileData.chunks.at(j);
|
||||
QCOMPARE(origChunkData.leftStartingLineNumber,
|
||||
resultChunkData.leftStartingLineNumber);
|
||||
QCOMPARE(origChunkData.rightStartingLineNumber,
|
||||
resultChunkData.rightStartingLineNumber);
|
||||
QCOMPARE(origChunkData.contextChunk,
|
||||
resultChunkData.contextChunk);
|
||||
QCOMPARE(origChunkData.rows.count(),
|
||||
resultChunkData.rows.count());
|
||||
for (int k = 0; k < origChunkData.rows.count(); k++) {
|
||||
const RowData &origRowData = origChunkData.rows.at(k);
|
||||
const RowData &resultRowData = resultChunkData.rows.at(k);
|
||||
QCOMPARE(origRowData.equal,
|
||||
resultRowData.equal);
|
||||
QCOMPARE(origRowData.leftLine.text,
|
||||
resultRowData.leftLine.text);
|
||||
QCOMPARE(origRowData.leftLine.textLineType,
|
||||
resultRowData.leftLine.textLineType);
|
||||
QCOMPARE(origRowData.rightLine.text,
|
||||
resultRowData.rightLine.text);
|
||||
QCOMPARE(origRowData.rightLine.textLineType,
|
||||
resultRowData.rightLine.textLineType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WITH_TESTS
|
||||
|
||||
|
||||
Q_EXPORT_PLUGIN(DiffEditor::Internal::DiffEditorPlugin)
|
||||
|
||||
#include "diffeditorplugin.moc"
|
||||
|
||||
@@ -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
|
||||
|
||||
98
src/plugins/diffeditor/diffeditorreloader.cpp
Normal file
98
src/plugins/diffeditor/diffeditorreloader.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 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 "diffeditorreloader.h"
|
||||
#include "diffeditorcontroller.h"
|
||||
|
||||
namespace DiffEditor {
|
||||
|
||||
DiffEditorReloader::DiffEditorReloader(QObject *parent)
|
||||
: QObject(parent),
|
||||
m_controller(0),
|
||||
m_reloading(false)
|
||||
{
|
||||
}
|
||||
|
||||
DiffEditorReloader::~DiffEditorReloader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DiffEditorController *DiffEditorReloader::diffEditorController() const
|
||||
{
|
||||
return m_controller;
|
||||
}
|
||||
|
||||
void DiffEditorReloader::setDiffEditorController(DiffEditorController *controller)
|
||||
{
|
||||
if (m_controller) {
|
||||
disconnect(m_controller, SIGNAL(ignoreWhitespaceChanged(bool)),
|
||||
this, SLOT(requestReload()));
|
||||
disconnect(m_controller, SIGNAL(contextLinesNumberChanged(int)),
|
||||
this, SLOT(requestReload()));
|
||||
disconnect(m_controller, SIGNAL(reloadRequested()),
|
||||
this, SLOT(requestReload()));
|
||||
}
|
||||
|
||||
m_controller = controller;
|
||||
|
||||
if (m_controller) {
|
||||
connect(m_controller, SIGNAL(ignoreWhitespaceChanged(bool)),
|
||||
this, SLOT(requestReload()));
|
||||
connect(m_controller, SIGNAL(contextLinesNumberChanged(int)),
|
||||
this, SLOT(requestReload()));
|
||||
connect(m_controller, SIGNAL(reloadRequested()),
|
||||
this, SLOT(requestReload()));
|
||||
}
|
||||
}
|
||||
|
||||
void DiffEditorReloader::requestReload()
|
||||
{
|
||||
if (m_reloading)
|
||||
return;
|
||||
|
||||
if (!m_controller)
|
||||
return;
|
||||
|
||||
m_reloading = true;
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
bool DiffEditorReloader::isReloading() const
|
||||
{
|
||||
return m_reloading;
|
||||
}
|
||||
|
||||
void DiffEditorReloader::reloadFinished()
|
||||
{
|
||||
m_reloading = false;
|
||||
}
|
||||
|
||||
} // namespace DiffEditor
|
||||
72
src/plugins/diffeditor/diffeditorreloader.h
Normal file
72
src/plugins/diffeditor/diffeditorreloader.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef DIFFEDITORRELOADER_H
|
||||
#define DIFFEDITORRELOADER_H
|
||||
|
||||
#include "diffeditor_global.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace DiffEditor {
|
||||
|
||||
class DiffEditorController;
|
||||
|
||||
class DIFFEDITOR_EXPORT DiffEditorReloader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiffEditorReloader(QObject *parent = 0);
|
||||
~DiffEditorReloader();
|
||||
|
||||
DiffEditorController *diffEditorController() const;
|
||||
void setDiffEditorController(DiffEditorController *controller);
|
||||
|
||||
bool isReloading() const;
|
||||
|
||||
protected:
|
||||
// reloadFinished() should be called
|
||||
// inside reload() (for synchronous reload)
|
||||
// or later (for asynchronous reload)
|
||||
virtual void reload() = 0;
|
||||
|
||||
protected slots:
|
||||
void reloadFinished();
|
||||
|
||||
private slots:
|
||||
void requestReload();
|
||||
|
||||
private:
|
||||
DiffEditorController *m_controller;
|
||||
bool m_reloading;
|
||||
};
|
||||
|
||||
} // namespace DiffEditor
|
||||
|
||||
#endif // DIFFEDITORRELOADER_H
|
||||
@@ -297,14 +297,16 @@ QList<Diff> Differ::moveWhitespaceIntoEqualities(const QList<Diff> &input)
|
||||
const int previousDiffCount = previousDiff.text.count();
|
||||
if (previousDiff.command == Diff::Equal
|
||||
&& previousDiffCount
|
||||
&& isWhitespace(previousDiff.text.at(previousDiffCount - 1))) { // previous diff ends with whitespace
|
||||
&& isWhitespace(previousDiff.text.at(previousDiffCount - 1))) {
|
||||
// previous diff ends with whitespace
|
||||
int j = 0;
|
||||
while (j < diff.text.count()) {
|
||||
if (!isWhitespace(diff.text.at(j)))
|
||||
break;
|
||||
++j;
|
||||
}
|
||||
if (j > 0) { // diff starts with j whitespaces, move them to the previous diff
|
||||
if (j > 0) {
|
||||
// diff starts with j whitespaces, move them to the previous diff
|
||||
previousDiff.text.append(diff.text.left(j));
|
||||
diff.text = diff.text.mid(j);
|
||||
}
|
||||
@@ -316,14 +318,16 @@ QList<Diff> Differ::moveWhitespaceIntoEqualities(const QList<Diff> &input)
|
||||
const int nextDiffCount = nextDiff.text.count();
|
||||
if (nextDiff.command == Diff::Equal
|
||||
&& nextDiffCount
|
||||
&& (isWhitespace(nextDiff.text.at(0)) || isNewLine(nextDiff.text.at(0)))) { // next diff starts with whitespace or with newline
|
||||
&& (isWhitespace(nextDiff.text.at(0)) || isNewLine(nextDiff.text.at(0)))) {
|
||||
// next diff starts with whitespace or with newline
|
||||
int j = 0;
|
||||
while (j < diffCount) {
|
||||
if (!isWhitespace(diff.text.at(diffCount - j - 1)))
|
||||
break;
|
||||
++j;
|
||||
}
|
||||
if (j > 0) { // diff ends with j whitespaces, move them to the next diff
|
||||
if (j > 0) {
|
||||
// diff ends with j whitespaces, move them to the next diff
|
||||
nextDiff.text.prepend(diff.text.mid(diffCount - j));
|
||||
diff.text = diff.text.left(diffCount - j);
|
||||
}
|
||||
@@ -559,15 +563,18 @@ static QString encodeExpandedWhitespace(const QString &leftEquality,
|
||||
|
||||
if ((leftWhitespaces.count() && !rightWhitespaces.count())
|
||||
|| (!leftWhitespaces.count() && rightWhitespaces.count())) {
|
||||
return QString(); // there must be at least 1 corresponding whitespace, equalities broken
|
||||
// there must be at least 1 corresponding whitespace, equalities broken
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (leftWhitespaces.count() && rightWhitespaces.count()) {
|
||||
const int replacementPosition = output.count();
|
||||
const int replacementSize = qMax(leftWhitespaces.count(), rightWhitespaces.count());
|
||||
const QString replacement(replacementSize, QLatin1Char(' '));
|
||||
leftCodeMap->insert(replacementPosition, qMakePair(replacementSize, leftWhitespaces));
|
||||
rightCodeMap->insert(replacementPosition, qMakePair(replacementSize, rightWhitespaces));
|
||||
leftCodeMap->insert(replacementPosition,
|
||||
qMakePair(replacementSize, leftWhitespaces));
|
||||
rightCodeMap->insert(replacementPosition,
|
||||
qMakePair(replacementSize, rightWhitespaces));
|
||||
output.append(replacement);
|
||||
}
|
||||
|
||||
@@ -612,7 +619,8 @@ static QList<Diff> decodeExpandedWhitespace(const QList<Diff> &input,
|
||||
return QList<Diff>(); // replacement exceeds one Diff
|
||||
const QString replacement = it.value().second;
|
||||
const int updatedDiffCount = diff.text.count();
|
||||
diff.text.replace(updatedDiffCount - reversePosition, replacementSize, replacement);
|
||||
diff.text.replace(updatedDiffCount - reversePosition,
|
||||
replacementSize, replacement);
|
||||
++it;
|
||||
}
|
||||
output.append(diff);
|
||||
@@ -680,12 +688,14 @@ static bool diffWithWhitespaceExpandedInEqualities(const QList<Diff> &leftInput,
|
||||
QMapIterator<int, QPair<int, QString> > itLeft(leftCodeMap);
|
||||
while (itLeft.hasNext()) {
|
||||
itLeft.next();
|
||||
commonLeftCodeMap.insert(leftText.count() + itLeft.key(), itLeft.value());
|
||||
commonLeftCodeMap.insert(leftText.count() + itLeft.key(),
|
||||
itLeft.value());
|
||||
}
|
||||
QMapIterator<int, QPair<int, QString> > itRight(rightCodeMap);
|
||||
while (itRight.hasNext()) {
|
||||
itRight.next();
|
||||
commonRightCodeMap.insert(rightText.count() + itRight.key(), itRight.value());
|
||||
commonRightCodeMap.insert(rightText.count() + itRight.key(),
|
||||
itRight.value());
|
||||
}
|
||||
|
||||
leftText.append(commonEquality);
|
||||
@@ -706,7 +716,8 @@ static bool diffWithWhitespaceExpandedInEqualities(const QList<Diff> &leftInput,
|
||||
}
|
||||
|
||||
Differ differ;
|
||||
QList<Diff> diffList = differ.cleanupSemantics(differ.diff(leftText, rightText));
|
||||
QList<Diff> diffList = differ.cleanupSemantics(
|
||||
differ.diff(leftText, rightText));
|
||||
|
||||
QList<Diff> leftDiffList;
|
||||
QList<Diff> rightDiffList;
|
||||
@@ -716,10 +727,12 @@ static bool diffWithWhitespaceExpandedInEqualities(const QList<Diff> &leftInput,
|
||||
rightDiffList = Differ::moveWhitespaceIntoEqualities(rightDiffList);
|
||||
|
||||
bool ok = false;
|
||||
*leftOutput = decodeExpandedWhitespace(leftDiffList, commonLeftCodeMap, &ok);
|
||||
*leftOutput = decodeExpandedWhitespace(leftDiffList,
|
||||
commonLeftCodeMap, &ok);
|
||||
if (!ok)
|
||||
return false;
|
||||
*rightOutput = decodeExpandedWhitespace(rightDiffList, commonRightCodeMap, &ok);
|
||||
*rightOutput = decodeExpandedWhitespace(rightDiffList,
|
||||
commonRightCodeMap, &ok);
|
||||
if (!ok)
|
||||
return false;
|
||||
return true;
|
||||
@@ -755,11 +768,13 @@ static void appendWithEqualitiesSquashed(const QList<Diff> &leftInput,
|
||||
* Deletions and insertions need to be merged.
|
||||
*
|
||||
* For each corresponding insertion / deletion pair:
|
||||
* - diffWithWhitespaceReduced(): rediff them separately with whitespace reduced (new equalities may appear)
|
||||
* - diffWithWhitespaceReduced(): rediff them separately with whitespace reduced
|
||||
* (new equalities may appear)
|
||||
* - moveWhitespaceIntoEqualities(): move whitespace into new equalities
|
||||
* - diffWithWhitespaceExpandedInEqualities(): expand whitespace inside new equalities only and rediff with cleanup
|
||||
* - diffWithWhitespaceExpandedInEqualities(): expand whitespace inside new
|
||||
* equalities only and rediff with cleanup
|
||||
*/
|
||||
void Differ::diffBetweenEqualities(const QList<Diff> &leftInput,
|
||||
void Differ::ignoreWhitespaceBetweenEqualities(const QList<Diff> &leftInput,
|
||||
const QList<Diff> &rightInput,
|
||||
QList<Diff> *leftOutput,
|
||||
QList<Diff> *rightOutput)
|
||||
@@ -848,6 +863,88 @@ void Differ::diffBetweenEqualities(const QList<Diff> &leftInput,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Prerequisites:
|
||||
* leftInput cannot contain insertions, while right input cannot contain deletions.
|
||||
* The number of equalities on leftInput and rightInput lists should be the same.
|
||||
* Deletions and insertions need to be merged.
|
||||
*
|
||||
* For each corresponding insertion / deletion pair do just diff and merge equalities
|
||||
*/
|
||||
void Differ::diffBetweenEqualities(const QList<Diff> &leftInput,
|
||||
const QList<Diff> &rightInput,
|
||||
QList<Diff> *leftOutput,
|
||||
QList<Diff> *rightOutput)
|
||||
{
|
||||
if (!leftOutput || !rightOutput)
|
||||
return;
|
||||
|
||||
leftOutput->clear();
|
||||
rightOutput->clear();
|
||||
|
||||
const int leftCount = leftInput.count();
|
||||
const int rightCount = rightInput.count();
|
||||
int l = 0;
|
||||
int r = 0;
|
||||
|
||||
while (l <= leftCount && r <= rightCount) {
|
||||
Diff leftDiff = l < leftCount
|
||||
? leftInput.at(l)
|
||||
: Diff(Diff::Equal);
|
||||
Diff rightDiff = r < rightCount
|
||||
? rightInput.at(r)
|
||||
: Diff(Diff::Equal);
|
||||
|
||||
if (leftDiff.command == Diff::Equal && rightDiff.command == Diff::Equal) {
|
||||
Diff previousLeftDiff = l > 0 ? leftInput.at(l - 1) : Diff(Diff::Equal);
|
||||
Diff previousRightDiff = r > 0 ? rightInput.at(r - 1) : Diff(Diff::Equal);
|
||||
|
||||
if (previousLeftDiff.command == Diff::Delete
|
||||
&& previousRightDiff.command == Diff::Insert) {
|
||||
Differ differ;
|
||||
differ.setDiffMode(Differ::CharMode);
|
||||
QList<Diff> commonOutput = differ.cleanupSemantics(
|
||||
differ.diff(previousLeftDiff.text, previousRightDiff.text));
|
||||
|
||||
QList<Diff> outputLeftDiffList;
|
||||
QList<Diff> outputRightDiffList;
|
||||
|
||||
Differ::splitDiffList(commonOutput, &outputLeftDiffList,
|
||||
&outputRightDiffList);
|
||||
|
||||
appendWithEqualitiesSquashed(outputLeftDiffList,
|
||||
outputRightDiffList,
|
||||
leftOutput,
|
||||
rightOutput);
|
||||
} else if (previousLeftDiff.command == Diff::Delete) {
|
||||
leftOutput->append(previousLeftDiff);
|
||||
} else if (previousRightDiff.command == Diff::Insert) {
|
||||
rightOutput->append(previousRightDiff);
|
||||
}
|
||||
|
||||
QList<Diff> leftEquality;
|
||||
QList<Diff> rightEquality;
|
||||
if (l < leftCount)
|
||||
leftEquality.append(leftDiff);
|
||||
if (r < rightCount)
|
||||
rightEquality.append(rightDiff);
|
||||
|
||||
appendWithEqualitiesSquashed(leftEquality,
|
||||
rightEquality,
|
||||
leftOutput,
|
||||
rightOutput);
|
||||
|
||||
++l;
|
||||
++r;
|
||||
}
|
||||
|
||||
if (leftDiff.command != Diff::Equal)
|
||||
++l;
|
||||
if (rightDiff.command != Diff::Equal)
|
||||
++r;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////
|
||||
|
||||
|
||||
@@ -989,7 +1086,8 @@ QList<Diff> Differ::preprocess2AndDiff(const QString &text1, const QString &text
|
||||
const QString shorttext = text1.count() > text2.count() ? text2 : text1;
|
||||
const int i = longtext.indexOf(shorttext);
|
||||
if (i != -1) {
|
||||
const Diff::Command command = (text1.count() > text2.count()) ? Diff::Delete : Diff::Insert;
|
||||
const Diff::Command command = (text1.count() > text2.count())
|
||||
? Diff::Delete : Diff::Insert;
|
||||
diffList.append(Diff(command, longtext.left(i)));
|
||||
diffList.append(Diff(Diff::Equal, shorttext));
|
||||
diffList.append(Diff(command, longtext.mid(i + shorttext.count())));
|
||||
@@ -1146,7 +1244,8 @@ QList<Diff> Differ::diffNonCharMode(const QString &text1, const QString &text2)
|
||||
for (int i = 0; i <= diffList.count(); i++) {
|
||||
const Diff diffItem = i < diffList.count()
|
||||
? diffList.at(i)
|
||||
: Diff(Diff::Equal); // dummy, ensure we process to the end even when diffList doesn't end with equality
|
||||
: Diff(Diff::Equal); // dummy, ensure we process to the end
|
||||
// even when diffList doesn't end with equality
|
||||
if (diffItem.command == Diff::Delete) {
|
||||
lastDelete += diffItem.text;
|
||||
} else if (diffItem.command == Diff::Insert) {
|
||||
@@ -1235,7 +1334,8 @@ QList<Diff> Differ::merge(const QList<Diff> &diffList)
|
||||
for (int i = 0; i <= diffList.count(); i++) {
|
||||
Diff diff = i < diffList.count()
|
||||
? diffList.at(i)
|
||||
: Diff(Diff::Equal); // dummy, ensure we process to the end even when diffList doesn't end with equality
|
||||
: Diff(Diff::Equal); // dummy, ensure we process to the end
|
||||
// even when diffList doesn't end with equality
|
||||
if (diff.command == Diff::Delete) {
|
||||
lastDelete += diff.text;
|
||||
} else if (diff.command == Diff::Insert) {
|
||||
@@ -1315,7 +1415,8 @@ QList<Diff> Differ::cleanupSemantics(const QList<Diff> &diffList)
|
||||
for (int i = 0; i <= diffList.count(); i++) {
|
||||
Diff diff = i < diffList.count()
|
||||
? diffList.at(i)
|
||||
: Diff(Diff::Equal); // dummy, ensure we process to the end even when diffList doesn't end with equality
|
||||
: Diff(Diff::Equal); // dummy, ensure we process to the end
|
||||
// even when diffList doesn't end with equality
|
||||
if (diff.command == Diff::Equal) {
|
||||
if (!equalities.isEmpty()) {
|
||||
EqualityData &previousData = equalities.last();
|
||||
|
||||
@@ -88,6 +88,10 @@ public:
|
||||
const QString &rightInput,
|
||||
QList<Diff> *leftOutput,
|
||||
QList<Diff> *rightOutput);
|
||||
static void ignoreWhitespaceBetweenEqualities(const QList<Diff> &leftInput,
|
||||
const QList<Diff> &rightInput,
|
||||
QList<Diff> *leftOutput,
|
||||
QList<Diff> *rightOutput);
|
||||
static void diffBetweenEqualities(const QList<Diff> &leftInput,
|
||||
const QList<Diff> &rightInput,
|
||||
QList<Diff> *leftOutput,
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "texteditor/fontsettings.h"
|
||||
|
||||
namespace DiffEditor {
|
||||
namespace Internal {
|
||||
|
||||
static QList<TextLineData> assemblyRows(const QList<TextLineData> &lines,
|
||||
const QMap<int, int> &lineSpans)
|
||||
@@ -102,11 +101,9 @@ static void handleDifference(const QString &text,
|
||||
* while rightDiffList can contain only insertions and equalities.
|
||||
* The number of equalities on both lists must be the same.
|
||||
*/
|
||||
ChunkData calculateOriginalData(const QList<Diff> &leftDiffList,
|
||||
ChunkData DiffUtils::calculateOriginalData(const QList<Diff> &leftDiffList,
|
||||
const QList<Diff> &rightDiffList)
|
||||
{
|
||||
ChunkData chunkData;
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
@@ -152,61 +149,71 @@ ChunkData calculateOriginalData(const QList<Diff> &leftDiffList,
|
||||
|
||||
int line = 0;
|
||||
|
||||
while (line < qMax(newLeftLines.count(), newRightLines.count())) {
|
||||
handleLine(newLeftLines, line, &leftLines, &leftLineNumber);
|
||||
handleLine(newRightLines, line, &rightLines, &rightLineNumber);
|
||||
if (i < leftDiffList.count() || j < rightDiffList.count() || (leftLines.count() && rightLines.count())) {
|
||||
while (line < qMax(newLeftLines.count(), newRightLines.count())) {
|
||||
handleLine(newLeftLines, line, &leftLines, &leftLineNumber);
|
||||
handleLine(newRightLines, line, &rightLines, &rightLineNumber);
|
||||
|
||||
const int commonLineCount = qMin(newLeftLines.count(), newRightLines.count());
|
||||
if (line < commonLineCount) {
|
||||
// try to align
|
||||
const int leftDifference = leftLineNumber - leftLineAligned;
|
||||
const int rightDifference = rightLineNumber - rightLineAligned;
|
||||
const int commonLineCount = qMin(newLeftLines.count(),
|
||||
newRightLines.count());
|
||||
if (line < commonLineCount) {
|
||||
// try to align
|
||||
const int leftDifference = leftLineNumber - leftLineAligned;
|
||||
const int rightDifference = rightLineNumber - rightLineAligned;
|
||||
|
||||
if (leftDifference && rightDifference) {
|
||||
bool doAlign = true;
|
||||
if (line == 0 // omit alignment when first lines of equalities are empty and last generated lines are not equal
|
||||
&& (newLeftLines.at(0).isEmpty() || newRightLines.at(0).isEmpty())
|
||||
&& !lastLineEqual) {
|
||||
doAlign = false;
|
||||
}
|
||||
|
||||
if (line == commonLineCount - 1) {
|
||||
// omit alignment when last lines of equalities are empty
|
||||
if (leftLines.last().text.isEmpty() || rightLines.last().text.isEmpty())
|
||||
if (leftDifference && rightDifference) {
|
||||
bool doAlign = true;
|
||||
if (line == 0
|
||||
&& (newLeftLines.at(0).isEmpty()
|
||||
|| newRightLines.at(0).isEmpty())
|
||||
&& !lastLineEqual) {
|
||||
// omit alignment when first lines of equalities
|
||||
// are empty and last generated lines are not equal
|
||||
doAlign = false;
|
||||
}
|
||||
|
||||
// unless it's the last dummy line (don't omit in that case)
|
||||
if (i == leftDiffList.count() && j == rightDiffList.count())
|
||||
doAlign = true;
|
||||
}
|
||||
if (line == commonLineCount - 1) {
|
||||
// omit alignment when last lines of equalities are empty
|
||||
if (leftLines.last().text.isEmpty()
|
||||
|| rightLines.last().text.isEmpty())
|
||||
doAlign = false;
|
||||
|
||||
if (doAlign) {
|
||||
// align here
|
||||
leftLineAligned = leftLineNumber;
|
||||
rightLineAligned = rightLineNumber;
|
||||
// unless it's the last dummy line (don't omit in that case)
|
||||
if (i == leftDiffList.count()
|
||||
&& j == rightDiffList.count())
|
||||
doAlign = true;
|
||||
}
|
||||
|
||||
// insert separators if needed
|
||||
if (rightDifference > leftDifference)
|
||||
leftSpans.insert(leftLineNumber, rightDifference - leftDifference);
|
||||
else if (leftDifference > rightDifference)
|
||||
rightSpans.insert(rightLineNumber, leftDifference - rightDifference);
|
||||
if (doAlign) {
|
||||
// align here
|
||||
leftLineAligned = leftLineNumber;
|
||||
rightLineAligned = rightLineNumber;
|
||||
|
||||
// insert separators if needed
|
||||
if (rightDifference > leftDifference)
|
||||
leftSpans.insert(leftLineNumber,
|
||||
rightDifference - leftDifference);
|
||||
else if (leftDifference > rightDifference)
|
||||
rightSpans.insert(rightLineNumber,
|
||||
leftDifference - rightDifference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if lines are equal
|
||||
if ((line < commonLineCount - 1) // before the last common line in equality
|
||||
|| (line == commonLineCount - 1 // or the last common line in equality
|
||||
&& i == leftDiffList.count() // and it's the last iteration
|
||||
&& j == rightDiffList.count())) {
|
||||
if (line > 0 || lastLineEqual)
|
||||
equalLines.insert(leftLineNumber, rightLineNumber);
|
||||
}
|
||||
|
||||
if (line > 0)
|
||||
lastLineEqual = true;
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
// check if lines are equal
|
||||
if ((line < commonLineCount - 1) // before the last common line in equality
|
||||
|| (line == commonLineCount - 1 // or the last common line in equality
|
||||
&& i == leftDiffList.count() // and it's the last iteration
|
||||
&& j == rightDiffList.count())) {
|
||||
if (line > 0 || lastLineEqual)
|
||||
equalLines.insert(leftLineNumber, rightLineNumber);
|
||||
}
|
||||
|
||||
if (line > 0)
|
||||
lastLineEqual = true;
|
||||
|
||||
line++;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
@@ -227,6 +234,8 @@ ChunkData calculateOriginalData(const QList<Diff> &leftDiffList,
|
||||
const int visualLineCount = leftData.count();
|
||||
int leftLine = -1;
|
||||
int rightLine = -1;
|
||||
ChunkData chunkData;
|
||||
|
||||
for (int i = 0; i < visualLineCount; i++) {
|
||||
const TextLineData &leftTextLine = leftData.at(i);
|
||||
const TextLineData &rightTextLine = rightData.at(i);
|
||||
@@ -244,14 +253,16 @@ ChunkData calculateOriginalData(const QList<Diff> &leftDiffList,
|
||||
return chunkData;
|
||||
}
|
||||
|
||||
FileData calculateContextData(const ChunkData &originalData, int contextLinesNumber)
|
||||
FileData DiffUtils::calculateContextData(const ChunkData &originalData,
|
||||
int contextLinesNumber,
|
||||
int joinChunkThreshold)
|
||||
{
|
||||
if (contextLinesNumber < 0)
|
||||
return FileData(originalData);
|
||||
|
||||
const int joinChunkThreshold = 1;
|
||||
|
||||
FileData fileData;
|
||||
fileData.contextChunksIncluded = true;
|
||||
|
||||
QMap<int, bool> hiddenRows;
|
||||
int i = 0;
|
||||
while (i < originalData.rows.count()) {
|
||||
@@ -269,8 +280,10 @@ FileData calculateContextData(const ChunkData &originalData, int contextLinesNum
|
||||
const bool first = equalRowStart == 0; // includes first line?
|
||||
const bool last = i == originalData.rows.count(); // includes last line?
|
||||
|
||||
const int firstLine = first ? 0 : equalRowStart + contextLinesNumber;
|
||||
const int lastLine = last ? originalData.rows.count() : i - contextLinesNumber;
|
||||
const int firstLine = first
|
||||
? 0 : equalRowStart + contextLinesNumber;
|
||||
const int lastLine = last
|
||||
? originalData.rows.count() : i - contextLinesNumber;
|
||||
|
||||
if (firstLine < lastLine - joinChunkThreshold) {
|
||||
for (int j = firstLine; j < lastLine; j++) {
|
||||
@@ -283,15 +296,23 @@ FileData calculateContextData(const ChunkData &originalData, int contextLinesNum
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
int leftLineNumber = 0;
|
||||
int rightLineNumber = 0;
|
||||
while (i < originalData.rows.count()) {
|
||||
const bool contextChunk = hiddenRows.contains(i);
|
||||
ChunkData chunkData;
|
||||
chunkData.contextChunk = contextChunk;
|
||||
chunkData.leftStartingLineNumber = leftLineNumber;
|
||||
chunkData.rightStartingLineNumber = rightLineNumber;
|
||||
while (i < originalData.rows.count()) {
|
||||
if (contextChunk != hiddenRows.contains(i))
|
||||
break;
|
||||
RowData rowData = originalData.rows.at(i);
|
||||
chunkData.rows.append(rowData);
|
||||
if (rowData.leftLine.textLineType == TextLineData::TextLine)
|
||||
++leftLineNumber;
|
||||
if (rowData.rightLine.textLineType == TextLineData::TextLine)
|
||||
++rightLineNumber;
|
||||
++i;
|
||||
}
|
||||
fileData.chunks.append(chunkData);
|
||||
@@ -300,54 +321,630 @@ FileData calculateContextData(const ChunkData &originalData, int contextLinesNum
|
||||
return fileData;
|
||||
}
|
||||
|
||||
void addChangedPositions(int positionOffset, const QMap<int, int> &originalChangedPositions, QMap<int, int> *changedPositions)
|
||||
QString DiffUtils::makePatchLine(const QChar &startLineCharacter,
|
||||
const QString &textLine,
|
||||
bool lastChunk,
|
||||
bool lastLine)
|
||||
{
|
||||
QMapIterator<int, int> it(originalChangedPositions);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
const int startPos = it.key();
|
||||
const int endPos = it.value();
|
||||
const int newStartPos = startPos < 0 ? -1 : startPos + positionOffset;
|
||||
const int newEndPos = endPos < 0 ? -1 : endPos + positionOffset;
|
||||
if (startPos < 0 && !changedPositions->isEmpty()) {
|
||||
QMap<int, int>::iterator last = changedPositions->end();
|
||||
--last;
|
||||
last.value() = newEndPos;
|
||||
} else
|
||||
changedPositions->insert(newStartPos, newEndPos);
|
||||
QString line;
|
||||
|
||||
const bool addNoNewline = lastChunk // it's the last chunk in file
|
||||
&& lastLine // it's the last row in chunk
|
||||
&& !textLine.isEmpty(); // the row is not empty
|
||||
|
||||
const bool addLine = !lastChunk // not the last chunk in file
|
||||
|| !lastLine // not the last row in chunk
|
||||
|| addNoNewline; // no addNoNewline case
|
||||
|
||||
if (addLine) {
|
||||
line = startLineCharacter + textLine + QLatin1Char('\n');
|
||||
if (addNoNewline)
|
||||
line += QLatin1String("\\ No newline at end of file\n");
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
QList<QTextEdit::ExtraSelection> colorPositions(
|
||||
const QTextCharFormat &format,
|
||||
QTextCursor &cursor,
|
||||
const QMap<int, int> &positions)
|
||||
QString DiffUtils::makePatch(const ChunkData &chunkData,
|
||||
const QString &leftFileName,
|
||||
const QString &rightFileName,
|
||||
bool lastChunk)
|
||||
{
|
||||
QList<QTextEdit::ExtraSelection> lineSelections;
|
||||
QString diffText;
|
||||
int leftLineCount = 0;
|
||||
int rightLineCount = 0;
|
||||
QList<TextLineData> leftBuffer, rightBuffer;
|
||||
|
||||
cursor.setPosition(0);
|
||||
QMapIterator<int, int> itPositions(positions);
|
||||
while (itPositions.hasNext()) {
|
||||
itPositions.next();
|
||||
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
|
||||
if (rowData.equal) {
|
||||
if (leftBuffer.count()) {
|
||||
for (int j = 0; j < leftBuffer.count(); j++) {
|
||||
const QString line = makePatchLine(QLatin1Char('-'),
|
||||
leftBuffer.at(j).text,
|
||||
lastChunk,
|
||||
i == chunkData.rows.count()
|
||||
&& j == leftBuffer.count() - 1);
|
||||
|
||||
cursor.setPosition(itPositions.key());
|
||||
cursor.setPosition(itPositions.value(), QTextCursor::KeepAnchor);
|
||||
if (!line.isEmpty())
|
||||
++leftLineCount;
|
||||
|
||||
QTextEdit::ExtraSelection selection;
|
||||
selection.cursor = cursor;
|
||||
selection.format = format;
|
||||
lineSelections.append(selection);
|
||||
diffText += line;
|
||||
}
|
||||
leftBuffer.clear();
|
||||
}
|
||||
if (rightBuffer.count()) {
|
||||
for (int j = 0; j < rightBuffer.count(); j++) {
|
||||
const QString line = makePatchLine(QLatin1Char('+'),
|
||||
rightBuffer.at(j).text,
|
||||
lastChunk,
|
||||
i == chunkData.rows.count()
|
||||
&& j == rightBuffer.count() - 1);
|
||||
|
||||
if (!line.isEmpty())
|
||||
++rightLineCount;
|
||||
|
||||
diffText += line;
|
||||
}
|
||||
rightBuffer.clear();
|
||||
}
|
||||
if (i < chunkData.rows.count()) {
|
||||
const QString line = makePatchLine(QLatin1Char(' '),
|
||||
rowData.rightLine.text,
|
||||
lastChunk,
|
||||
i == chunkData.rows.count() - 1);
|
||||
|
||||
if (!line.isEmpty()) {
|
||||
++leftLineCount;
|
||||
++rightLineCount;
|
||||
}
|
||||
|
||||
diffText += line;
|
||||
}
|
||||
} else {
|
||||
if (rowData.leftLine.textLineType == TextLineData::TextLine)
|
||||
leftBuffer.append(rowData.leftLine);
|
||||
if (rowData.rightLine.textLineType == TextLineData::TextLine)
|
||||
rightBuffer.append(rowData.rightLine);
|
||||
}
|
||||
}
|
||||
return lineSelections;
|
||||
|
||||
const QString chunkLine = QLatin1String("@@ -")
|
||||
+ QString::number(chunkData.leftStartingLineNumber + 1)
|
||||
+ QLatin1Char(',')
|
||||
+ QString::number(leftLineCount)
|
||||
+ QLatin1String(" +")
|
||||
+ QString::number(chunkData.rightStartingLineNumber + 1)
|
||||
+ QLatin1Char(',')
|
||||
+ QString::number(rightLineCount)
|
||||
+ QLatin1String(" @@\n");
|
||||
|
||||
diffText.prepend(chunkLine);
|
||||
|
||||
const QString rightFileInfo = QLatin1String("+++ ") + rightFileName + QLatin1Char('\n');
|
||||
const QString leftFileInfo = QLatin1String("--- ") + leftFileName + QLatin1Char('\n');
|
||||
|
||||
diffText.prepend(rightFileInfo);
|
||||
diffText.prepend(leftFileInfo);
|
||||
|
||||
return diffText;
|
||||
}
|
||||
|
||||
QTextCharFormat fullWidthFormatForTextStyle(const TextEditor::FontSettings &fontSettings,
|
||||
TextEditor::TextStyle textStyle)
|
||||
static QList<RowData> readLines(const QString &patch,
|
||||
bool ignoreWhitespace,
|
||||
bool lastChunk,
|
||||
bool *lastChunkAtTheEndOfFile,
|
||||
bool *ok)
|
||||
{
|
||||
QTextCharFormat format = fontSettings.toTextCharFormat(textStyle);
|
||||
format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||
return format;
|
||||
// const QRegExp lineRegExp(QLatin1String("(?:\\n)" // beginning of the line
|
||||
// "([ \\-\\+\\\\])([^\\n]*)" // -, +, \\ or space, followed by any no-newline character
|
||||
// "(?:\\n|$)")); // end of line or file
|
||||
QList<Diff> diffList;
|
||||
|
||||
const QChar newLine = QLatin1Char('\n');
|
||||
|
||||
int lastEqual = -1;
|
||||
int lastDelete = -1;
|
||||
int lastInsert = -1;
|
||||
|
||||
int noNewLineInEqual = -1;
|
||||
int noNewLineInDelete = -1;
|
||||
int noNewLineInInsert = -1;
|
||||
|
||||
const QStringList lines = patch.split(newLine);
|
||||
int i;
|
||||
for (i = 0; i < lines.count(); i++) {
|
||||
const QString line = lines.at(i);
|
||||
if (line.isEmpty())
|
||||
break; // need to have at least one character (1 column)
|
||||
QChar firstCharacter = line.at(0);
|
||||
if (firstCharacter == QLatin1Char('\\')) { // no new line marker
|
||||
if (!lastChunk) // can only appear in last chunk of the file
|
||||
break;
|
||||
if (!diffList.isEmpty()) {
|
||||
Diff &last = diffList.last();
|
||||
if (last.text.isEmpty())
|
||||
break;
|
||||
if (last.text.at(0) == newLine) // there is a new line
|
||||
break;
|
||||
|
||||
if (last.command == Diff::Equal) {
|
||||
if (noNewLineInEqual >= 0)
|
||||
break;
|
||||
noNewLineInEqual = diffList.count() - 1;
|
||||
} else if (last.command == Diff::Delete) {
|
||||
if (noNewLineInDelete >= 0)
|
||||
break;
|
||||
noNewLineInDelete = diffList.count() - 1;
|
||||
} else if (last.command == Diff::Insert) {
|
||||
if (noNewLineInInsert >= 0)
|
||||
break;
|
||||
noNewLineInInsert = diffList.count() - 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Diff::Command command = Diff::Equal;
|
||||
if (firstCharacter == QLatin1Char(' ')) // common line
|
||||
command = Diff::Equal;
|
||||
else if (firstCharacter == QLatin1Char('-')) // deleted line
|
||||
command = Diff::Delete;
|
||||
else if (firstCharacter == QLatin1Char('+')) // inserted line
|
||||
command = Diff::Insert;
|
||||
else
|
||||
break; // no other character may exist as the first character
|
||||
|
||||
Diff diffToBeAdded(command, line.mid(1) + newLine);
|
||||
|
||||
if (!diffList.isEmpty() && diffList.last().command == command)
|
||||
diffList.last().text.append(diffToBeAdded.text);
|
||||
else
|
||||
diffList.append(diffToBeAdded);
|
||||
|
||||
if (command == Diff::Equal) // common line
|
||||
lastEqual = diffList.count() - 1;
|
||||
else if (command == Diff::Delete) // deleted line
|
||||
lastDelete = diffList.count() - 1;
|
||||
else if (command == Diff::Insert) // inserted line
|
||||
lastInsert = diffList.count() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < lines.count()
|
||||
|| (noNewLineInEqual >= 0 && (noNewLineInDelete >= 0 || noNewLineInInsert >= 0))
|
||||
|| (noNewLineInEqual >= 0 && noNewLineInEqual != lastEqual)
|
||||
|| (noNewLineInDelete >= 0 && noNewLineInDelete != lastDelete)
|
||||
|| (noNewLineInInsert >= 0 && noNewLineInInsert != lastInsert)) {
|
||||
if (ok)
|
||||
*ok = false;
|
||||
return QList<RowData>();
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = true;
|
||||
|
||||
bool removeNewLineFromLastEqual = false;
|
||||
bool removeNewLineFromLastDelete = false;
|
||||
bool removeNewLineFromLastInsert = false;
|
||||
bool prependNewLineAfterLastEqual = false;
|
||||
|
||||
if (noNewLineInDelete >= 0 || noNewLineInInsert >= 0) {
|
||||
if (noNewLineInDelete >= 0)
|
||||
removeNewLineFromLastDelete = true;
|
||||
if (noNewLineInInsert >= 0)
|
||||
removeNewLineFromLastInsert = true;
|
||||
} else if (lastEqual > lastDelete && lastEqual > lastInsert) {
|
||||
removeNewLineFromLastEqual = true;
|
||||
} else if (lastDelete > lastEqual && lastDelete > lastInsert) {
|
||||
if (lastInsert > lastEqual) {
|
||||
removeNewLineFromLastDelete = true;
|
||||
removeNewLineFromLastInsert = true;
|
||||
} else if (lastEqual > lastInsert) {
|
||||
removeNewLineFromLastEqual = true;
|
||||
prependNewLineAfterLastEqual = true;
|
||||
}
|
||||
} else if (lastInsert > lastEqual && lastInsert > lastDelete) {
|
||||
if (lastDelete > lastEqual) {
|
||||
removeNewLineFromLastDelete = true;
|
||||
removeNewLineFromLastInsert = true;
|
||||
} else if (lastEqual > lastDelete) {
|
||||
removeNewLineFromLastEqual = true;
|
||||
prependNewLineAfterLastEqual = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (removeNewLineFromLastEqual) {
|
||||
Diff &diff = diffList[lastEqual];
|
||||
diff.text = diff.text.left(diff.text.count() - 1);
|
||||
}
|
||||
if (removeNewLineFromLastDelete) {
|
||||
Diff &diff = diffList[lastDelete];
|
||||
diff.text = diff.text.left(diff.text.count() - 1);
|
||||
}
|
||||
if (removeNewLineFromLastInsert) {
|
||||
Diff &diff = diffList[lastInsert];
|
||||
diff.text = diff.text.left(diff.text.count() - 1);
|
||||
}
|
||||
if (prependNewLineAfterLastEqual) {
|
||||
Diff &diff = diffList[lastEqual + 1];
|
||||
diff.text = newLine + diff.text;
|
||||
}
|
||||
|
||||
if (lastChunkAtTheEndOfFile) {
|
||||
*lastChunkAtTheEndOfFile = noNewLineInEqual >= 0
|
||||
|| noNewLineInDelete >= 0|| noNewLineInInsert >= 0;
|
||||
}
|
||||
|
||||
// diffList = Differ::merge(diffList);
|
||||
QList<Diff> leftDiffList;
|
||||
QList<Diff> rightDiffList;
|
||||
Differ::splitDiffList(diffList, &leftDiffList, &rightDiffList);
|
||||
QList<Diff> outputLeftDiffList;
|
||||
QList<Diff> outputRightDiffList;
|
||||
|
||||
if (ignoreWhitespace) {
|
||||
const QList<Diff> leftIntermediate =
|
||||
Differ::moveWhitespaceIntoEqualities(leftDiffList);
|
||||
const QList<Diff> rightIntermediate =
|
||||
Differ::moveWhitespaceIntoEqualities(rightDiffList);
|
||||
Differ::ignoreWhitespaceBetweenEqualities(leftIntermediate,
|
||||
rightIntermediate,
|
||||
&outputLeftDiffList,
|
||||
&outputRightDiffList);
|
||||
} else {
|
||||
Differ::diffBetweenEqualities(leftDiffList,
|
||||
rightDiffList,
|
||||
&outputLeftDiffList,
|
||||
&outputRightDiffList);
|
||||
}
|
||||
|
||||
return DiffUtils::calculateOriginalData(outputLeftDiffList,
|
||||
outputRightDiffList).rows;
|
||||
}
|
||||
|
||||
static QList<ChunkData> readChunks(const QString &patch,
|
||||
bool ignoreWhitespace,
|
||||
bool *lastChunkAtTheEndOfFile,
|
||||
bool *ok)
|
||||
{
|
||||
const QRegExp chunkRegExp(QLatin1String("((?:\\n|^)" // beginning of the line
|
||||
"@@ \\-([\\d]+)\\,[\\d]+ \\+([\\d]+)\\,[\\d]+ @@" // @@ -leftPos,leftCount +rightPos,rightCount @@
|
||||
"(?:\\ +[^\\n]*)?" // optional hint (e.g. function name)
|
||||
"(?:\\n))")); // end of line (need to be followed by text line)
|
||||
|
||||
bool readOk = false;
|
||||
|
||||
QList<ChunkData> chunkDataList;
|
||||
|
||||
int pos = chunkRegExp.indexIn(patch, 0);
|
||||
if (pos == 0) {
|
||||
int endOfLastChunk = 0;
|
||||
do {
|
||||
const QStringList capturedTexts = chunkRegExp.capturedTexts();
|
||||
const QString captured = capturedTexts.at(1);
|
||||
const int leftStartingPos = capturedTexts.at(2).toInt();
|
||||
const int rightStartingPos = capturedTexts.at(3).toInt();
|
||||
if (endOfLastChunk > 0) {
|
||||
const QString lines = patch.mid(endOfLastChunk,
|
||||
pos - endOfLastChunk);
|
||||
chunkDataList.last().rows = readLines(lines,
|
||||
ignoreWhitespace,
|
||||
false,
|
||||
lastChunkAtTheEndOfFile,
|
||||
&readOk);
|
||||
if (!readOk)
|
||||
break;
|
||||
}
|
||||
pos += captured.count();
|
||||
endOfLastChunk = pos;
|
||||
ChunkData chunkData;
|
||||
chunkData.leftStartingLineNumber = leftStartingPos - 1;
|
||||
chunkData.rightStartingLineNumber = rightStartingPos - 1;
|
||||
chunkDataList.append(chunkData);
|
||||
} while ((pos = chunkRegExp.indexIn(patch, pos)) != -1);
|
||||
|
||||
if (endOfLastChunk > 0) {
|
||||
const QString lines = patch.mid(endOfLastChunk);
|
||||
chunkDataList.last().rows = readLines(lines,
|
||||
ignoreWhitespace,
|
||||
true,
|
||||
lastChunkAtTheEndOfFile,
|
||||
&readOk);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = readOk;
|
||||
|
||||
return chunkDataList;
|
||||
}
|
||||
|
||||
static FileData readDiffHeaderAndChunks(const QString &headerAndChunks,
|
||||
bool ignoreWhitespace,
|
||||
bool *ok)
|
||||
{
|
||||
QString patch = headerAndChunks;
|
||||
FileData fileData;
|
||||
bool readOk = false;
|
||||
|
||||
const QRegExp leftFileRegExp(QLatin1String("((?:\\n|^)\\-{3} ") // "--- "
|
||||
+ QLatin1String("([^\\t\\n]+)") // "fileName1"
|
||||
+ QLatin1String("(?:\\t[^\\n]*)*\\n)")); // optionally followed by: \t anything \t anything ...)
|
||||
const QRegExp rightFileRegExp(QLatin1String("(^\\+{3} ") // "+++ "
|
||||
+ QLatin1String("([^\\t\\n]+)") // "fileName2"
|
||||
+ QLatin1String("(?:\\t[^\\n]*)*\\n)")); // optionally followed by: \t anything \t anything ...)
|
||||
const QRegExp binaryRegExp(QLatin1String("(^Binary files ")
|
||||
+ QLatin1String("([^\\t\\n]+)")
|
||||
+ QLatin1String(" and ")
|
||||
+ QLatin1String("([^\\t\\n]+)")
|
||||
+ QLatin1String(" differ$)"));
|
||||
|
||||
// followed either by leftFileRegExp or by binaryRegExp
|
||||
if (leftFileRegExp.indexIn(patch, 0) == 0) {
|
||||
const QStringList leftCaptured = leftFileRegExp.capturedTexts();
|
||||
patch = patch.mid(leftCaptured.at(1).count());
|
||||
fileData.leftFileInfo.fileName = leftCaptured.at(2);
|
||||
|
||||
// followed by rightFileRegExp
|
||||
if (rightFileRegExp.indexIn(patch, 0) == 0) {
|
||||
const QStringList rightCaptured = rightFileRegExp.capturedTexts();
|
||||
patch = patch.mid(rightCaptured.at(1).count());
|
||||
fileData.rightFileInfo.fileName = rightCaptured.at(2);
|
||||
|
||||
fileData.chunks = readChunks(patch,
|
||||
ignoreWhitespace,
|
||||
&fileData.lastChunkAtTheEndOfFile,
|
||||
&readOk);
|
||||
}
|
||||
} else if (binaryRegExp.indexIn(patch, 0) == 0) {
|
||||
const QStringList binaryCaptured = binaryRegExp.capturedTexts();
|
||||
fileData.leftFileInfo.fileName = binaryCaptured.at(2);
|
||||
fileData.rightFileInfo.fileName = binaryCaptured.at(3);
|
||||
fileData.binaryFiles = true;
|
||||
readOk = true;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = readOk;
|
||||
|
||||
if (!readOk)
|
||||
return FileData();
|
||||
|
||||
return fileData;
|
||||
|
||||
}
|
||||
|
||||
static QList<FileData> readDiffPatch(const QString &patch,
|
||||
bool ignoreWhitespace,
|
||||
bool *ok)
|
||||
{
|
||||
const QRegExp diffRegExp(QLatin1String("(" // capture all
|
||||
"(?:\\n|^)" // new line of the beginning of a patch
|
||||
"(" // either
|
||||
"\\-{3} " // ---
|
||||
"[^\\t\\n]+" // filename1
|
||||
"(?:\\t[^\\n]*)*\\n" // optionally followed by: \t anything \t anything ...
|
||||
"\\+{3} " // +++
|
||||
"[^\\t\\n]+" // filename2
|
||||
"(?:\\t[^\\n]*)*\\n" // optionally followed by: \t anything \t anything ...
|
||||
"|" // or
|
||||
"Binary files "
|
||||
"[^\\t\\n]+" // filename1
|
||||
" and "
|
||||
"[^\\t\\n]+" // filename2
|
||||
" differ"
|
||||
")" // end of or
|
||||
")")); // end of capture all
|
||||
|
||||
bool readOk = false;
|
||||
|
||||
QList<FileData> fileDataList;
|
||||
|
||||
int pos = diffRegExp.indexIn(patch, 0);
|
||||
if (pos == 0) { // git style patch
|
||||
readOk = true;
|
||||
int lastPos = -1;
|
||||
do {
|
||||
const QStringList capturedTexts = diffRegExp.capturedTexts();
|
||||
const QString captured = capturedTexts.at(1);
|
||||
if (lastPos >= 0) {
|
||||
const QString headerAndChunks = patch.mid(lastPos,
|
||||
pos - lastPos);
|
||||
|
||||
const FileData fileData = readDiffHeaderAndChunks(headerAndChunks,
|
||||
ignoreWhitespace,
|
||||
&readOk);
|
||||
|
||||
if (!readOk)
|
||||
break;
|
||||
|
||||
fileDataList.append(fileData);
|
||||
}
|
||||
lastPos = pos;
|
||||
pos += captured.count();
|
||||
} while ((pos = diffRegExp.indexIn(patch, pos)) != -1);
|
||||
|
||||
if (lastPos >= 0 && readOk) {
|
||||
const QString headerAndChunks = patch.mid(lastPos,
|
||||
patch.count() - lastPos - 1);
|
||||
|
||||
const FileData fileData = readDiffHeaderAndChunks(headerAndChunks,
|
||||
ignoreWhitespace,
|
||||
&readOk);
|
||||
|
||||
if (readOk)
|
||||
fileDataList.append(fileData);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = readOk;
|
||||
|
||||
if (!readOk)
|
||||
return QList<FileData>();
|
||||
|
||||
return fileDataList;
|
||||
}
|
||||
|
||||
static FileData readGitHeaderAndChunks(const QString &headerAndChunks,
|
||||
const QString &fileName,
|
||||
bool ignoreWhitespace,
|
||||
bool *ok)
|
||||
{
|
||||
FileData fileData;
|
||||
fileData.leftFileInfo.fileName = fileName;
|
||||
fileData.rightFileInfo.fileName = fileName;
|
||||
|
||||
QString patch = headerAndChunks;
|
||||
bool readOk = false;
|
||||
|
||||
const QString devNull(QLatin1String("/dev/null"));
|
||||
|
||||
// will be followed by: index 0000000..shasha, file "a" replaced by "/dev/null", @@ -0,0 +m,n @@
|
||||
const QRegExp newFileMode(QLatin1String("(^new file mode [\\d]+\\n)")); // new file mode octal
|
||||
|
||||
// will be followed by: index shasha..0000000, file "b" replaced by "/dev/null", @@ -m,n +0,0 @@
|
||||
const QRegExp deletedFileMode(QLatin1String("(^deleted file mode [\\d]+\\n)")); // deleted file mode octal
|
||||
|
||||
const QRegExp indexRegExp(QLatin1String("(^index ([\\w]+)\\.{2}([\\w]+)(?: [\\d]+)?\\n)")); // index cap2..cap3(optionally: octal)
|
||||
|
||||
QString leftFileName = QLatin1String("a/") + fileName;
|
||||
QString rightFileName = QLatin1String("b/") + fileName;
|
||||
|
||||
if (newFileMode.indexIn(patch, 0) == 0) {
|
||||
fileData.leftFileInfo.devNull = true;
|
||||
leftFileName = devNull;
|
||||
patch = patch.mid(newFileMode.capturedTexts().at(1).count());
|
||||
} else if (deletedFileMode.indexIn(patch, 0) == 0) {
|
||||
fileData.rightFileInfo.devNull = true;
|
||||
rightFileName = devNull;
|
||||
patch = patch.mid(deletedFileMode.capturedTexts().at(1).count());
|
||||
}
|
||||
|
||||
if (indexRegExp.indexIn(patch, 0) == 0) {
|
||||
const QStringList capturedTexts = indexRegExp.capturedTexts();
|
||||
const QString captured = capturedTexts.at(1);
|
||||
fileData.leftFileInfo.typeInfo = capturedTexts.at(2);
|
||||
fileData.rightFileInfo.typeInfo = capturedTexts.at(3);
|
||||
|
||||
patch = patch.mid(captured.count());
|
||||
|
||||
const QRegExp leftFileRegExp(QLatin1String("(^\\-{3} ") // "--- "
|
||||
+ leftFileName // "a/fileName" or "/dev/null"
|
||||
+ QLatin1String("(?:\\t[^\\n]*)*\\n)")); // optionally followed by: \t anything \t anything ...)
|
||||
const QRegExp rightFileRegExp(QLatin1String("(^\\+{3} ") // "+++ "
|
||||
+ rightFileName // "b/fileName" or "/dev/null"
|
||||
+ QLatin1String("(?:\\t[^\\n]*)*\\n)")); // optionally followed by: \t anything \t anything ...)
|
||||
const QRegExp binaryRegExp(QLatin1String("(^Binary files ")
|
||||
+ leftFileName
|
||||
+ QLatin1String(" and ")
|
||||
+ rightFileName
|
||||
+ QLatin1String(" differ$)"));
|
||||
|
||||
// followed either by leftFileRegExp or by binaryRegExp
|
||||
if (leftFileRegExp.indexIn(patch, 0) == 0) {
|
||||
patch = patch.mid(leftFileRegExp.capturedTexts().at(1).count());
|
||||
|
||||
// followed by rightFileRegExp
|
||||
if (rightFileRegExp.indexIn(patch, 0) == 0) {
|
||||
patch = patch.mid(rightFileRegExp.capturedTexts().at(1).count());
|
||||
|
||||
fileData.chunks = readChunks(patch,
|
||||
ignoreWhitespace,
|
||||
&fileData.lastChunkAtTheEndOfFile,
|
||||
&readOk);
|
||||
}
|
||||
} else if (binaryRegExp.indexIn(patch, 0) == 0) {
|
||||
readOk = true;
|
||||
fileData.binaryFiles = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = readOk;
|
||||
|
||||
if (!readOk)
|
||||
return FileData();
|
||||
|
||||
return fileData;
|
||||
}
|
||||
|
||||
static QList<FileData> readGitPatch(const QString &patch, bool ignoreWhitespace, bool *ok)
|
||||
{
|
||||
const QRegExp gitRegExp(QLatin1String("((?:\\n|^)diff --git a/([^\\n]+) b/\\2\\n)")); // diff --git a/cap2 b/cap2
|
||||
|
||||
bool readOk = false;
|
||||
|
||||
QList<FileData> fileDataList;
|
||||
|
||||
int pos = gitRegExp.indexIn(patch, 0);
|
||||
if (pos == 0) { // git style patch
|
||||
readOk = true;
|
||||
int endOfLastHeader = 0;
|
||||
QString lastFileName;
|
||||
do {
|
||||
const QStringList capturedTexts = gitRegExp.capturedTexts();
|
||||
const QString captured = capturedTexts.at(1);
|
||||
const QString fileName = capturedTexts.at(2);
|
||||
if (endOfLastHeader > 0) {
|
||||
const QString headerAndChunks = patch.mid(endOfLastHeader,
|
||||
pos - endOfLastHeader);
|
||||
|
||||
const FileData fileData = readGitHeaderAndChunks(headerAndChunks,
|
||||
lastFileName,
|
||||
ignoreWhitespace,
|
||||
&readOk);
|
||||
|
||||
if (!readOk)
|
||||
break;
|
||||
|
||||
fileDataList.append(fileData);
|
||||
}
|
||||
pos += captured.count();
|
||||
endOfLastHeader = pos;
|
||||
lastFileName = fileName;
|
||||
} while ((pos = gitRegExp.indexIn(patch, pos)) != -1);
|
||||
|
||||
if (endOfLastHeader > 0 && readOk) {
|
||||
const QString headerAndChunks = patch.mid(endOfLastHeader,
|
||||
patch.count() - endOfLastHeader - 1);
|
||||
|
||||
const FileData fileData = readGitHeaderAndChunks(headerAndChunks,
|
||||
lastFileName,
|
||||
ignoreWhitespace,
|
||||
&readOk);
|
||||
|
||||
if (readOk)
|
||||
fileDataList.append(fileData);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = readOk;
|
||||
|
||||
if (!readOk)
|
||||
return QList<FileData>();
|
||||
|
||||
return fileDataList;
|
||||
}
|
||||
|
||||
QList<FileData> DiffUtils::readPatch(const QString &patch, bool ignoreWhitespace, bool *ok)
|
||||
{
|
||||
bool readOk = false;
|
||||
|
||||
QList<FileData> fileDataList;
|
||||
|
||||
fileDataList = readGitPatch(patch, ignoreWhitespace, &readOk);
|
||||
if (!readOk)
|
||||
fileDataList = readDiffPatch(patch, ignoreWhitespace, &readOk);
|
||||
|
||||
if (ok)
|
||||
*ok = readOk;
|
||||
|
||||
return fileDataList;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace DiffEditor
|
||||
|
||||
@@ -30,11 +30,12 @@
|
||||
#ifndef DIFFUTILS_H
|
||||
#define DIFFUTILS_H
|
||||
|
||||
#include "diffeditor_global.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QTextEdit>
|
||||
|
||||
#include "diffeditorcontroller.h"
|
||||
#include "texteditor/texteditorconstants.h"
|
||||
|
||||
namespace TextEditor { class FontSettings; }
|
||||
@@ -43,9 +44,18 @@ namespace DiffEditor {
|
||||
|
||||
class Diff;
|
||||
|
||||
namespace Internal {
|
||||
class DIFFEDITOR_EXPORT DiffFileInfo {
|
||||
public:
|
||||
DiffFileInfo() : devNull(false) {}
|
||||
DiffFileInfo(const QString &file) : fileName(file), devNull(false) {}
|
||||
DiffFileInfo(const QString &file, const QString &type)
|
||||
: fileName(file), typeInfo(type), devNull(false) {}
|
||||
QString fileName;
|
||||
QString typeInfo;
|
||||
bool devNull;
|
||||
};
|
||||
|
||||
class TextLineData {
|
||||
class DIFFEDITOR_EXPORT TextLineData {
|
||||
public:
|
||||
enum TextLineType {
|
||||
TextLine,
|
||||
@@ -66,7 +76,7 @@ public:
|
||||
QMap<int, int> changedPositions; // counting from the beginning of the line
|
||||
};
|
||||
|
||||
class RowData {
|
||||
class DIFFEDITOR_EXPORT RowData {
|
||||
public:
|
||||
RowData() : equal(false) {}
|
||||
RowData(const TextLineData &l)
|
||||
@@ -78,35 +88,55 @@ public:
|
||||
bool equal;
|
||||
};
|
||||
|
||||
class ChunkData {
|
||||
class DIFFEDITOR_EXPORT ChunkData {
|
||||
public:
|
||||
ChunkData() : contextChunk(false) {}
|
||||
ChunkData() : contextChunk(false),
|
||||
leftStartingLineNumber(0), rightStartingLineNumber(0) {}
|
||||
QList<RowData> rows;
|
||||
bool contextChunk;
|
||||
int leftStartingLineNumber;
|
||||
int rightStartingLineNumber;
|
||||
};
|
||||
|
||||
class FileData {
|
||||
class DIFFEDITOR_EXPORT FileData {
|
||||
public:
|
||||
FileData() {}
|
||||
FileData(const ChunkData &chunkData) { chunks.append(chunkData); }
|
||||
FileData()
|
||||
: binaryFiles(false),
|
||||
lastChunkAtTheEndOfFile(false),
|
||||
contextChunksIncluded(false) {}
|
||||
FileData(const ChunkData &chunkData)
|
||||
: binaryFiles(false),
|
||||
lastChunkAtTheEndOfFile(false),
|
||||
contextChunksIncluded(false) { chunks.append(chunkData); }
|
||||
QList<ChunkData> chunks;
|
||||
DiffEditorController::DiffFileInfo leftFileInfo;
|
||||
DiffEditorController::DiffFileInfo rightFileInfo;
|
||||
DiffFileInfo leftFileInfo;
|
||||
DiffFileInfo rightFileInfo;
|
||||
bool binaryFiles;
|
||||
bool lastChunkAtTheEndOfFile;
|
||||
bool contextChunksIncluded;
|
||||
};
|
||||
|
||||
class DIFFEDITOR_EXPORT DiffUtils {
|
||||
public:
|
||||
|
||||
static ChunkData calculateOriginalData(const QList<Diff> &leftDiffList,
|
||||
const QList<Diff> &rightDiffList);
|
||||
static FileData calculateContextData(const ChunkData &originalData,
|
||||
int contextLinesNumber,
|
||||
int joinChunkThreshold = 1);
|
||||
static QString makePatchLine(const QChar &startLineCharacter,
|
||||
const QString &textLine,
|
||||
bool lastChunk,
|
||||
bool lastLine);
|
||||
static QString makePatch(const ChunkData &chunkData,
|
||||
const QString &leftFileName,
|
||||
const QString &rightFileName,
|
||||
bool lastChunk = false);
|
||||
static QList<FileData> readPatch(const QString &patch,
|
||||
bool ignoreWhitespace,
|
||||
bool *ok = 0);
|
||||
};
|
||||
|
||||
ChunkData calculateOriginalData(const QList<Diff> &leftDiffList,
|
||||
const QList<Diff> &rightDiffList);
|
||||
FileData calculateContextData(const ChunkData &originalData,
|
||||
int contextLinesNumber);
|
||||
void addChangedPositions(int positionOffset,
|
||||
const QMap<int, int> &originalChangedPositions,
|
||||
QMap<int, int> *changedPositions);
|
||||
QList<QTextEdit::ExtraSelection> colorPositions(const QTextCharFormat &format,
|
||||
QTextCursor &cursor,
|
||||
const QMap<int, int> &positions);
|
||||
QTextCharFormat fullWidthFormatForTextStyle(const TextEditor::FontSettings &fontSettings,
|
||||
TextEditor::TextStyle textStyle);
|
||||
} // namespace Internal
|
||||
} // namespace DiffEditor
|
||||
|
||||
#endif // DIFFUTILS_H
|
||||
|
||||
BIN
src/plugins/diffeditor/images/reload.png
Normal file
BIN
src/plugins/diffeditor/images/reload.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 250 B |
BIN
src/plugins/diffeditor/images/sidebysidediff.png
Normal file
BIN
src/plugins/diffeditor/images/sidebysidediff.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 257 B |
BIN
src/plugins/diffeditor/images/topbar.png
Normal file
BIN
src/plugins/diffeditor/images/topbar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 379 B |
BIN
src/plugins/diffeditor/images/unifieddiff.png
Normal file
BIN
src/plugins/diffeditor/images/unifieddiff.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 258 B |
125
src/plugins/diffeditor/selectabletexteditorwidget.cpp
Normal file
125
src/plugins/diffeditor/selectabletexteditorwidget.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 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 "selectabletexteditorwidget.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QTextBlock>
|
||||
|
||||
namespace DiffEditor {
|
||||
|
||||
SelectableTextEditorWidget::SelectableTextEditorWidget(QWidget *parent)
|
||||
: BaseTextEditorWidget(parent)
|
||||
{
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
}
|
||||
|
||||
SelectableTextEditorWidget::~SelectableTextEditorWidget()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SelectableTextEditorWidget::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
paintSelections(e);
|
||||
BaseTextEditorWidget::paintEvent(e);
|
||||
}
|
||||
|
||||
void SelectableTextEditorWidget::paintSelections(QPaintEvent *e)
|
||||
{
|
||||
QPainter painter(viewport());
|
||||
|
||||
QPointF offset = contentOffset();
|
||||
QTextBlock firstBlock = firstVisibleBlock();
|
||||
QTextBlock currentBlock = firstBlock;
|
||||
|
||||
while (currentBlock.isValid()) {
|
||||
if (currentBlock.isVisible()) {
|
||||
qreal top = blockBoundingGeometry(currentBlock).translated(offset).top();
|
||||
qreal bottom = top + blockBoundingRect(currentBlock).height();
|
||||
|
||||
if (top > e->rect().bottom())
|
||||
break;
|
||||
|
||||
if (bottom >= e->rect().top()) {
|
||||
const int blockNumber = currentBlock.blockNumber();
|
||||
|
||||
paintSelections(painter, m_selections.value(blockNumber),
|
||||
currentBlock, top);
|
||||
}
|
||||
}
|
||||
currentBlock = currentBlock.next();
|
||||
}
|
||||
}
|
||||
|
||||
void SelectableTextEditorWidget::paintSelections(QPainter &painter,
|
||||
const QList<DiffSelection> &selections,
|
||||
const QTextBlock &block,
|
||||
int top)
|
||||
{
|
||||
QPointF offset = contentOffset();
|
||||
painter.save();
|
||||
|
||||
QTextLayout *layout = block.layout();
|
||||
QTextLine textLine = layout->lineAt(0);
|
||||
QRectF lineRect = textLine.naturalTextRect().translated(offset.x(), top);
|
||||
QRect clipRect = contentsRect();
|
||||
painter.setClipRect(clipRect);
|
||||
for (int i = 0; i < selections.count(); i++) {
|
||||
const DiffSelection &selection = selections.at(i);
|
||||
|
||||
if (!selection.format)
|
||||
continue;
|
||||
if (selection.start == -1 && selection.end == 0)
|
||||
continue;
|
||||
if (selection.start == selection.end && selection.start >= 0)
|
||||
continue;
|
||||
|
||||
painter.save();
|
||||
const QBrush &brush = selection.format->background();
|
||||
painter.setPen(brush.color());
|
||||
painter.setBrush(brush);
|
||||
|
||||
const int x1 = selection.start <= 0
|
||||
? -1
|
||||
: textLine.cursorToX(selection.start) + offset.x();
|
||||
const int x2 = selection.end < 0
|
||||
? clipRect.right()
|
||||
: textLine.cursorToX(selection.end) + offset.x();
|
||||
painter.drawRect(QRectF(QPointF(x1, lineRect.top()),
|
||||
QPointF(x2, lineRect.bottom())));
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
painter.restore();
|
||||
}
|
||||
|
||||
|
||||
} // namespace DiffEditor
|
||||
|
||||
80
src/plugins/diffeditor/selectabletexteditorwidget.h
Normal file
80
src/plugins/diffeditor/selectabletexteditorwidget.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SELECTABLETEXTEDITORWIDGET_H
|
||||
#define SELECTABLETEXTEDITORWIDGET_H
|
||||
|
||||
#include "diffeditor_global.h"
|
||||
#include <texteditor/basetexteditor.h>
|
||||
|
||||
namespace DiffEditor {
|
||||
|
||||
class DIFFEDITOR_EXPORT DiffSelection
|
||||
{
|
||||
public:
|
||||
DiffSelection() : start(-1), end(-1), format(0) {}
|
||||
DiffSelection(QTextCharFormat *f) : start(-1), end(-1), format(f) {}
|
||||
DiffSelection(int s, int e, QTextCharFormat *f) : start(s), end(e), format(f) {}
|
||||
|
||||
int start;
|
||||
int end;
|
||||
QTextCharFormat *format;
|
||||
};
|
||||
|
||||
class DIFFEDITOR_EXPORT SelectableTextEditorWidget
|
||||
: public TextEditor::BaseTextEditorWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SelectableTextEditorWidget(QWidget *parent = 0);
|
||||
~SelectableTextEditorWidget();
|
||||
void setSelections(const QMap<int,
|
||||
QList<DiffSelection> > &selections) {
|
||||
m_selections = selections;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *e);
|
||||
|
||||
private:
|
||||
void paintSelections(QPaintEvent *e);
|
||||
void paintSelections(QPainter &painter,
|
||||
const QList<DiffSelection> &selections,
|
||||
const QTextBlock &block,
|
||||
int top);
|
||||
|
||||
// block number, list of ranges
|
||||
// DiffSelection.start - can be -1 (continues from the previous line)
|
||||
// DiffSelection.end - can be -1 (spans to the end of line, even after the last character in line)
|
||||
QMap<int, QList<DiffSelection> > m_selections;
|
||||
};
|
||||
|
||||
} // namespace DiffEditor
|
||||
|
||||
#endif // SELECTABLETEXTEDITORWIDGET_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,17 +40,15 @@ namespace TextEditor { class FontSettings; }
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSplitter;
|
||||
class QMenu;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
namespace DiffEditor {
|
||||
class DiffEditorGuiController;
|
||||
class SideDiffEditorWidget;
|
||||
|
||||
namespace Internal {
|
||||
class ChunkData;
|
||||
class FileData;
|
||||
}
|
||||
|
||||
class DIFFEDITOR_EXPORT SideBySideDiffEditorWidget : public QWidget
|
||||
{
|
||||
@@ -64,40 +62,39 @@ public:
|
||||
|
||||
private slots:
|
||||
void clear(const QString &message = QString());
|
||||
void setDiff(const QList<DiffEditorController::DiffFilesContents> &diffFileList, const QString &workingDirectory);
|
||||
void clearAll(const QString &message = QString());
|
||||
void setDiff(const QList<FileData> &diffFileList,
|
||||
const QString &workingDirectory);
|
||||
|
||||
void setContextLinesNumber(int lines);
|
||||
void setIgnoreWhitespaces(bool ignore);
|
||||
void setCurrentDiffFileIndex(int diffFileIndex);
|
||||
|
||||
void setFontSettings(const TextEditor::FontSettings &fontSettings);
|
||||
void slotLeftJumpToOriginalFileRequested(int diffFileIndex, int lineNumber, int columnNumber);
|
||||
void slotRightJumpToOriginalFileRequested(int diffFileIndex, int lineNumber, int columnNumber);
|
||||
void slotLeftJumpToOriginalFileRequested(int diffFileIndex,
|
||||
int lineNumber, int columnNumber);
|
||||
void slotRightJumpToOriginalFileRequested(int diffFileIndex,
|
||||
int lineNumber, int columnNumber);
|
||||
void slotLeftContextMenuRequested(QMenu *menu, int diffFileIndex,
|
||||
int chunkIndex);
|
||||
void slotRightContextMenuRequested(QMenu *menu, int diffFileIndex,
|
||||
int chunkIndex);
|
||||
void slotSendChunkToCodePaster();
|
||||
void slotApplyChunk();
|
||||
void slotRevertChunk();
|
||||
void leftVSliderChanged();
|
||||
void rightVSliderChanged();
|
||||
void leftHSliderChanged();
|
||||
void rightHSliderChanged();
|
||||
void leftCursorPositionChanged();
|
||||
void rightCursorPositionChanged();
|
||||
void leftDocumentSizeChanged();
|
||||
void rightDocumentSizeChanged();
|
||||
// void leftDocumentSizeChanged();
|
||||
// void rightDocumentSizeChanged();
|
||||
|
||||
private:
|
||||
class DiffList {
|
||||
public:
|
||||
DiffEditorController::DiffFileInfo leftFileInfo;
|
||||
DiffEditorController::DiffFileInfo rightFileInfo;
|
||||
QList<Diff> diffList;
|
||||
};
|
||||
|
||||
void setDiff(const QList<DiffList> &diffList);
|
||||
void handleWhitespaces(const QList<Diff> &input,
|
||||
QList<Diff> *leftOutput,
|
||||
QList<Diff> *rightOutput) const;
|
||||
void colorDiff(const QList<Internal::FileData> &fileDataList);
|
||||
void showDiff();
|
||||
void synchronizeFoldings(SideDiffEditorWidget *source, SideDiffEditorWidget *destination);
|
||||
void jumpToOriginalFile(const QString &fileName, int lineNumber, int columnNumber);
|
||||
// void synchronizeFoldings(SideDiffEditorWidget *source, SideDiffEditorWidget *destination);
|
||||
void jumpToOriginalFile(const QString &fileName,
|
||||
int lineNumber, int columnNumber);
|
||||
void patch(int diffFileIndex, int chunkIndex, bool revert);
|
||||
|
||||
DiffEditorGuiController *m_guiController;
|
||||
DiffEditorController *m_controller;
|
||||
@@ -105,12 +102,14 @@ private:
|
||||
SideDiffEditorWidget *m_rightEditor;
|
||||
QSplitter *m_splitter;
|
||||
|
||||
QList<DiffList> m_diffList; // list of original outputs from differ
|
||||
QList<Internal::ChunkData> m_originalChunkData; // one big chunk for every file, ignoreWhitespace taken into account
|
||||
QList<Internal::FileData> m_contextFileData; // ultimate data to be shown, contextLinesNumber taken into account
|
||||
QList<FileData> m_contextFileData; // ultimate data to be shown, contextLinesNumber taken into account
|
||||
|
||||
bool m_ignoreCurrentIndexChange;
|
||||
bool m_foldingBlocker;
|
||||
int m_contextMenuFileIndex;
|
||||
int m_contextMenuChunkIndex;
|
||||
|
||||
QTextCharFormat m_spanLineFormat;
|
||||
QTextCharFormat m_fileLineFormat;
|
||||
QTextCharFormat m_chunkLineFormat;
|
||||
QTextCharFormat m_leftLineFormat;
|
||||
|
||||
783
src/plugins/diffeditor/unifieddiffeditorwidget.cpp
Normal file
783
src/plugins/diffeditor/unifieddiffeditorwidget.cpp
Normal file
@@ -0,0 +1,783 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 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 "unifieddiffeditorwidget.h"
|
||||
#include "diffeditorguicontroller.h"
|
||||
#include "diffutils.h"
|
||||
#include "diffeditorconstants.h"
|
||||
|
||||
#include <QPlainTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPlainTextDocumentLayout>
|
||||
#include <QTextBlock>
|
||||
#include <QScrollBar>
|
||||
#include <QPainter>
|
||||
#include <QDir>
|
||||
#include <QToolButton>
|
||||
#include <QTextCodec>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <texteditor/basetexteditor.h>
|
||||
#include <texteditor/basetextdocumentlayout.h>
|
||||
#include <texteditor/ihighlighterfactory.h>
|
||||
#include <texteditor/syntaxhighlighter.h>
|
||||
#include <texteditor/basetextdocument.h>
|
||||
#include <texteditor/texteditorsettings.h>
|
||||
#include <texteditor/fontsettings.h>
|
||||
#include <texteditor/displaysettings.h>
|
||||
#include <texteditor/highlighterutils.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/minisplitter.h>
|
||||
#include <coreplugin/mimedatabase.h>
|
||||
#include <coreplugin/patchtool.h>
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <utils/tooltip/tipcontents.h>
|
||||
#include <utils/tooltip/tooltip.h>
|
||||
|
||||
static const int FILE_LEVEL = 1;
|
||||
static const int CHUNK_LEVEL = 2;
|
||||
|
||||
using namespace Core;
|
||||
using namespace TextEditor;
|
||||
|
||||
namespace DiffEditor {
|
||||
|
||||
class UnifiedDiffEditor : public BaseTextEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
UnifiedDiffEditor(BaseTextEditorWidget *editorWidget)
|
||||
: BaseTextEditor(editorWidget) {
|
||||
document()->setId("DiffEditor.UnifiedDiffEditor");
|
||||
}
|
||||
};
|
||||
|
||||
UnifiedDiffEditorWidget::UnifiedDiffEditorWidget(QWidget *parent)
|
||||
: SelectableTextEditorWidget(parent)
|
||||
, m_guiController(0)
|
||||
, m_controller(0)
|
||||
, 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);
|
||||
connect(TextEditorSettings::instance(),
|
||||
SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings)),
|
||||
this, SLOT(setDisplaySettings(TextEditor::DisplaySettings)));
|
||||
setDisplaySettings(TextEditorSettings::displaySettings());
|
||||
setCodeStyle(TextEditorSettings::codeStyle());
|
||||
|
||||
connect(TextEditorSettings::instance(),
|
||||
SIGNAL(fontSettingsChanged(TextEditor::FontSettings)),
|
||||
this, SLOT(setFontSettings(TextEditor::FontSettings)));
|
||||
setFontSettings(TextEditorSettings::fontSettings());
|
||||
|
||||
clear(tr("No controller"));
|
||||
|
||||
connect(this, SIGNAL(cursorPositionChanged()),
|
||||
this, SLOT(slotCursorPositionChangedInEditor()));
|
||||
}
|
||||
|
||||
UnifiedDiffEditorWidget::~UnifiedDiffEditorWidget()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::setDiffEditorGuiController(
|
||||
DiffEditorGuiController *controller)
|
||||
{
|
||||
if (m_guiController == controller)
|
||||
return;
|
||||
|
||||
if (m_guiController) {
|
||||
disconnect(m_controller, SIGNAL(cleared(QString)),
|
||||
this, SLOT(clearAll(QString)));
|
||||
disconnect(m_controller, SIGNAL(diffFilesChanged(QList<FileData>,QString)),
|
||||
this, SLOT(setDiff(QList<FileData>,QString)));
|
||||
|
||||
disconnect(m_guiController, SIGNAL(currentDiffFileIndexChanged(int)),
|
||||
this, SLOT(setCurrentDiffFileIndex(int)));
|
||||
|
||||
clear(tr("No controller"));
|
||||
}
|
||||
m_guiController = controller;
|
||||
m_controller = 0;
|
||||
if (m_guiController) {
|
||||
m_controller = m_guiController->controller();
|
||||
|
||||
connect(m_controller, SIGNAL(cleared(QString)),
|
||||
this, SLOT(clearAll(QString)));
|
||||
connect(m_controller, SIGNAL(diffFilesChanged(QList<FileData>,QString)),
|
||||
this, SLOT(setDiff(QList<FileData>,QString)));
|
||||
|
||||
connect(m_guiController, SIGNAL(currentDiffFileIndexChanged(int)),
|
||||
this, SLOT(setCurrentDiffFileIndex(int)));
|
||||
|
||||
setDiff(m_controller->diffFiles(), m_controller->workingDirectory());
|
||||
setCurrentDiffFileIndex(m_guiController->currentDiffFileIndex());
|
||||
}
|
||||
}
|
||||
|
||||
DiffEditorGuiController *UnifiedDiffEditorWidget::diffEditorGuiController() const
|
||||
{
|
||||
return m_guiController;
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::setDisplaySettings(const DisplaySettings &ds)
|
||||
{
|
||||
DisplaySettings settings = displaySettings();
|
||||
settings.m_visualizeWhitespace = ds.m_visualizeWhitespace;
|
||||
SelectableTextEditorWidget::setDisplaySettings(settings);
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::setFontSettings(const FontSettings &fontSettings)
|
||||
{
|
||||
baseTextDocument()->setFontSettings(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);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor()
|
||||
{
|
||||
if (!m_guiController)
|
||||
return;
|
||||
|
||||
if (m_ignoreCurrentIndexChange)
|
||||
return;
|
||||
|
||||
const bool oldIgnore = m_ignoreCurrentIndexChange;
|
||||
m_ignoreCurrentIndexChange = true;
|
||||
m_guiController->setCurrentDiffFileIndex(fileIndexForBlockNumber(
|
||||
textCursor().blockNumber()));
|
||||
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));
|
||||
|
||||
connect(this, SIGNAL(destroyed()), menu, SLOT(deleteLater()));
|
||||
menu->exec(e->globalPos());
|
||||
delete menu;
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::addContextMenuActions(QMenu *menu,
|
||||
int diffFileIndex,
|
||||
int chunkIndex)
|
||||
{
|
||||
if (!m_controller)
|
||||
return;
|
||||
|
||||
menu->addSeparator();
|
||||
menu->addSeparator();
|
||||
QAction *sendChunkToCodePasterAction =
|
||||
menu->addAction(tr("Send Chunk to CodePaster..."));
|
||||
connect(sendChunkToCodePasterAction, SIGNAL(triggered()),
|
||||
this, SLOT(slotSendChunkToCodePaster()));
|
||||
QAction *applyAction = menu->addAction(tr("Apply Chunk..."));
|
||||
connect(applyAction, SIGNAL(triggered()), this, SLOT(slotApplyChunk()));
|
||||
QAction *revertAction = menu->addAction(tr("Revert Chunk..."));
|
||||
connect(revertAction, SIGNAL(triggered()), this, SLOT(slotRevertChunk()));
|
||||
|
||||
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;
|
||||
|
||||
emit m_controller->chunkActionsRequested(menu, diffFileIndex, chunkIndex);
|
||||
|
||||
revertAction->setEnabled(true);
|
||||
|
||||
if (fileData.leftFileInfo.fileName == fileData.rightFileInfo.fileName)
|
||||
return;
|
||||
|
||||
applyAction->setEnabled(true);
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::slotSendChunkToCodePaster()
|
||||
{
|
||||
if (!m_controller)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
const QString patch = m_controller->makePatch(m_contextMenuFileIndex,
|
||||
m_contextMenuChunkIndex,
|
||||
false);
|
||||
|
||||
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()
|
||||
{
|
||||
patch(m_contextMenuFileIndex, m_contextMenuChunkIndex, false);
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::slotRevertChunk()
|
||||
{
|
||||
patch(m_contextMenuFileIndex, m_contextMenuChunkIndex, true);
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::patch(int diffFileIndex, int chunkIndex, bool revert)
|
||||
{
|
||||
if (!m_controller)
|
||||
return;
|
||||
|
||||
if (diffFileIndex < 0 || chunkIndex < 0)
|
||||
return;
|
||||
|
||||
if (diffFileIndex >= m_contextFileData.count())
|
||||
return;
|
||||
|
||||
const FileData fileData = m_contextFileData.at(diffFileIndex);
|
||||
if (chunkIndex >= fileData.chunks.count())
|
||||
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;
|
||||
}
|
||||
|
||||
const int strip = m_controller->workingDirectory().isEmpty() ? -1 : 0;
|
||||
|
||||
const QString fileName = revert
|
||||
? fileData.rightFileInfo.fileName
|
||||
: fileData.leftFileInfo.fileName;
|
||||
|
||||
const QString workingDirectory = m_controller->workingDirectory().isEmpty()
|
||||
? QFileInfo(fileName).absolutePath()
|
||||
: m_controller->workingDirectory();
|
||||
|
||||
const QString patch = m_controller->makePatch(diffFileIndex,
|
||||
chunkIndex,
|
||||
revert);
|
||||
if (patch.isEmpty())
|
||||
return;
|
||||
|
||||
if (PatchTool::runPatch(
|
||||
Core::EditorManager::defaultTextCodec()->fromUnicode(patch),
|
||||
workingDirectory, strip, revert))
|
||||
m_controller->requestReload();
|
||||
}
|
||||
|
||||
TextEditor::BaseTextEditor *UnifiedDiffEditorWidget::createEditor()
|
||||
{
|
||||
return new UnifiedDiffEditor(this);
|
||||
}
|
||||
|
||||
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();
|
||||
setPlainText(message);
|
||||
m_ignoreCurrentIndexChange = oldIgnore;
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::clearAll(const QString &message)
|
||||
{
|
||||
setDiff(QList<FileData>(), QString());
|
||||
clear(message);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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
|
||||
if (rowData.equal) {
|
||||
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)
|
||||
+ QLatin1String(" @@\n");
|
||||
|
||||
diffText.prepend(chunkLine);
|
||||
|
||||
*blockNumber += blockCount + 1; // +1 for chunk line
|
||||
*charNumber += charCount + chunkLine.count();
|
||||
return diffText;
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::showDiff()
|
||||
{
|
||||
clear(tr("No difference"));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (diffText.isEmpty())
|
||||
return;
|
||||
|
||||
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();
|
||||
const int columnNumber = cursor.positionInBlock() - 1; // -1 for the first character in line
|
||||
|
||||
const int rightLineNumber = m_rightLineNumbers.value(blockNumber, -1);
|
||||
if (rightLineNumber >= 0) {
|
||||
jumpToOriginalFile(m_contextFileData.at(
|
||||
fileIndexForBlockNumber(blockNumber)).rightFileInfo.fileName,
|
||||
rightLineNumber, columnNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
const int leftLineNumber = m_leftLineNumbers.value(blockNumber, -1);
|
||||
if (leftLineNumber >= 0) {
|
||||
jumpToOriginalFile(m_contextFileData.at(
|
||||
fileIndexForBlockNumber(blockNumber)).leftFileInfo.fileName,
|
||||
leftLineNumber, columnNumber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UnifiedDiffEditorWidget::jumpToOriginalFile(const QString &fileName,
|
||||
int lineNumber,
|
||||
int columnNumber)
|
||||
{
|
||||
if (!m_controller)
|
||||
return;
|
||||
|
||||
const QDir dir(m_controller->workingDirectory());
|
||||
const QString absoluteFileName = dir.absoluteFilePath(fileName);
|
||||
Core::EditorManager::openEditorAt(absoluteFileName, lineNumber, columnNumber);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace DiffEditor
|
||||
|
||||
#include "unifieddiffeditorwidget.moc"
|
||||
146
src/plugins/diffeditor/unifieddiffeditorwidget.h
Normal file
146
src/plugins/diffeditor/unifieddiffeditorwidget.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef UNIFIEDDIFFEDITORWIDGET_H
|
||||
#define UNIFIEDDIFFEDITORWIDGET_H
|
||||
|
||||
#include "diffeditor_global.h"
|
||||
#include "differ.h"
|
||||
#include "diffeditorcontroller.h"
|
||||
#include "selectabletexteditorwidget.h"
|
||||
|
||||
namespace TextEditor {
|
||||
class DisplaySettings;
|
||||
class FontSettings;
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSplitter;
|
||||
class QTextCharFormat;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace DiffEditor {
|
||||
|
||||
class DiffEditorGuiController;
|
||||
class ChunkData;
|
||||
class FileData;
|
||||
|
||||
class DIFFEDITOR_EXPORT UnifiedDiffEditorWidget
|
||||
: public SelectableTextEditorWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
UnifiedDiffEditorWidget(QWidget *parent = 0);
|
||||
~UnifiedDiffEditorWidget();
|
||||
|
||||
void setDiffEditorGuiController(DiffEditorGuiController *controller);
|
||||
DiffEditorGuiController *diffEditorGuiController() const;
|
||||
|
||||
public slots:
|
||||
void setDisplaySettings(const TextEditor::DisplaySettings &ds);
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent *e);
|
||||
void contextMenuEvent(QContextMenuEvent *e);
|
||||
TextEditor::BaseTextEditor *createEditor();
|
||||
QString lineNumber(int blockNumber) const;
|
||||
int lineNumberDigits() const;
|
||||
|
||||
private slots:
|
||||
void clear(const QString &message = QString());
|
||||
void clearAll(const QString &message = QString());
|
||||
void setDiff(const QList<FileData> &diffFileList,
|
||||
const QString &workingDirectory);
|
||||
|
||||
void setCurrentDiffFileIndex(int diffFileIndex);
|
||||
|
||||
void setFontSettings(const TextEditor::FontSettings &fontSettings);
|
||||
|
||||
void slotCursorPositionChangedInEditor();
|
||||
|
||||
void slotSendChunkToCodePaster();
|
||||
void slotApplyChunk();
|
||||
void slotRevertChunk();
|
||||
|
||||
private:
|
||||
void setLeftLineNumber(int blockNumber, int lineNumber);
|
||||
void setRightLineNumber(int blockNumber, int lineNumber);
|
||||
void setFileInfo(int blockNumber,
|
||||
const DiffFileInfo &leftFileInfo,
|
||||
const DiffFileInfo &rightFileInfo);
|
||||
void setChunkIndex(int startBlockNumber, int blockCount, int chunkIndex);
|
||||
void showDiff();
|
||||
QString showChunk(const ChunkData &chunkData,
|
||||
bool lastChunk,
|
||||
int *blockNumber,
|
||||
int *charNumber,
|
||||
QMap<int, QList<DiffSelection> > *selections);
|
||||
int blockNumberForFileIndex(int fileIndex) const;
|
||||
int fileIndexForBlockNumber(int blockNumber) const;
|
||||
int chunkIndexForBlockNumber(int blockNumber) const;
|
||||
void jumpToOriginalFile(const QTextCursor &cursor);
|
||||
void jumpToOriginalFile(const QString &fileName,
|
||||
int lineNumber,
|
||||
int columnNumber);
|
||||
void addContextMenuActions(QMenu *menu,
|
||||
int diffFileIndex,
|
||||
int chunkIndex);
|
||||
void patch(int diffFileIndex, int chunkIndex, bool revert);
|
||||
|
||||
DiffEditorGuiController *m_guiController;
|
||||
DiffEditorController *m_controller;
|
||||
|
||||
// block number, visual line number.
|
||||
QMap<int, int> m_leftLineNumbers;
|
||||
QMap<int, int> m_rightLineNumbers;
|
||||
bool m_ignoreCurrentIndexChange;
|
||||
int m_contextMenuFileIndex;
|
||||
int m_contextMenuChunkIndex;
|
||||
|
||||
int m_leftLineNumberDigits;
|
||||
int m_rightLineNumberDigits;
|
||||
// block number, visual line number.
|
||||
QMap<int, QPair<DiffFileInfo, DiffFileInfo> > m_fileInfo;
|
||||
// start block number, block count of a chunk, chunk index inside a file.
|
||||
QMap<int, QPair<int, int> > m_chunkInfo;
|
||||
|
||||
QList<FileData> m_contextFileData; // ultimate data to be shown
|
||||
// contextLinesNumber taken into account
|
||||
|
||||
QTextCharFormat m_fileLineFormat;
|
||||
QTextCharFormat m_chunkLineFormat;
|
||||
QTextCharFormat m_leftLineFormat;
|
||||
QTextCharFormat m_rightLineFormat;
|
||||
QTextCharFormat m_leftCharFormat;
|
||||
QTextCharFormat m_rightCharFormat;
|
||||
};
|
||||
|
||||
} // namespace DiffEditor
|
||||
|
||||
#endif // UNIFIEDDIFFEDITORWIDGET_H
|
||||
Reference in New Issue
Block a user