summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjkobus <jaroslaw.kobus@digia.com>2014-02-13 16:43:28 +0100
committerJarek Kobus <jaroslaw.kobus@digia.com>2014-06-06 12:33:59 +0200
commit8cad94534f97849081bd595da458670d168d6b63 (patch)
tree5575aab843ad47d38a30d5bbc38f1449254eaf4e
parent8cb25f9e3e1afcc1ccfa17966cdcc31a95c02289 (diff)
downloadqt-creator-8cad94534f97849081bd595da458670d168d6b63.tar.gz
Implement unified diff editor
Change-Id: I93e0bfd71a8a650afbe2ca9e0f1f3dbfc9d57db0 Reviewed-by: Jarek Kobus <jaroslaw.kobus@digia.com>
-rw-r--r--src/plugins/coreplugin/core.qrc3
-rw-r--r--src/plugins/coreplugin/coreconstants.h3
-rw-r--r--src/plugins/coreplugin/patchtool.cpp4
-rw-r--r--src/plugins/diffeditor/diffeditor.cpp272
-rw-r--r--src/plugins/diffeditor/diffeditor.h24
-rw-r--r--src/plugins/diffeditor/diffeditor.pro12
-rw-r--r--src/plugins/diffeditor/diffeditor.qbs5
-rw-r--r--src/plugins/diffeditor/diffeditor.qrc8
-rw-r--r--src/plugins/diffeditor/diffeditorconstants.h6
-rw-r--r--src/plugins/diffeditor/diffeditorcontroller.cpp105
-rw-r--r--src/plugins/diffeditor/diffeditorcontroller.h46
-rw-r--r--src/plugins/diffeditor/diffeditorfactory.cpp1
-rw-r--r--src/plugins/diffeditor/diffeditorguicontroller.cpp73
-rw-r--r--src/plugins/diffeditor/diffeditorguicontroller.h11
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.cpp496
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.h9
-rw-r--r--src/plugins/diffeditor/diffeditorreloader.cpp98
-rw-r--r--src/plugins/diffeditor/diffeditorreloader.h72
-rw-r--r--src/plugins/diffeditor/differ.cpp141
-rw-r--r--src/plugins/diffeditor/differ.h4
-rw-r--r--src/plugins/diffeditor/diffutils.cpp785
-rw-r--r--src/plugins/diffeditor/diffutils.h78
-rw-r--r--src/plugins/diffeditor/images/reload.pngbin0 -> 250 bytes
-rw-r--r--src/plugins/diffeditor/images/sidebysidediff.png (renamed from src/plugins/coreplugin/images/sidebysidediff.png)bin257 -> 257 bytes
-rw-r--r--src/plugins/diffeditor/images/topbar.png (renamed from src/plugins/coreplugin/images/topbaricon.png)bin379 -> 379 bytes
-rw-r--r--src/plugins/diffeditor/images/unifieddiff.png (renamed from src/plugins/coreplugin/images/textdiff.png)bin258 -> 258 bytes
-rw-r--r--src/plugins/diffeditor/selectabletexteditorwidget.cpp125
-rw-r--r--src/plugins/diffeditor/selectabletexteditorwidget.h80
-rw-r--r--src/plugins/diffeditor/sidebysidediffeditorwidget.cpp837
-rw-r--r--src/plugins/diffeditor/sidebysidediffeditorwidget.h53
-rw-r--r--src/plugins/diffeditor/unifieddiffeditorwidget.cpp783
-rw-r--r--src/plugins/diffeditor/unifieddiffeditorwidget.h146
-rw-r--r--src/plugins/git/branchdialog.cpp2
-rw-r--r--src/plugins/git/gerrit/gerritplugin.cpp2
-rw-r--r--src/plugins/git/gitclient.cpp1171
-rw-r--r--src/plugins/git/gitclient.h16
-rw-r--r--src/plugins/git/gitsettings.cpp2
-rw-r--r--src/plugins/git/gitsettings.h1
-rw-r--r--src/plugins/vcsbase/basevcseditorfactory.cpp8
39 files changed, 4058 insertions, 1424 deletions
diff --git a/src/plugins/coreplugin/core.qrc b/src/plugins/coreplugin/core.qrc
index 9ce5a5bf5b..8032e35c7d 100644
--- a/src/plugins/coreplugin/core.qrc
+++ b/src/plugins/coreplugin/core.qrc
@@ -42,7 +42,6 @@
<file>images/replace.png</file>
<file>images/reset.png</file>
<file>images/sidebaricon.png</file>
- <file>images/topbaricon.png</file>
<file>images/splitbutton_horizontal.png</file>
<file>images/splitbutton_horizontal@2x.png</file>
<file>images/statusbar.png</file>
@@ -89,8 +88,6 @@
<file>images/splitbutton_vertical.png</file>
<file>images/splitbutton_vertical@2x.png</file>
<file>images/panel_manage_button.png</file>
- <file>images/sidebysidediff.png</file>
- <file>images/textdiff.png</file>
<file>images/pause.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h
index 33b5febb4a..9d531139d4 100644
--- a/src/plugins/coreplugin/coreconstants.h
+++ b/src/plugins/coreplugin/coreconstants.h
@@ -197,7 +197,6 @@ const char ICON_CLEAR[] = ":/core/images/clear.png";
const char ICON_RESET[] = ":/core/images/reset.png";
const char ICON_MAGNIFIER[] = ":/core/images/magnifier.png";
const char ICON_TOGGLE_SIDEBAR[] = ":/core/images/sidebaricon.png";
-const char ICON_TOGGLE_TOPBAR[] = ":/core/images/topbaricon.png";
const char ICON_CLOSE_DOCUMENT[] = ":/core/images/button_close.png";
const char ICON_CLOSE[] = ":/core/images/closebutton.png";
const char ICON_CLOSE_DARK[] = ":/core/images/darkclosebutton.png";
@@ -207,8 +206,6 @@ const char ICON_CLOSE_SPLIT_TOP[] = ":/core/images/splitbutton_closetop.png";
const char ICON_CLOSE_SPLIT_BOTTOM[] = ":/core/images/splitbutton_closebottom.png";
const char ICON_CLOSE_SPLIT_LEFT[] = ":/core/images/splitbutton_closeleft.png";
const char ICON_CLOSE_SPLIT_RIGHT[] = ":/core/images/splitbutton_closeright.png";
-const char ICON_SIDE_BY_SIDE_DIFF[] = ":/core/images/sidebysidediff.png";
-const char ICON_TEXT_DIFF[] = ":/core/images/textdiff.png";
const char ICON_FILTER[] = ":/core/images/filtericon.png";
const char ICON_LINK[] = ":/core/images/linkicon.png";
const char ICON_PAUSE[] = ":/core/images/pause.png";
diff --git a/src/plugins/coreplugin/patchtool.cpp b/src/plugins/coreplugin/patchtool.cpp
index 90c38d7102..83e2ce7104 100644
--- a/src/plugins/coreplugin/patchtool.cpp
+++ b/src/plugins/coreplugin/patchtool.cpp
@@ -93,7 +93,9 @@ bool PatchTool::runPatch(const QByteArray &input, const QString &workingDirector
QProcess patchProcess;
if (!workingDirectory.isEmpty())
patchProcess.setWorkingDirectory(workingDirectory);
- QStringList args(QLatin1String("-p") + QString::number(strip));
+ QStringList args;
+ if (strip >= 0)
+ args << (QLatin1String("-p") + QString::number(strip));
if (reverse)
args << QLatin1String("-R");
MessageManager::write(QApplication::translate("Core::PatchTool", "Executing in %1: %2 %3").
diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp
index 875af4ddb6..b586aeeec7 100644
--- a/src/plugins/diffeditor/diffeditor.cpp
+++ b/src/plugins/diffeditor/diffeditor.cpp
@@ -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
diff --git a/src/plugins/diffeditor/diffeditor.h b/src/plugins/diffeditor/diffeditor.h
index 4c982ab13d..cd09ee1e46 100644
--- a/src/plugins/diffeditor/diffeditor.h
+++ b/src/plugins/diffeditor/diffeditor.h
@@ -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
diff --git a/src/plugins/diffeditor/diffeditor.pro b/src/plugins/diffeditor/diffeditor.pro
index d015f3d1b4..801abc25c4 100644
--- a/src/plugins/diffeditor/diffeditor.pro
+++ b/src/plugins/diffeditor/diffeditor.pro
@@ -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
diff --git a/src/plugins/diffeditor/diffeditor.qbs b/src/plugins/diffeditor/diffeditor.qbs
index e561e1cd9c..4746e3346c 100644
--- a/src/plugins/diffeditor/diffeditor.qbs
+++ b/src/plugins/diffeditor/diffeditor.qbs
@@ -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",
]
}
diff --git a/src/plugins/diffeditor/diffeditor.qrc b/src/plugins/diffeditor/diffeditor.qrc
new file mode 100644
index 0000000000..e7c06f78ee
--- /dev/null
+++ b/src/plugins/diffeditor/diffeditor.qrc
@@ -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>
diff --git a/src/plugins/diffeditor/diffeditorconstants.h b/src/plugins/diffeditor/diffeditorconstants.h
index a19528ce15..ef07b4c168 100644
--- a/src/plugins/diffeditor/diffeditorconstants.h
+++ b/src/plugins/diffeditor/diffeditorconstants.h
@@ -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
diff --git a/src/plugins/diffeditor/diffeditorcontroller.cpp b/src/plugins/diffeditor/diffeditorcontroller.cpp
index fae78efbcb..b815961cf1 100644
--- a/src/plugins/diffeditor/diffeditorcontroller.cpp
+++ b/src/plugins/diffeditor/diffeditorcontroller.cpp
@@ -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
diff --git a/src/plugins/diffeditor/diffeditorcontroller.h b/src/plugins/diffeditor/diffeditorcontroller.h
index 97aee8c118..cd632cafef 100644
--- a/src/plugins/diffeditor/diffeditorcontroller.h
+++ b/src/plugins/diffeditor/diffeditorcontroller.h
@@ -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
diff --git a/src/plugins/diffeditor/diffeditorfactory.cpp b/src/plugins/diffeditor/diffeditorfactory.cpp
index 25c873654e..b0b23b2771 100644
--- a/src/plugins/diffeditor/diffeditorfactory.cpp
+++ b/src/plugins/diffeditor/diffeditorfactory.cpp
@@ -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()
diff --git a/src/plugins/diffeditor/diffeditorguicontroller.cpp b/src/plugins/diffeditor/diffeditorguicontroller.cpp
index a24cd8ae98..026feb7a95 100644
--- a/src/plugins/diffeditor/diffeditorguicontroller.cpp
+++ b/src/plugins/diffeditor/diffeditorguicontroller.cpp
@@ -30,19 +30,35 @@
#include "diffeditorguicontroller.h"
#include "diffeditorcontroller.h"
+#include <coreplugin/icore.h>
+
+static const char settingsGroupC[] = "DiffEditor";
+static const char descriptionVisibleKeyC[] = "DescriptionVisible";
+static const char horizontalScrollBarSynchronizationKeyC[] =
+ "HorizontalScrollBarSynchronization";
+
namespace DiffEditor {
-DiffEditorGuiController::DiffEditorGuiController(DiffEditorController *controller, QObject *parent)
+DiffEditorGuiController::DiffEditorGuiController(
+ DiffEditorController *controller,
+ QObject *parent)
: QObject(parent),
m_controller(controller),
m_descriptionVisible(true),
- m_contextLinesNumber(3),
- m_ignoreWhitespaces(true),
m_syncScrollBars(true),
m_currentDiffFileIndex(-1)
{
- connect(m_controller, SIGNAL(cleared(QString)), this, SLOT(slotUpdateDiffFileIndex()));
- connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),
+ QSettings *s = Core::ICore::settings();
+ s->beginGroup(QLatin1String(settingsGroupC));
+ m_descriptionVisible = s->value(QLatin1String(descriptionVisibleKeyC),
+ m_descriptionVisible).toBool();
+ m_syncScrollBars = s->value(QLatin1String(horizontalScrollBarSynchronizationKeyC),
+ m_syncScrollBars).toBool();
+ s->endGroup();
+
+ connect(m_controller, SIGNAL(cleared(QString)),
+ this, SLOT(slotUpdateDiffFileIndex()));
+ connect(m_controller, SIGNAL(diffFilesChanged(QList<FileData>,QString)),
this, SLOT(slotUpdateDiffFileIndex()));
slotUpdateDiffFileIndex();
}
@@ -62,16 +78,6 @@ bool DiffEditorGuiController::isDescriptionVisible() const
return m_descriptionVisible;
}
-int DiffEditorGuiController::contextLinesNumber() const
-{
- return m_contextLinesNumber;
-}
-
-bool DiffEditorGuiController::isIgnoreWhitespaces() const
-{
- return m_ignoreWhitespaces;
-}
-
bool DiffEditorGuiController::horizontalScrollBarSynchronization() const
{
return m_syncScrollBars;
@@ -84,7 +90,7 @@ int DiffEditorGuiController::currentDiffFileIndex() const
void DiffEditorGuiController::slotUpdateDiffFileIndex()
{
- m_currentDiffFileIndex = (m_controller->diffContents().isEmpty() ? -1 : 0);
+ m_currentDiffFileIndex = (m_controller->diffFiles().isEmpty() ? -1 : 0);
}
void DiffEditorGuiController::setDescriptionVisible(bool on)
@@ -93,26 +99,13 @@ void DiffEditorGuiController::setDescriptionVisible(bool on)
return;
m_descriptionVisible = on;
- emit descriptionVisibilityChanged(on);
-}
-void DiffEditorGuiController::setContextLinesNumber(int lines)
-{
- const int l = qMax(lines, -1);
- if (m_contextLinesNumber == l)
- return;
-
- m_contextLinesNumber = l;
- emit contextLinesNumberChanged(l);
-}
-
-void DiffEditorGuiController::setIgnoreWhitespaces(bool ignore)
-{
- if (m_ignoreWhitespaces == ignore)
- return;
+ QSettings *s = Core::ICore::settings();
+ s->beginGroup(QLatin1String(settingsGroupC));
+ s->setValue(QLatin1String(descriptionVisibleKeyC), m_descriptionVisible);
+ s->endGroup();
- m_ignoreWhitespaces = ignore;
- emit ignoreWhitespacesChanged(ignore);
+ emit descriptionVisibilityChanged(on);
}
void DiffEditorGuiController::setHorizontalScrollBarSynchronization(bool on)
@@ -121,15 +114,23 @@ void DiffEditorGuiController::setHorizontalScrollBarSynchronization(bool on)
return;
m_syncScrollBars = on;
+
+ QSettings *s = Core::ICore::settings();
+ s->beginGroup(QLatin1String(settingsGroupC));
+ s->setValue(QLatin1String(horizontalScrollBarSynchronizationKeyC),
+ m_syncScrollBars);
+ s->endGroup();
+
emit horizontalScrollBarSynchronizationChanged(on);
}
void DiffEditorGuiController::setCurrentDiffFileIndex(int diffFileIndex)
{
- if (m_controller->diffContents().isEmpty())
+ if (m_controller->diffFiles().isEmpty())
return; // -1 is the only valid value in this case
- const int newIndex = qBound(0, diffFileIndex, m_controller->diffContents().count() - 1);
+ const int newIndex = qBound(0, diffFileIndex,
+ m_controller->diffFiles().count() - 1);
if (m_currentDiffFileIndex == newIndex)
return;
diff --git a/src/plugins/diffeditor/diffeditorguicontroller.h b/src/plugins/diffeditor/diffeditorguicontroller.h
index a140a829b7..098ca0ef4a 100644
--- a/src/plugins/diffeditor/diffeditorguicontroller.h
+++ b/src/plugins/diffeditor/diffeditorguicontroller.h
@@ -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;
};
diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp
index 1a9f5c2bc7..d15ab4a8b7 100644
--- a/src/plugins/diffeditor/diffeditorplugin.cpp
+++ b/src/plugins/diffeditor/diffeditorplugin.cpp
@@ -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);
+ document->controller()->requestReload();
}
-QString DiffEditorPlugin::getFileContents(const QString &fileName) const
+} // 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()
{
- QFile file(fileName);
- if (file.open(QIODevice::ReadOnly | QIODevice::Text))
- return Core::EditorManager::defaultTextCodec()->toUnicode(file.readAll());
- return QString();
+ 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;
}
-} // namespace Internal
-} // namespace DiffEditor
+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"
diff --git a/src/plugins/diffeditor/diffeditorplugin.h b/src/plugins/diffeditor/diffeditorplugin.h
index ba347213af..40eccd47d6 100644
--- a/src/plugins/diffeditor/diffeditorplugin.h
+++ b/src/plugins/diffeditor/diffeditorplugin.h
@@ -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
diff --git a/src/plugins/diffeditor/diffeditorreloader.cpp b/src/plugins/diffeditor/diffeditorreloader.cpp
new file mode 100644
index 0000000000..77087c1bda
--- /dev/null
+++ b/src/plugins/diffeditor/diffeditorreloader.cpp
@@ -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
diff --git a/src/plugins/diffeditor/diffeditorreloader.h b/src/plugins/diffeditor/diffeditorreloader.h
new file mode 100644
index 0000000000..962902fb9d
--- /dev/null
+++ b/src/plugins/diffeditor/diffeditorreloader.h
@@ -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
diff --git a/src/plugins/diffeditor/differ.cpp b/src/plugins/diffeditor/differ.cpp
index e6c435bef9..aa396dfe8c 100644
--- a/src/plugins/diffeditor/differ.cpp
+++ b/src/plugins/diffeditor/differ.cpp
@@ -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();
diff --git a/src/plugins/diffeditor/differ.h b/src/plugins/diffeditor/differ.h
index 5ec2679ba7..9c9054ff02 100644
--- a/src/plugins/diffeditor/differ.h
+++ b/src/plugins/diffeditor/differ.h
@@ -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,
diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp
index ed15a283d3..072d43a18d 100644
--- a/src/plugins/diffeditor/diffutils.cpp
+++ b/src/plugins/diffeditor/diffutils.cpp
@@ -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);
-
- 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 (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;
+
+ 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 (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);
+ }
+
+ if (line == commonLineCount - 1) {
+ // omit alignment when last lines of equalities are empty
+ if (leftLines.last().text.isEmpty()
+ || rightLines.last().text.isEmpty())
+ 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 (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);
- }
+ // 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;
+ if (line > 0)
+ lastLineEqual = true;
- line++;
+ 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)
+{
+ 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;
+}
+
+QString DiffUtils::makePatch(const ChunkData &chunkData,
+ const QString &leftFileName,
+ const QString &rightFileName,
+ bool lastChunk)
+{
+ QString diffText;
+ int leftLineCount = 0;
+ int rightLineCount = 0;
+ QList<TextLineData> leftBuffer, rightBuffer;
+
+ 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);
+
+ if (!line.isEmpty())
+ ++leftLineCount;
+
+ 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);
+ }
+ }
+
+ 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;
+}
+
+static QList<RowData> readLines(const QString &patch,
+ bool ignoreWhitespace,
+ bool lastChunk,
+ bool *lastChunkAtTheEndOfFile,
+ bool *ok)
+{
+// 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)
{
- 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);
+ 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;
}
-QList<QTextEdit::ExtraSelection> colorPositions(
- const QTextCharFormat &format,
- QTextCursor &cursor,
- const QMap<int, int> &positions)
+static FileData readGitHeaderAndChunks(const QString &headerAndChunks,
+ const QString &fileName,
+ bool ignoreWhitespace,
+ bool *ok)
{
- QList<QTextEdit::ExtraSelection> lineSelections;
+ 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;
- cursor.setPosition(0);
- QMapIterator<int, int> itPositions(positions);
- while (itPositions.hasNext()) {
- itPositions.next();
+ if (!readOk)
+ return FileData();
+
+ return fileData;
+}
- cursor.setPosition(itPositions.key());
- cursor.setPosition(itPositions.value(), QTextCursor::KeepAnchor);
+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;
- QTextEdit::ExtraSelection selection;
- selection.cursor = cursor;
- selection.format = format;
- lineSelections.append(selection);
+ 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);
+ }
}
- return lineSelections;
+
+ if (ok)
+ *ok = readOk;
+
+ if (!readOk)
+ return QList<FileData>();
+
+ return fileDataList;
}
-QTextCharFormat fullWidthFormatForTextStyle(const TextEditor::FontSettings &fontSettings,
- TextEditor::TextStyle textStyle)
+QList<FileData> DiffUtils::readPatch(const QString &patch, bool ignoreWhitespace, bool *ok)
{
- QTextCharFormat format = fontSettings.toTextCharFormat(textStyle);
- format.setProperty(QTextFormat::FullWidthSelection, true);
- return format;
+ 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
diff --git a/src/plugins/diffeditor/diffutils.h b/src/plugins/diffeditor/diffutils.h
index 75102c7406..9043969929 100644
--- a/src/plugins/diffeditor/diffutils.h
+++ b/src/plugins/diffeditor/diffutils.h
@@ -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
diff --git a/src/plugins/diffeditor/images/reload.png b/src/plugins/diffeditor/images/reload.png
new file mode 100644
index 0000000000..bd07fb34b0
--- /dev/null
+++ b/src/plugins/diffeditor/images/reload.png
Binary files differ
diff --git a/src/plugins/coreplugin/images/sidebysidediff.png b/src/plugins/diffeditor/images/sidebysidediff.png
index 55c6f2802d..55c6f2802d 100644
--- a/src/plugins/coreplugin/images/sidebysidediff.png
+++ b/src/plugins/diffeditor/images/sidebysidediff.png
Binary files differ
diff --git a/src/plugins/coreplugin/images/topbaricon.png b/src/plugins/diffeditor/images/topbar.png
index 40d0fd842e..40d0fd842e 100644
--- a/src/plugins/coreplugin/images/topbaricon.png
+++ b/src/plugins/diffeditor/images/topbar.png
Binary files differ
diff --git a/src/plugins/coreplugin/images/textdiff.png b/src/plugins/diffeditor/images/unifieddiff.png
index 254b187744..254b187744 100644
--- a/src/plugins/coreplugin/images/textdiff.png
+++ b/src/plugins/diffeditor/images/unifieddiff.png
Binary files differ
diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.cpp b/src/plugins/diffeditor/selectabletexteditorwidget.cpp
new file mode 100644
index 0000000000..91e31b0e8c
--- /dev/null
+++ b/src/plugins/diffeditor/selectabletexteditorwidget.cpp
@@ -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
+
diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.h b/src/plugins/diffeditor/selectabletexteditorwidget.h
new file mode 100644
index 0000000000..0c0f2d9af9
--- /dev/null
+++ b/src/plugins/diffeditor/selectabletexteditorwidget.h
@@ -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
diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
index 9f0c7e8604..32d8ce73eb 100644
--- a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
+++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
@@ -28,8 +28,10 @@
****************************************************************************/
#include "sidebysidediffeditorwidget.h"
+#include "selectabletexteditorwidget.h"
#include "diffeditorguicontroller.h"
#include "diffutils.h"
+#include "diffeditorconstants.h"
#include <QPlainTextEdit>
#include <QVBoxLayout>
@@ -39,6 +41,8 @@
#include <QPainter>
#include <QDir>
#include <QToolButton>
+#include <QTextCodec>
+#include <QMessageBox>
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextdocumentlayout.h>
@@ -53,6 +57,7 @@
#include <coreplugin/icore.h>
#include <coreplugin/minisplitter.h>
#include <coreplugin/mimedatabase.h>
+#include <coreplugin/patchtool.h>
#include <extensionsystem/pluginmanager.h>
@@ -67,8 +72,6 @@ using namespace TextEditor;
namespace DiffEditor {
-using namespace Internal;
-
//////////////////////
class SideDiffEditor : public BaseTextEditor
@@ -84,12 +87,14 @@ public:
}
private slots:
- void slotTooltipRequested(TextEditor::ITextEditor *editor, const QPoint &globalPoint, int position);
+ void slotTooltipRequested(TextEditor::ITextEditor *editor,
+ const QPoint &globalPoint,
+ int position);
};
////////////////////////
-
+/*
class MultiHighlighter : public SyntaxHighlighter
{
Q_OBJECT
@@ -98,7 +103,7 @@ public:
~MultiHighlighter();
virtual void setFontSettings(const TextEditor::FontSettings &fontSettings);
- void setDocuments(const QList<QPair<DiffEditorController::DiffFileInfo, QString> > &documents);
+ void setDocuments(const QList<QPair<DiffFileInfo, QString> > &documents);
protected:
virtual void highlightBlock(const QString &text);
@@ -109,36 +114,43 @@ private:
QList<SyntaxHighlighter *> m_highlighters;
QList<QTextDocument *> m_documents;
};
-
+*/
////////////////////////
-class SideDiffEditorWidget : public BaseTextEditorWidget
+class SideDiffEditorWidget : public SelectableTextEditorWidget
{
Q_OBJECT
public:
- class ExtendedFileInfo {
- public:
- DiffEditorController::DiffFileInfo fileInfo;
- TextEditor::SyntaxHighlighter *highlighter;
- };
-
SideDiffEditorWidget(QWidget *parent = 0);
// block number, file info
- QMap<int, DiffEditorController::DiffFileInfo> fileInfo() const { return m_fileInfo; }
+ QMap<int, DiffFileInfo> fileInfo() const { return m_fileInfo; }
void setLineNumber(int blockNumber, int lineNumber);
- void setFileInfo(int blockNumber, const DiffEditorController::DiffFileInfo &fileInfo);
- void setSkippedLines(int blockNumber, int skippedLines) { m_skippedLines[blockNumber] = skippedLines; setSeparator(blockNumber, true); }
- void setSeparator(int blockNumber, bool separator) { m_separators[blockNumber] = separator; }
- bool isFileLine(int blockNumber) const { return m_fileInfo.contains(blockNumber); }
+ void setFileInfo(int blockNumber, const DiffFileInfo &fileInfo);
+ void setSkippedLines(int blockNumber, int skippedLines) {
+ m_skippedLines[blockNumber] = skippedLines;
+ setSeparator(blockNumber, true);
+ }
+ void setChunkIndex(int startBlockNumber, int blockCount, int chunkIndex);
+ void setSeparator(int blockNumber, bool separator) {
+ m_separators[blockNumber] = separator;
+ }
+ bool isFileLine(int blockNumber) const {
+ return m_fileInfo.contains(blockNumber);
+ }
int blockNumberForFileIndex(int fileIndex) const;
int fileIndexForBlockNumber(int blockNumber) const;
- bool isChunkLine(int blockNumber) const { return m_skippedLines.contains(blockNumber); }
+ int chunkIndexForBlockNumber(int blockNumber) const;
+ bool isChunkLine(int blockNumber) const {
+ return m_skippedLines.contains(blockNumber);
+ }
void clearAll(const QString &message);
void clearAllData();
- QTextBlock firstVisibleBlock() const { return BaseTextEditorWidget::firstVisibleBlock(); }
- void setDocuments(const QList<QPair<DiffEditorController::DiffFileInfo, QString> > &documents);
+ QTextBlock firstVisibleBlock() const {
+ return BaseTextEditorWidget::firstVisibleBlock();
+ }
+// void setDocuments(const QList<QPair<DiffFileInfo, QString> > &documents);
public slots:
void setDisplaySettings(const DisplaySettings &ds);
@@ -147,9 +159,14 @@ signals:
void jumpToOriginalFileRequested(int diffFileIndex,
int lineNumber,
int columnNumber);
+ void contextMenuRequested(QMenu *menu,
+ int diffFileIndex,
+ int chunkIndex);
protected:
- virtual int extraAreaWidth(int *markWidthPtr = 0) const { return BaseTextEditorWidget::extraAreaWidth(markWidthPtr); }
+ virtual int extraAreaWidth(int *markWidthPtr = 0) const {
+ return SelectableTextEditorWidget::extraAreaWidth(markWidthPtr);
+ }
void applyFontSettings();
BaseTextEditor *createEditor() { return new SideDiffEditor(this); }
virtual QString lineNumber(int blockNumber) const;
@@ -158,16 +175,17 @@ protected:
virtual bool replacementVisible(int blockNumber) const;
QColor replacementPenColor(int blockNumber) const;
virtual QString plainTextFromSelection(const QTextCursor &cursor) const;
- virtual void drawCollapsedBlockPopup(QPainter &painter,
- const QTextBlock &block,
- QPointF offset,
- const QRect &clip);
+// virtual void drawCollapsedBlockPopup(QPainter &painter,
+// const QTextBlock &block,
+// QPointF offset,
+// const QRect &clip);
void mouseDoubleClickEvent(QMouseEvent *e);
+ void contextMenuEvent(QContextMenuEvent *e);
virtual void paintEvent(QPaintEvent *e);
virtual void scrollContentsBy(int dx, int dy);
private:
- void paintCollapsedBlockPopup(QPainter &painter, const QRect &clipRect);
+// void paintCollapsedBlockPopup(QPainter &painter, const QRect &clipRect);
void paintSeparator(QPainter &painter, QColor &color, const QString &text,
const QTextBlock &block, int top);
void jumpToOriginalFile(const QTextCursor &cursor);
@@ -176,28 +194,32 @@ private:
QMap<int, int> m_lineNumbers;
int m_lineNumberDigits;
// block number, fileInfo. Set for file lines only.
- QMap<int, DiffEditorController::DiffFileInfo> m_fileInfo;
+ QMap<int, DiffFileInfo> m_fileInfo;
// block number, skipped lines. Set for chunk lines only.
QMap<int, int> m_skippedLines;
+ // start block number, block count of a chunk, chunk index inside a file.
+ QMap<int, QPair<int, int> > m_chunkInfo;
// block number, separator. Set for file, chunk or span line.
QMap<int, bool> m_separators;
bool m_inPaintEvent;
QColor m_fileLineForeground;
QColor m_chunkLineForeground;
QColor m_textForeground;
- MultiHighlighter *m_highlighter;
+// MultiHighlighter *m_highlighter;
};
////////////////////////
-void SideDiffEditor::slotTooltipRequested(TextEditor::ITextEditor *editor, const QPoint &globalPoint, int position)
+void SideDiffEditor::slotTooltipRequested(TextEditor::ITextEditor *editor,
+ const QPoint &globalPoint,
+ int position)
{
SideDiffEditorWidget *ew = qobject_cast<SideDiffEditorWidget *>(editorWidget());
if (!ew)
return;
- QMap<int, DiffEditorController::DiffFileInfo> fi = ew->fileInfo();
- QMap<int, DiffEditorController::DiffFileInfo>::const_iterator it
+ QMap<int, DiffFileInfo> fi = ew->fileInfo();
+ QMap<int, DiffFileInfo>::const_iterator it
= fi.constFind(ew->document()->findBlock(position).blockNumber());
if (it != fi.constEnd()) {
Utils::ToolTip::show(globalPoint, Utils::TextContent(it.value().fileName),
@@ -208,7 +230,7 @@ void SideDiffEditor::slotTooltipRequested(TextEditor::ITextEditor *editor, const
}
////////////////////////
-
+/*
MultiHighlighter::MultiHighlighter(SideDiffEditorWidget *editor, QTextDocument *document)
: SyntaxHighlighter(document),
m_editor(editor)
@@ -224,7 +246,7 @@ MultiHighlighter::MultiHighlighter(SideDiffEditorWidget *editor, QTextDocument *
MultiHighlighter::~MultiHighlighter()
{
- setDocuments(QList<QPair<DiffEditorController::DiffFileInfo, QString> >());
+ setDocuments(QList<QPair<DiffFileInfo, QString> >());
}
void MultiHighlighter::setFontSettings(const TextEditor::FontSettings &fontSettings)
@@ -237,7 +259,7 @@ void MultiHighlighter::setFontSettings(const TextEditor::FontSettings &fontSetti
}
}
-void MultiHighlighter::setDocuments(const QList<QPair<DiffEditorController::DiffFileInfo, QString> > &documents)
+void MultiHighlighter::setDocuments(const QList<QPair<DiffFileInfo, QString> > &documents)
{
// clear old documents
qDeleteAll(m_documents);
@@ -247,7 +269,7 @@ void MultiHighlighter::setDocuments(const QList<QPair<DiffEditorController::Diff
// create new documents
for (int i = 0; i < documents.count(); i++) {
- DiffEditorController::DiffFileInfo fileInfo = documents.at(i).first;
+ DiffFileInfo fileInfo = documents.at(i).first;
const QString contents = documents.at(i).second;
QTextDocument *document = new QTextDocument(contents);
const MimeType mimeType = MimeDatabase::findByFile(QFileInfo(fileInfo.fileName));
@@ -293,11 +315,13 @@ void MultiHighlighter::highlightBlock(const QString &text)
QList<QTextLayout::FormatRange> formats = documentBlock.layout()->additionalFormats();
setExtraAdditionalFormats(block, formats);
}
-
+*/
////////////////////////
SideDiffEditorWidget::SideDiffEditorWidget(QWidget *parent)
- : BaseTextEditorWidget(parent), m_lineNumberDigits(1), m_inPaintEvent(false)
+ : SelectableTextEditorWidget(parent),
+ m_lineNumberDigits(1),
+ m_inPaintEvent(false)
{
DisplaySettings settings = displaySettings();
settings.m_textWrapping = false;
@@ -306,25 +330,24 @@ SideDiffEditorWidget::SideDiffEditorWidget(QWidget *parent)
settings.m_displayFoldingMarkers = true;
settings.m_markTextChanges = false;
settings.m_highlightBlocks = false;
- BaseTextEditorWidget::setDisplaySettings(settings);
+ SelectableTextEditorWidget::setDisplaySettings(settings);
- setCodeFoldingSupported(true);
- setFrameStyle(QFrame::NoFrame);
+// setCodeFoldingSupported(true);
- m_highlighter = new MultiHighlighter(this, baseTextDocument()->document());
- baseTextDocument()->setSyntaxHighlighter(m_highlighter);
+// m_highlighter = new MultiHighlighter(this, baseTextDocument()->document());
+// baseTextDocument()->setSyntaxHighlighter(m_highlighter);
}
void SideDiffEditorWidget::setDisplaySettings(const DisplaySettings &ds)
{
DisplaySettings settings = displaySettings();
settings.m_visualizeWhitespace = ds.m_visualizeWhitespace;
- BaseTextEditorWidget::setDisplaySettings(settings);
+ SelectableTextEditorWidget::setDisplaySettings(settings);
}
void SideDiffEditorWidget::applyFontSettings()
{
- BaseTextEditorWidget::applyFontSettings();
+ SelectableTextEditorWidget::applyFontSettings();
const TextEditor::FontSettings &fs = baseTextDocument()->fontSettings();
m_fileLineForeground = fs.formatFor(C_DIFF_FILE_LINE).foreground();
m_chunkLineForeground = fs.formatFor(C_DIFF_CONTEXT_LINE).foreground();
@@ -352,7 +375,8 @@ bool SideDiffEditorWidget::selectionVisible(int blockNumber) const
bool SideDiffEditorWidget::replacementVisible(int blockNumber) const
{
return isChunkLine(blockNumber) || (isFileLine(blockNumber)
- && TextEditor::BaseTextDocumentLayout::isFolded(document()->findBlockByNumber(blockNumber)));
+ && TextEditor::BaseTextDocumentLayout::isFolded(
+ document()->findBlockByNumber(blockNumber)));
}
QColor SideDiffEditorWidget::replacementPenColor(int blockNumber) const
@@ -403,18 +427,23 @@ void SideDiffEditorWidget::setLineNumber(int blockNumber, int lineNumber)
m_lineNumberDigits = qMax(m_lineNumberDigits, lineNumberString.count());
}
-void SideDiffEditorWidget::setFileInfo(int blockNumber, const DiffEditorController::DiffFileInfo &fileInfo)
+void SideDiffEditorWidget::setFileInfo(int blockNumber, const DiffFileInfo &fileInfo)
{
m_fileInfo[blockNumber] = fileInfo;
setSeparator(blockNumber, true);
}
+void SideDiffEditorWidget::setChunkIndex(int startBlockNumber, int blockCount, int chunkIndex)
+{
+ m_chunkInfo.insert(startBlockNumber, qMakePair(blockCount, chunkIndex));
+}
+
int SideDiffEditorWidget::blockNumberForFileIndex(int fileIndex) const
{
if (fileIndex < 0 || fileIndex >= m_fileInfo.count())
return -1;
- QMap<int, DiffEditorController::DiffFileInfo>::const_iterator it
+ QMap<int, DiffFileInfo>::const_iterator it
= m_fileInfo.constBegin();
for (int i = 0; i < fileIndex; i++)
++it;
@@ -424,9 +453,9 @@ int SideDiffEditorWidget::blockNumberForFileIndex(int fileIndex) const
int SideDiffEditorWidget::fileIndexForBlockNumber(int blockNumber) const
{
- QMap<int, DiffEditorController::DiffFileInfo>::const_iterator it
+ QMap<int, DiffFileInfo>::const_iterator it
= m_fileInfo.constBegin();
- QMap<int, DiffEditorController::DiffFileInfo>::const_iterator itEnd
+ QMap<int, DiffFileInfo>::const_iterator itEnd
= m_fileInfo.constEnd();
int i = -1;
@@ -439,13 +468,33 @@ int SideDiffEditorWidget::fileIndexForBlockNumber(int blockNumber) const
return i;
}
+int SideDiffEditorWidget::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 SideDiffEditorWidget::clearAll(const QString &message)
{
setBlockSelection(false);
clear();
clearAllData();
+ setExtraSelections(BaseTextEditorWidget::OtherSelection,
+ QList<QTextEdit::ExtraSelection>());
setPlainText(message);
- m_highlighter->setDocuments(QList<QPair<DiffEditorController::DiffFileInfo, QString> >());
+// m_highlighter->setDocuments(QList<QPair<DiffFileInfo, QString> >());
}
void SideDiffEditorWidget::clearAllData()
@@ -454,17 +503,19 @@ void SideDiffEditorWidget::clearAllData()
m_lineNumbers.clear();
m_fileInfo.clear();
m_skippedLines.clear();
+ m_chunkInfo.clear();
m_separators.clear();
+ setSelections(QMap<int, QList<DiffSelection> >());
}
-
-void SideDiffEditorWidget::setDocuments(const QList<QPair<DiffEditorController::DiffFileInfo, QString> > &documents)
+/*
+void SideDiffEditorWidget::setDocuments(const QList<QPair<DiffFileInfo, QString> > &documents)
{
m_highlighter->setDocuments(documents);
}
-
+*/
void SideDiffEditorWidget::scrollContentsBy(int dx, int dy)
{
- BaseTextEditorWidget::scrollContentsBy(dx, dy);
+ SelectableTextEditorWidget::scrollContentsBy(dx, dy);
// TODO: update only chunk lines
viewport()->update();
}
@@ -491,8 +542,10 @@ void SideDiffEditorWidget::paintSeparator(QPainter &painter,
+ QLatin1String("}; ");
const int replacementTextWidth = fontMetrics().width(replacementText) + 24;
int x = replacementTextWidth + offset.x();
- if (x < document()->documentMargin() || !TextEditor::BaseTextDocumentLayout::isFolded(block))
+ if (x < document()->documentMargin()
+ || !TextEditor::BaseTextDocumentLayout::isFolded(block)) {
x = document()->documentMargin();
+ }
const QString elidedText = fontMetrics().elidedText(text,
Qt::ElideRight,
viewport()->width() - x);
@@ -515,7 +568,22 @@ void SideDiffEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
e->accept();
return;
}
- BaseTextEditorWidget::mouseDoubleClickEvent(e);
+ SelectableTextEditorWidget::mouseDoubleClickEvent(e);
+}
+
+void SideDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
+{
+ QPointer<QMenu> menu = createStandardContextMenu();
+
+ QTextCursor cursor = cursorForPosition(e->pos());
+ const int blockNumber = cursor.blockNumber();
+
+ emit contextMenuRequested(menu, fileIndexForBlockNumber(blockNumber),
+ chunkIndexForBlockNumber(blockNumber));
+
+ connect(this, SIGNAL(destroyed()), menu, SLOT(deleteLater()));
+ menu->exec(e->globalPos());
+ delete menu;
}
void SideDiffEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
@@ -530,16 +598,26 @@ void SideDiffEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
const int lineNumber = m_lineNumbers.value(blockNumber);
- emit jumpToOriginalFileRequested(fileIndexForBlockNumber(blockNumber), lineNumber, columnNumber);
+ emit jumpToOriginalFileRequested(fileIndexForBlockNumber(blockNumber),
+ lineNumber, columnNumber);
+}
+
+static QString skippedText(int skippedNumber)
+{
+ if (skippedNumber > 0)
+ return SideBySideDiffEditorWidget::tr("Skipped %n lines...", 0, skippedNumber);
+ if (skippedNumber == -2)
+ return SideBySideDiffEditorWidget::tr("Binary files differ");
+ return SideBySideDiffEditorWidget::tr("Skipped unknown number of lines...");
}
void SideDiffEditorWidget::paintEvent(QPaintEvent *e)
{
m_inPaintEvent = true;
- BaseTextEditorWidget::paintEvent(e);
+ SelectableTextEditorWidget::paintEvent(e);
m_inPaintEvent = false;
- QPainter painter(viewport());
+ QPainter painter(viewport());
QPointF offset = contentOffset();
QTextBlock firstBlock = firstVisibleBlock();
QTextBlock currentBlock = firstBlock;
@@ -557,16 +635,17 @@ void SideDiffEditorWidget::paintEvent(QPaintEvent *e)
const int skippedBefore = m_skippedLines.value(blockNumber);
if (skippedBefore) {
- const QString skippedRowsText = tr("Skipped %n lines...", 0, skippedBefore);
+ const QString skippedRowsText = skippedText(skippedBefore);
paintSeparator(painter, m_chunkLineForeground,
skippedRowsText, currentBlock, top);
}
- const DiffEditorController::DiffFileInfo fileInfo = m_fileInfo.value(blockNumber);
+ const DiffFileInfo fileInfo = m_fileInfo.value(blockNumber);
if (!fileInfo.fileName.isEmpty()) {
const QString fileNameText = fileInfo.typeInfo.isEmpty()
? fileInfo.fileName
- : tr("[%1] %2").arg(fileInfo.typeInfo).arg(fileInfo.fileName);
+ : tr("[%1] %2").arg(fileInfo.typeInfo)
+ .arg(fileInfo.fileName);
paintSeparator(painter, m_fileLineForeground,
fileNameText, currentBlock, top);
}
@@ -574,9 +653,9 @@ void SideDiffEditorWidget::paintEvent(QPaintEvent *e)
}
currentBlock = currentBlock.next();
}
- paintCollapsedBlockPopup(painter, e->rect());
+// paintCollapsedBlockPopup(painter, e->rect());
}
-
+/*
void SideDiffEditorWidget::paintCollapsedBlockPopup(QPainter &painter, const QRect &clipRect)
{
QPointF offset(contentOffset());
@@ -620,7 +699,7 @@ void SideDiffEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
QPointF offset,
const QRect &clip)
{
- // We ignore the call coming from the BaseTextEditorWidget::paintEvent()
+ // We ignore the call coming from the SelectableTextEditorWidget::paintEvent()
// since we will draw it later, after custom drawings of this paintEvent.
// We need to draw it after our custom drawings, otherwise custom
// drawings will appear in front of block popup.
@@ -676,7 +755,7 @@ void SideDiffEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
b = b.next();
}
}
-
+*/
//////////////////
@@ -684,7 +763,10 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
: QWidget(parent)
, m_guiController(0)
, m_controller(0)
+ , m_ignoreCurrentIndexChange(false)
, m_foldingBlocker(false)
+ , m_contextMenuFileIndex(-1)
+ , m_contextMenuChunkIndex(-1)
{
m_leftEditor = new SideDiffEditorWidget(this);
m_leftEditor->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -696,6 +778,8 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
m_leftEditor->setCodeStyle(TextEditorSettings::codeStyle());
connect(m_leftEditor, SIGNAL(jumpToOriginalFileRequested(int,int,int)),
this, SLOT(slotLeftJumpToOriginalFileRequested(int,int,int)));
+ connect(m_leftEditor, SIGNAL(contextMenuRequested(QMenu*,int,int)),
+ this, SLOT(slotLeftContextMenuRequested(QMenu*,int,int)));
m_rightEditor = new SideDiffEditorWidget(this);
m_rightEditor->setReadOnly(true);
@@ -706,6 +790,8 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
m_rightEditor->setCodeStyle(TextEditorSettings::codeStyle());
connect(m_rightEditor, SIGNAL(jumpToOriginalFileRequested(int,int,int)),
this, SLOT(slotRightJumpToOriginalFileRequested(int,int,int)));
+ connect(m_rightEditor, SIGNAL(contextMenuRequested(QMenu*,int,int)),
+ this, SLOT(slotRightContextMenuRequested(QMenu*,int,int)));
connect(TextEditorSettings::instance(),
SIGNAL(fontSettingsChanged(TextEditor::FontSettings)),
@@ -724,8 +810,8 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
connect(m_leftEditor, SIGNAL(cursorPositionChanged()),
this, SLOT(leftCursorPositionChanged()));
- connect(m_leftEditor->document()->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)),
- this, SLOT(leftDocumentSizeChanged()));
+// connect(m_leftEditor->document()->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)),
+// this, SLOT(leftDocumentSizeChanged()));
connect(m_rightEditor->verticalScrollBar(), SIGNAL(valueChanged(int)),
this, SLOT(rightVSliderChanged()));
@@ -739,8 +825,8 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
connect(m_rightEditor, SIGNAL(cursorPositionChanged()),
this, SLOT(rightCursorPositionChanged()));
- connect(m_rightEditor->document()->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)),
- this, SLOT(rightDocumentSizeChanged()));
+// connect(m_rightEditor->document()->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)),
+// this, SLOT(rightDocumentSizeChanged()));
m_splitter = new Core::MiniSplitter(this);
m_splitter->addWidget(m_leftEditor);
@@ -757,42 +843,38 @@ SideBySideDiffEditorWidget::~SideBySideDiffEditorWidget()
}
-void SideBySideDiffEditorWidget::setDiffEditorGuiController(DiffEditorGuiController *controller)
+void SideBySideDiffEditorWidget::setDiffEditorGuiController(
+ DiffEditorGuiController *controller)
{
if (m_guiController == controller)
return;
if (m_guiController) {
- disconnect(m_controller, SIGNAL(cleared(QString)), this, SLOT(clear(QString)));
- disconnect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),
- this, SLOT(setDiff(QList<DiffEditorController::DiffFilesContents>,QString)));
-
- disconnect(m_guiController, SIGNAL(contextLinesNumberChanged(int)),
- this, SLOT(setContextLinesNumber(int)));
- disconnect(m_guiController, SIGNAL(ignoreWhitespacesChanged(bool)),
- this, SLOT(setIgnoreWhitespaces(bool)));
+ 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)));
+ this, SLOT(setCurrentDiffFileIndex(int)));
- clear(tr("No controller"));
+ clearAll(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(clear(QString)));
- connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),
- this, SLOT(setDiff(QList<DiffEditorController::DiffFilesContents>,QString)));
+ 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(contextLinesNumberChanged(int)),
- this, SLOT(setContextLinesNumber(int)));
- connect(m_guiController, SIGNAL(ignoreWhitespacesChanged(bool)),
- this, SLOT(setIgnoreWhitespaces(bool)));
connect(m_guiController, SIGNAL(currentDiffFileIndexChanged(int)),
this, SLOT(setCurrentDiffFileIndex(int)));
- setDiff(m_controller->diffContents(), m_controller->workingDirectory());
+ setDiff(m_controller->diffFiles(), m_controller->workingDirectory());
+ setCurrentDiffFileIndex(m_guiController->currentDiffFileIndex());
}
}
@@ -804,91 +886,37 @@ DiffEditorGuiController *SideBySideDiffEditorWidget::diffEditorGuiController() c
void SideBySideDiffEditorWidget::clear(const QString &message)
{
+ const bool oldIgnore = m_ignoreCurrentIndexChange;
+ m_ignoreCurrentIndexChange = true;
m_leftEditor->clearAll(message);
m_rightEditor->clearAll(message);
+ m_ignoreCurrentIndexChange = oldIgnore;
}
-void SideBySideDiffEditorWidget::setDiff(const QList<DiffEditorController::DiffFilesContents> &diffFileList, const QString &workingDirectory)
+void SideBySideDiffEditorWidget::clearAll(const QString &message)
{
- Q_UNUSED(workingDirectory)
-
- Differ differ;
- QList<DiffList> diffList;
- for (int i = 0; i < diffFileList.count(); i++) {
- DiffEditorController::DiffFilesContents dfc = diffFileList.at(i);
- DiffList dl;
- dl.leftFileInfo = dfc.leftFileInfo;
- dl.rightFileInfo = dfc.rightFileInfo;
- dl.diffList = differ.cleanupSemantics(differ.diff(dfc.leftText, dfc.rightText));
- diffList.append(dl);
- }
- setDiff(diffList);
-}
-
-void SideBySideDiffEditorWidget::setDiff(const QList<DiffList> &diffList)
-{
- m_diffList = diffList;
- m_originalChunkData.clear();
- m_contextFileData.clear();
- const int contextLinesNumber = m_guiController ? m_guiController->contextLinesNumber() : 3;
-
- for (int i = 0; i < m_diffList.count(); i++) {
- const DiffList &dl = m_diffList.at(i);
- QList<Diff> leftDiffs;
- QList<Diff> rightDiffs;
- handleWhitespaces(dl.diffList, &leftDiffs, &rightDiffs);
- ChunkData chunkData = calculateOriginalData(leftDiffs, rightDiffs);
- m_originalChunkData.append(chunkData);
- FileData fileData = calculateContextData(chunkData, contextLinesNumber);
- fileData.leftFileInfo = dl.leftFileInfo;
- fileData.rightFileInfo = dl.rightFileInfo;
- m_contextFileData.append(fileData);
- }
- showDiff();
-}
-
-void SideBySideDiffEditorWidget::handleWhitespaces(const QList<Diff> &input,
- QList<Diff> *leftOutput,
- QList<Diff> *rightOutput) const
-{
- if (!leftOutput || !rightOutput)
- return;
-
- Differ::splitDiffList(input, leftOutput, rightOutput);
- if (m_guiController && m_guiController->isIgnoreWhitespaces()) {
- const QList<Diff> leftDiffList = Differ::moveWhitespaceIntoEqualities(*leftOutput);
- const QList<Diff> rightDiffList = Differ::moveWhitespaceIntoEqualities(*rightOutput);
- Differ::diffBetweenEqualities(leftDiffList, rightDiffList, leftOutput, rightOutput);
- }
+ setDiff(QList<FileData>(), QString());
+ clear(message);
}
-void SideBySideDiffEditorWidget::setContextLinesNumber(int lines)
+void SideBySideDiffEditorWidget::setDiff(const QList<FileData> &diffFileList,
+ const QString &workingDirectory)
{
- Q_UNUSED(lines)
- const int contextLinesNumber = m_guiController ? m_guiController->contextLinesNumber() : 3;
-
- for (int i = 0; i < m_contextFileData.count(); i++) {
- const FileData oldFileData = m_contextFileData.at(i);
- FileData newFileData = calculateContextData(m_originalChunkData.at(i), contextLinesNumber);
- newFileData.leftFileInfo = oldFileData.leftFileInfo;
- newFileData.rightFileInfo = oldFileData.rightFileInfo;
- m_contextFileData[i] = newFileData;
- }
+ Q_UNUSED(workingDirectory)
+ m_contextFileData = diffFileList;
showDiff();
}
-void SideBySideDiffEditorWidget::setIgnoreWhitespaces(bool ignore)
-{
- Q_UNUSED(ignore)
-
- setDiff(m_diffList);
-}
-
void SideBySideDiffEditorWidget::setCurrentDiffFileIndex(int diffFileIndex)
{
+ if (m_ignoreCurrentIndexChange)
+ return;
+
const int blockNumber = m_leftEditor->blockNumberForFileIndex(diffFileIndex);
+ const bool oldIgnore = m_ignoreCurrentIndexChange;
+ m_ignoreCurrentIndexChange = true;
QTextBlock leftBlock = m_leftEditor->document()->findBlockByNumber(blockNumber);
QTextCursor leftCursor = m_leftEditor->textCursor();
leftCursor.setPosition(leftBlock.position());
@@ -901,6 +929,7 @@ void SideBySideDiffEditorWidget::setCurrentDiffFileIndex(int diffFileIndex)
m_leftEditor->centerCursor();
m_rightEditor->centerCursor();
+ m_ignoreCurrentIndexChange = oldIgnore;
}
void SideBySideDiffEditorWidget::showDiff()
@@ -912,7 +941,10 @@ void SideBySideDiffEditorWidget::showDiff()
clear(tr("No difference"));
- QList<QPair<DiffEditorController::DiffFileInfo, QString> > leftDocs, rightDocs;
+ QMap<int, QList<DiffSelection> > leftFormats;
+ QMap<int, QList<DiffSelection> > rightFormats;
+
+// QList<QPair<DiffFileInfo, QString> > leftDocs, rightDocs;
QString leftTexts, rightTexts;
int blockNumber = 0;
QChar separator = QLatin1Char('\n');
@@ -920,68 +952,151 @@ void SideBySideDiffEditorWidget::showDiff()
QString leftText, rightText;
const FileData &contextFileData = m_contextFileData.at(i);
- int leftLineNumber = 0;
- int rightLineNumber = 0;
+ leftFormats[blockNumber].append(DiffSelection(&m_fileLineFormat));
+ rightFormats[blockNumber].append(DiffSelection(&m_fileLineFormat));
m_leftEditor->setFileInfo(blockNumber, contextFileData.leftFileInfo);
m_rightEditor->setFileInfo(blockNumber, contextFileData.rightFileInfo);
leftText = separator;
rightText = separator;
blockNumber++;
- for (int j = 0; j < contextFileData.chunks.count(); j++) {
- ChunkData chunkData = contextFileData.chunks.at(j);
- if (chunkData.contextChunk) {
- const int skippedLines = chunkData.rows.count();
- m_leftEditor->setSkippedLines(blockNumber, skippedLines);
- m_rightEditor->setSkippedLines(blockNumber, skippedLines);
- leftText += separator;
- rightText += separator;
- blockNumber++;
- }
+ int lastLeftLineNumber = -1;
- for (int k = 0; k < chunkData.rows.count(); k++) {
- RowData rowData = chunkData.rows.at(k);
- TextLineData leftLineData = rowData.leftLine;
- TextLineData rightLineData = rowData.rightLine;
- if (leftLineData.textLineType == TextLineData::TextLine) {
- leftText += leftLineData.text;
- leftLineNumber++;
- m_leftEditor->setLineNumber(blockNumber, leftLineNumber);
- } else if (leftLineData.textLineType == TextLineData::Separator) {
- m_leftEditor->setSeparator(blockNumber, true);
+ if (contextFileData.binaryFiles) {
+ leftFormats[blockNumber].append(DiffSelection(&m_chunkLineFormat));
+ rightFormats[blockNumber].append(DiffSelection(&m_chunkLineFormat));
+ m_leftEditor->setSkippedLines(blockNumber, -2);
+ m_rightEditor->setSkippedLines(blockNumber, -2);
+ leftText += separator;
+ rightText += separator;
+ blockNumber++;
+ } else {
+ for (int j = 0; j < contextFileData.chunks.count(); j++) {
+ ChunkData chunkData = contextFileData.chunks.at(j);
+
+ int leftLineNumber = chunkData.leftStartingLineNumber;
+ int rightLineNumber = chunkData.rightStartingLineNumber;
+
+ if (!chunkData.contextChunk) {
+ const int skippedLines = leftLineNumber - lastLeftLineNumber - 1;
+ if (skippedLines > 0) {
+ leftFormats[blockNumber].append(DiffSelection(&m_chunkLineFormat));
+ rightFormats[blockNumber].append(DiffSelection(&m_chunkLineFormat));
+ m_leftEditor->setSkippedLines(blockNumber, skippedLines);
+ m_rightEditor->setSkippedLines(blockNumber, skippedLines);
+ leftText += separator;
+ rightText += separator;
+ blockNumber++;
+ }
+
+ m_leftEditor->setChunkIndex(blockNumber, chunkData.rows.count(), j);
+ m_rightEditor->setChunkIndex(blockNumber, chunkData.rows.count(), j);
+
+ for (int k = 0; k < chunkData.rows.count(); k++) {
+ RowData rowData = chunkData.rows.at(k);
+ TextLineData leftLineData = rowData.leftLine;
+ TextLineData rightLineData = rowData.rightLine;
+ if (leftLineData.textLineType == TextLineData::TextLine) {
+ leftText += leftLineData.text;
+ lastLeftLineNumber = leftLineNumber;
+ leftLineNumber++;
+ m_leftEditor->setLineNumber(blockNumber, leftLineNumber);
+ } else if (leftLineData.textLineType == TextLineData::Separator) {
+ m_leftEditor->setSeparator(blockNumber, true);
+ }
+
+ if (rightLineData.textLineType == TextLineData::TextLine) {
+ rightText += rightLineData.text;
+ rightLineNumber++;
+ m_rightEditor->setLineNumber(blockNumber, rightLineNumber);
+ } else if (rightLineData.textLineType == TextLineData::Separator) {
+ m_rightEditor->setSeparator(blockNumber, true);
+ }
+
+ if (!rowData.equal) {
+ if (rowData.leftLine.textLineType == TextLineData::TextLine)
+ leftFormats[blockNumber].append(DiffSelection(&m_leftLineFormat));
+ else
+ leftFormats[blockNumber].append(DiffSelection(&m_spanLineFormat));
+ if (rowData.rightLine.textLineType == TextLineData::TextLine)
+ rightFormats[blockNumber].append(DiffSelection(&m_rightLineFormat));
+ else
+ rightFormats[blockNumber].append(DiffSelection(&m_spanLineFormat));
+ }
+
+ QMapIterator<int, int> itLeft(leftLineData.changedPositions);
+ while (itLeft.hasNext()) {
+ itLeft.next();
+ leftFormats[blockNumber].append(
+ DiffSelection(itLeft.key(), itLeft.value(),
+ &m_leftCharFormat));
+ }
+
+ QMapIterator<int, int> itRight(rightLineData.changedPositions);
+ while (itRight.hasNext()) {
+ itRight.next();
+ rightFormats[blockNumber].append(
+ DiffSelection(itRight.key(), itRight.value(),
+ &m_rightCharFormat));
+ }
+
+ leftText += separator;
+ rightText += separator;
+ blockNumber++;
+ }
}
- if (rightLineData.textLineType == TextLineData::TextLine) {
- rightText += rightLineData.text;
- rightLineNumber++;
- m_rightEditor->setLineNumber(blockNumber, rightLineNumber);
- } else if (rightLineData.textLineType == TextLineData::Separator) {
- m_rightEditor->setSeparator(blockNumber, true);
+ if (j == contextFileData.chunks.count() - 1) { // the last chunk
+ int skippedLines = -2;
+ if (chunkData.contextChunk) {
+ // if it's context chunk
+ skippedLines = chunkData.rows.count();
+ } else if (!contextFileData.lastChunkAtTheEndOfFile
+ && !contextFileData.contextChunksIncluded) {
+ // if not a context chunk and not a chunk at the end of file
+ // and context lines not included
+ skippedLines = -1; // unknown count skipped by the end of file
+ }
+
+ if (skippedLines >= -1) {
+ leftFormats[blockNumber].append(DiffSelection(&m_chunkLineFormat));
+ rightFormats[blockNumber].append(DiffSelection(&m_chunkLineFormat));
+ m_leftEditor->setSkippedLines(blockNumber, skippedLines);
+ m_rightEditor->setSkippedLines(blockNumber, skippedLines);
+ leftText += separator;
+ rightText += separator;
+ blockNumber++;
+ } // otherwise nothing skipped
}
- leftText += separator;
- rightText += separator;
- blockNumber++;
}
}
leftText.replace(QLatin1Char('\r'), QLatin1Char(' '));
rightText.replace(QLatin1Char('\r'), QLatin1Char(' '));
leftTexts += leftText;
rightTexts += rightText;
- leftDocs.append(qMakePair(contextFileData.leftFileInfo, leftText));
- rightDocs.append(qMakePair(contextFileData.rightFileInfo, rightText));
+// leftDocs.append(qMakePair(contextFileData.leftFileInfo, leftText));
+// rightDocs.append(qMakePair(contextFileData.rightFileInfo, rightText));
}
if (leftTexts.isEmpty() && rightTexts.isEmpty())
return;
- m_leftEditor->setDocuments(leftDocs);
- m_rightEditor->setDocuments(rightDocs);
+// m_leftEditor->setDocuments(leftDocs);
+// m_rightEditor->setDocuments(rightDocs);
+ const bool oldIgnore = m_ignoreCurrentIndexChange;
+ m_ignoreCurrentIndexChange = true;
+ m_leftEditor->clear();
m_leftEditor->setPlainText(leftTexts);
+ m_rightEditor->clear();
m_rightEditor->setPlainText(rightTexts);
+ m_ignoreCurrentIndexChange = oldIgnore;
- colorDiff(m_contextFileData);
+ m_leftEditor->setSelections(leftFormats);
+ m_rightEditor->setSelections(rightFormats);
+
+/*
QTextBlock leftBlock = m_leftEditor->document()->firstBlock();
QTextBlock rightBlock = m_rightEditor->document()->firstBlock();
for (int i = 0; i < m_contextFileData.count(); i++) {
@@ -1033,134 +1148,38 @@ void SideBySideDiffEditorWidget::showDiff()
rightLayout->emitDocumentSizeChanged();
}
m_foldingBlocker = false;
-
+*/
m_leftEditor->verticalScrollBar()->setValue(verticalValue);
m_rightEditor->verticalScrollBar()->setValue(verticalValue);
m_leftEditor->horizontalScrollBar()->setValue(leftHorizontalValue);
m_rightEditor->horizontalScrollBar()->setValue(rightHorizontalValue);
- m_leftEditor->updateFoldingHighlight(QPoint(-1, -1));
- m_rightEditor->updateFoldingHighlight(QPoint(-1, -1));
-}
-
-void SideBySideDiffEditorWidget::colorDiff(const QList<FileData> &fileDataList)
-{
- QPalette pal = m_leftEditor->extraArea()->palette();
- pal.setCurrentColorGroup(QPalette::Active);
- QTextCharFormat spanLineFormat;
- spanLineFormat.setBackground(pal.color(QPalette::Background));
- spanLineFormat.setProperty(QTextFormat::FullWidthSelection, true);
-
- int leftPos = 0;
- int rightPos = 0;
- // <start position, end position>
- QMap<int, int> leftLinePos;
- QMap<int, int> rightLinePos;
- QMap<int, int> leftCharPos;
- QMap<int, int> rightCharPos;
- QMap<int, int> leftSkippedPos;
- QMap<int, int> rightSkippedPos;
- QMap<int, int> leftChunkPos;
- QMap<int, int> rightChunkPos;
- QMap<int, int> leftFilePos;
- QMap<int, int> rightFilePos;
- int leftLastDiffBlockStartPos = 0;
- int rightLastDiffBlockStartPos = 0;
- int leftLastSkippedBlockStartPos = 0;
- int rightLastSkippedBlockStartPos = 0;
-
- for (int i = 0; i < fileDataList.count(); i++) {
- const FileData &fileData = fileDataList.at(i);
- leftFilePos[leftPos] = leftPos + 1;
- rightFilePos[rightPos] = rightPos + 1;
- leftPos++; // for file line
- rightPos++; // for file line
-
- for (int j = 0; j < fileData.chunks.count(); j++) {
- const ChunkData &chunkData = fileData.chunks.at(j);
- if (chunkData.contextChunk) {
- leftChunkPos[leftPos] = leftPos + 1;
- rightChunkPos[rightPos] = rightPos + 1;
- leftPos++; // for chunk line
- rightPos++; // for chunk line
- }
- leftLastDiffBlockStartPos = leftPos;
- rightLastDiffBlockStartPos = rightPos;
- leftLastSkippedBlockStartPos = leftPos;
- rightLastSkippedBlockStartPos = rightPos;
-
- for (int k = 0; k < chunkData.rows.count(); k++) {
- const RowData &rowData = chunkData.rows.at(k);
-
- addChangedPositions(leftPos, rowData.leftLine.changedPositions, &leftCharPos);
- addChangedPositions(rightPos, rowData.rightLine.changedPositions, &rightCharPos);
-
- leftPos += rowData.leftLine.text.count() + 1; // +1 for '\n'
- rightPos += rowData.rightLine.text.count() + 1; // +1 for '\n'
-
- if (!rowData.equal) {
- if (rowData.leftLine.textLineType == TextLineData::TextLine) {
- leftLinePos[leftLastDiffBlockStartPos] = leftPos;
- leftLastSkippedBlockStartPos = leftPos;
- } else {
- leftSkippedPos[leftLastSkippedBlockStartPos] = leftPos;
- leftLastDiffBlockStartPos = leftPos;
- }
- if (rowData.rightLine.textLineType == TextLineData::TextLine) {
- rightLinePos[rightLastDiffBlockStartPos] = rightPos;
- rightLastSkippedBlockStartPos = rightPos;
- } else {
- rightSkippedPos[rightLastSkippedBlockStartPos] = rightPos;
- rightLastDiffBlockStartPos = rightPos;
- }
- } else {
- leftLastDiffBlockStartPos = leftPos;
- leftLastSkippedBlockStartPos = leftPos;
- rightLastDiffBlockStartPos = rightPos;
- rightLastSkippedBlockStartPos = rightPos;
- }
- }
- }
- }
-
- QTextCursor leftCursor = m_leftEditor->textCursor();
- QTextCursor rightCursor = m_rightEditor->textCursor();
- QList<QTextEdit::ExtraSelection> leftSelections;
- leftSelections += colorPositions(m_leftLineFormat, leftCursor, leftLinePos);
- leftSelections += colorPositions(m_leftCharFormat, leftCursor, leftCharPos);
- leftSelections += colorPositions(spanLineFormat, leftCursor, leftSkippedPos);
- leftSelections += colorPositions(m_chunkLineFormat, leftCursor, leftChunkPos);
- leftSelections += colorPositions(m_fileLineFormat, leftCursor, leftFilePos);
-
- QList<QTextEdit::ExtraSelection> rightSelections;
- rightSelections += colorPositions(m_rightLineFormat, rightCursor, rightLinePos);
- rightSelections += colorPositions(m_rightCharFormat, rightCursor, rightCharPos);
- rightSelections += colorPositions(spanLineFormat, rightCursor, rightSkippedPos);
- rightSelections += colorPositions(m_chunkLineFormat, rightCursor, rightChunkPos);
- rightSelections += colorPositions(m_fileLineFormat, rightCursor, rightFilePos);
-
- m_leftEditor->setExtraSelections(BaseTextEditorWidget::OtherSelection, leftSelections);
- m_rightEditor->setExtraSelections(BaseTextEditorWidget::OtherSelection, rightSelections);
+// m_leftEditor->updateFoldingHighlight(QPoint(-1, -1));
+// m_rightEditor->updateFoldingHighlight(QPoint(-1, -1));
}
-void SideBySideDiffEditorWidget::setFontSettings(const TextEditor::FontSettings &fontSettings)
+void SideBySideDiffEditorWidget::setFontSettings(
+ const TextEditor::FontSettings &fontSettings)
{
m_leftEditor->baseTextDocument()->setFontSettings(fontSettings);
m_rightEditor->baseTextDocument()->setFontSettings(fontSettings);
- m_fileLineFormat = fullWidthFormatForTextStyle(fontSettings, C_DIFF_FILE_LINE);
- m_chunkLineFormat = fullWidthFormatForTextStyle(fontSettings, C_DIFF_CONTEXT_LINE);
- m_leftLineFormat = fullWidthFormatForTextStyle(fontSettings, C_DIFF_SOURCE_LINE);
- m_leftCharFormat = fullWidthFormatForTextStyle(fontSettings, C_DIFF_SOURCE_CHAR);
- m_rightLineFormat = fullWidthFormatForTextStyle(fontSettings, C_DIFF_DEST_LINE);
- m_rightCharFormat = fullWidthFormatForTextStyle(fontSettings, C_DIFF_DEST_CHAR);
+ m_spanLineFormat = fontSettings.toTextCharFormat(C_LINE_NUMBER);
+ 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);
- colorDiff(m_contextFileData);
+ m_leftEditor->update();
+ m_rightEditor->update();
}
-void SideBySideDiffEditorWidget::slotLeftJumpToOriginalFileRequested(int diffFileIndex,
- int lineNumber,
- int columnNumber)
+void SideBySideDiffEditorWidget::slotLeftJumpToOriginalFileRequested(
+ int diffFileIndex,
+ int lineNumber,
+ int columnNumber)
{
if (diffFileIndex < 0 || diffFileIndex >= m_contextFileData.count())
return;
@@ -1196,8 +1215,10 @@ void SideBySideDiffEditorWidget::slotLeftJumpToOriginalFileRequested(int diffFil
}
}
-void SideBySideDiffEditorWidget::slotRightJumpToOriginalFileRequested(int diffFileIndex,
- int lineNumber, int columnNumber)
+void SideBySideDiffEditorWidget::slotRightJumpToOriginalFileRequested(
+ int diffFileIndex,
+ int lineNumber,
+ int columnNumber)
{
if (diffFileIndex < 0 || diffFileIndex >= m_contextFileData.count())
return;
@@ -1218,26 +1239,188 @@ void SideBySideDiffEditorWidget::jumpToOriginalFile(const QString &fileName,
Core::EditorManager::openEditorAt(absoluteFileName, lineNumber, columnNumber);
}
+void SideBySideDiffEditorWidget::slotLeftContextMenuRequested(QMenu *menu,
+ int diffFileIndex,
+ int chunkIndex)
+{
+ menu->addSeparator();
+ QAction *sendChunkToCodePasterAction =
+ menu->addAction(tr("Send Chunk to CodePaster..."));
+ connect(sendChunkToCodePasterAction, SIGNAL(triggered()),
+ this, SLOT(slotSendChunkToCodePaster()));
+ menu->addSeparator();
+ QAction *applyAction = menu->addAction(tr("Apply Chunk..."));
+ connect(applyAction, SIGNAL(triggered()), this, SLOT(slotApplyChunk()));
+ applyAction->setEnabled(false);
+
+ m_contextMenuFileIndex = diffFileIndex;
+ m_contextMenuChunkIndex = chunkIndex;
+
+ 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);
+
+ if (fileData.leftFileInfo.fileName == fileData.rightFileInfo.fileName)
+ return;
+
+ applyAction->setEnabled(true);
+}
+
+void SideBySideDiffEditorWidget::slotRightContextMenuRequested(QMenu *menu,
+ int diffFileIndex,
+ int chunkIndex)
+{
+ menu->addSeparator();
+ QAction *sendChunkToCodePasterAction =
+ menu->addAction(tr("Send Chunk to CodePaster..."));
+ connect(sendChunkToCodePasterAction, SIGNAL(triggered()),
+ this, SLOT(slotSendChunkToCodePaster()));
+ menu->addSeparator();
+ QAction *revertAction = menu->addAction(tr("Revert Chunk..."));
+ connect(revertAction, SIGNAL(triggered()), this, SLOT(slotRevertChunk()));
+ revertAction->setEnabled(false);
+
+ m_contextMenuFileIndex = diffFileIndex;
+ m_contextMenuChunkIndex = chunkIndex;
+
+ 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);
+}
+
+void SideBySideDiffEditorWidget::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 SideBySideDiffEditorWidget::slotApplyChunk()
+{
+ patch(m_contextMenuFileIndex, m_contextMenuChunkIndex, false);
+}
+
+void SideBySideDiffEditorWidget::slotRevertChunk()
+{
+ patch(m_contextMenuFileIndex, m_contextMenuChunkIndex, true);
+}
+
+void SideBySideDiffEditorWidget::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();
+}
+
void SideBySideDiffEditorWidget::leftVSliderChanged()
{
- m_rightEditor->verticalScrollBar()->setValue(m_leftEditor->verticalScrollBar()->value());
+ m_rightEditor->verticalScrollBar()->setValue(
+ m_leftEditor->verticalScrollBar()->value());
}
void SideBySideDiffEditorWidget::rightVSliderChanged()
{
- m_leftEditor->verticalScrollBar()->setValue(m_rightEditor->verticalScrollBar()->value());
+ m_leftEditor->verticalScrollBar()->setValue(
+ m_rightEditor->verticalScrollBar()->value());
}
void SideBySideDiffEditorWidget::leftHSliderChanged()
{
if (!m_guiController || m_guiController->horizontalScrollBarSynchronization())
- m_rightEditor->horizontalScrollBar()->setValue(m_leftEditor->horizontalScrollBar()->value());
+ m_rightEditor->horizontalScrollBar()->setValue(
+ m_leftEditor->horizontalScrollBar()->value());
}
void SideBySideDiffEditorWidget::rightHSliderChanged()
{
if (!m_guiController || m_guiController->horizontalScrollBarSynchronization())
- m_leftEditor->horizontalScrollBar()->setValue(m_rightEditor->horizontalScrollBar()->value());
+ m_leftEditor->horizontalScrollBar()->setValue(
+ m_rightEditor->horizontalScrollBar()->value());
}
void SideBySideDiffEditorWidget::leftCursorPositionChanged()
@@ -1248,7 +1431,15 @@ void SideBySideDiffEditorWidget::leftCursorPositionChanged()
if (!m_guiController)
return;
- m_guiController->setCurrentDiffFileIndex(m_leftEditor->fileIndexForBlockNumber(m_leftEditor->textCursor().blockNumber()));
+ if (m_ignoreCurrentIndexChange)
+ return;
+
+ const bool oldIgnore = m_ignoreCurrentIndexChange;
+ m_ignoreCurrentIndexChange = true;
+ m_guiController->setCurrentDiffFileIndex(
+ m_leftEditor->fileIndexForBlockNumber(
+ m_leftEditor->textCursor().blockNumber()));
+ m_ignoreCurrentIndexChange = oldIgnore;
}
void SideBySideDiffEditorWidget::rightCursorPositionChanged()
@@ -1259,9 +1450,18 @@ void SideBySideDiffEditorWidget::rightCursorPositionChanged()
if (!m_guiController)
return;
- m_guiController->setCurrentDiffFileIndex(m_rightEditor->fileIndexForBlockNumber(m_rightEditor->textCursor().blockNumber()));
+ if (m_ignoreCurrentIndexChange)
+ return;
+
+ const bool oldIgnore = m_ignoreCurrentIndexChange;
+ m_ignoreCurrentIndexChange = true;
+ m_guiController->setCurrentDiffFileIndex(
+ m_rightEditor->fileIndexForBlockNumber(
+ m_rightEditor->textCursor().blockNumber()));
+ m_ignoreCurrentIndexChange = oldIgnore;
}
+#if 0
void SideBySideDiffEditorWidget::leftDocumentSizeChanged()
{
synchronizeFoldings(m_leftEditor, m_rightEditor);
@@ -1400,6 +1600,7 @@ void SideBySideDiffEditorWidget::synchronizeFoldings(SideDiffEditorWidget *sourc
}
m_foldingBlocker = false;
}
+#endif
} // namespace DiffEditor
diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.h b/src/plugins/diffeditor/sidebysidediffeditorwidget.h
index a25809e475..7367f489f9 100644
--- a/src/plugins/diffeditor/sidebysidediffeditorwidget.h
+++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.h
@@ -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;
diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
new file mode 100644
index 0000000000..9c58d47257
--- /dev/null
+++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
@@ -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"
diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.h b/src/plugins/diffeditor/unifieddiffeditorwidget.h
new file mode 100644
index 0000000000..dc94d22660
--- /dev/null
+++ b/src/plugins/diffeditor/unifieddiffeditorwidget.h
@@ -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
diff --git a/src/plugins/git/branchdialog.cpp b/src/plugins/git/branchdialog.cpp
index 6badb5e29f..2531867dcb 100644
--- a/src/plugins/git/branchdialog.cpp
+++ b/src/plugins/git/branchdialog.cpp
@@ -310,7 +310,7 @@ void BranchDialog::diff()
if (fullName.isEmpty())
return;
// Do not pass working dir by reference since it might change
- GitPlugin::instance()->gitClient()->diffBranch(QString(m_repository), QStringList(), fullName);
+ GitPlugin::instance()->gitClient()->diffBranch(QString(m_repository), fullName);
}
void BranchDialog::log()
diff --git a/src/plugins/git/gerrit/gerritplugin.cpp b/src/plugins/git/gerrit/gerritplugin.cpp
index 9c0af1c0db..11fccfa3d9 100644
--- a/src/plugins/git/gerrit/gerritplugin.cpp
+++ b/src/plugins/git/gerrit/gerritplugin.cpp
@@ -246,7 +246,7 @@ void FetchContext::show()
{
const QString title = QString::number(m_change->number) + QLatin1Char('/')
+ QString::number(m_change->currentPatchSet.patchSetNumber);
- gitClient()->show(m_repository, QLatin1String("FETCH_HEAD"), QStringList(), title);
+ gitClient()->show(m_repository, QLatin1String("FETCH_HEAD"), title);
}
void FetchContext::cherryPick()
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 57bad5f1ea..9f1edfb229 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -63,6 +63,8 @@
#include <diffeditor/diffeditorcontroller.h>
#include <diffeditor/diffeditordocument.h>
#include <diffeditor/diffeditormanager.h>
+#include <diffeditor/diffeditorreloader.h>
+#include <diffeditor/diffutils.h>
#include <QCoreApplication>
#include <QDir>
@@ -70,7 +72,7 @@
#include <QHash>
#include <QRegExp>
#include <QSignalMapper>
-#include <QTime>
+#include <QTemporaryFile>
#include <QMessageBox>
#include <QPushButton>
@@ -94,195 +96,20 @@ static inline unsigned diffExecutionFlags()
using VcsBase::VcsBasePlugin;
-class GitDiffSwitcher : public QObject
-{
- Q_OBJECT
-
-public:
- enum DiffType {
- DiffRepository,
- DiffFile,
- DiffFileList,
- DiffProjectList,
- DiffBranch,
- DiffShow
- };
-
- GitDiffSwitcher(Core::IDocument *parentDocument, GitClient *gitClient);
-
- void setWorkingDirectory(const QString &workingDir) { m_workingDirectory = workingDir; }
- void setDiffType(DiffType type) { m_diffType = type; }
- void setFileName(const QString &fileName) { m_fileName = fileName; }
- void setFileList(const QStringList &stagedFiles, const QStringList &unstagedFiles)
- {
- m_stagedFiles = stagedFiles;
- m_unstagedFiles = unstagedFiles;
- }
- void setProjectList(const QStringList &projectFiles) { m_projectFiles = projectFiles; }
- void setBranchName(const QString &branchName) { m_branchName = branchName; }
- void setId(const QString &id) { m_id = id; }
- void setDisplayName(const QString &displayName) { m_displayName = displayName; }
- void setBaseArguments(const QStringList &args) { m_baseArguments = args; }
-
-private slots:
- void slotEditorOpened(Core::IEditor *editor);
- void slotEditorClosed(Core::IEditor *editor);
- void execute(QObject *editor);
-
-private:
- void attachAction(Core::IEditor *editor);
- QString actionText();
-
- Core::IDocument *m_document;
- GitClient *m_gitClient;
- QString m_workingDirectory;
- DiffType m_diffType;
- bool m_usingDiffEditor;
- QString m_fileName;
- QStringList m_stagedFiles;
- QStringList m_unstagedFiles;
- QStringList m_projectFiles;
- QString m_branchName;
- QString m_id;
- QString m_displayName;
- QStringList m_baseArguments;
-
- QSignalMapper *m_signalMapper;
- QMap<QObject *, bool> m_editorToUsingSideBySideDiffEditor;
-};
-
-GitDiffSwitcher::GitDiffSwitcher(Core::IDocument *parentDocument, GitClient *gitClient)
- : QObject(parentDocument),
- m_document(parentDocument),
- m_gitClient(gitClient),
- m_signalMapper(new QSignalMapper(this))
-{
- QList<Core::IEditor *> editors = Core::DocumentModel::editorsForDocument(m_document);
- for (int i = 0; i < editors.count(); i++)
- attachAction(editors.at(i));
-
- // must be queued connection because execute() removes the editor & tool bar that the action was added to
- connect(m_signalMapper, SIGNAL(mapped(QObject*)), this, SLOT(execute(QObject*)), Qt::QueuedConnection);
- connect(Core::EditorManager::instance(), SIGNAL(editorOpened(Core::IEditor*)),
- this, SLOT(slotEditorOpened(Core::IEditor*)));
- connect(Core::EditorManager::instance(), SIGNAL(editorAboutToClose(Core::IEditor*)),
- this, SLOT(slotEditorClosed(Core::IEditor*)));
-}
-
-void GitDiffSwitcher::attachAction(Core::IEditor *editor)
-{
- if (m_editorToUsingSideBySideDiffEditor.contains(editor))
- return;
-
- const bool usingSideBySideDiffEditor = m_gitClient->settings()->boolValue(GitSettings::useDiffEditorKey);
- QIcon actionIcon = usingSideBySideDiffEditor
- ? QIcon(QLatin1String(Core::Constants::ICON_TEXT_DIFF))
- : QIcon(QLatin1String(Core::Constants::ICON_SIDE_BY_SIDE_DIFF));
-
- const QString actionToolTip = usingSideBySideDiffEditor
- ? tr("Switch to Text Diff Editor")
- : tr("Switch to Side By Side Diff Editor");
-
- QAction *switchAction = new QAction(actionIcon, actionToolTip, editor);
- editor->toolBar()->addAction(switchAction);
- connect(switchAction, SIGNAL(triggered()),
- m_signalMapper, SLOT(map()));
- m_signalMapper->setMapping(switchAction, editor);
-
- m_editorToUsingSideBySideDiffEditor.insert(editor, usingSideBySideDiffEditor);
-}
-
-void GitDiffSwitcher::slotEditorOpened(Core::IEditor *editor)
-{
- Core::IDocument *document = editor->document();
- if (document != m_document)
- return;
-
- attachAction(editor);
-}
-
-void GitDiffSwitcher::slotEditorClosed(Core::IEditor *editor)
-{
- Core::IDocument *document = editor->document();
- if (document != m_document)
- return;
-
- m_editorToUsingSideBySideDiffEditor.remove(editor);
-}
-
-void GitDiffSwitcher::execute(QObject *editor)
-{
- bool usingSideBySideEditor = !m_editorToUsingSideBySideDiffEditor.value(editor);
- m_editorToUsingSideBySideDiffEditor[editor] = usingSideBySideEditor;
- m_gitClient->settings()->setValue(GitSettings::useDiffEditorKey, usingSideBySideEditor);
-
- Core::IEditor *ieditor = qobject_cast<Core::IEditor*>(editor);
- Core::EditorManager::activateEditor(ieditor);
-
- switch (m_diffType) {
- case DiffRepository:
- m_gitClient->diff(m_workingDirectory, QStringList(), QStringList());
- break;
- case DiffFile:
- m_gitClient->diff(m_workingDirectory, m_fileName);
- break;
- case DiffFileList:
- m_gitClient->diff(m_workingDirectory, m_unstagedFiles, m_stagedFiles);
- break;
- case DiffProjectList:
- m_gitClient->diff(m_workingDirectory, m_projectFiles, QStringList());
- break;
- case DiffBranch:
- m_gitClient->diffBranch(m_workingDirectory, m_baseArguments, m_branchName);
- break;
- case DiffShow:
- m_gitClient->show(m_fileName, m_id, m_baseArguments, m_displayName);
- break;
- default:
- break;
- }
-
- Core::EditorManager::closeEditor(ieditor, false);
-}
-
class GitDiffHandler : public QObject
{
Q_OBJECT
public:
- enum RevisionType {
- WorkingTree,
- Index,
- Other
- };
-
- struct Revision {
- Revision() : type(WorkingTree) { }
- Revision(RevisionType t) : type(t) { }
- Revision(RevisionType t, const QString &i) : type(t), id(i) { }
- RevisionType type;
- QString id; // can be sha or HEAD
- QString infoText() const
- {
- switch (type) {
- case WorkingTree: return tr("Working tree");
- case Index: return tr("Index");
- default: return id;
- }
- }
- };
-
GitDiffHandler(DiffEditor::DiffEditorController *editorController,
- const QString &gitPath,
- const QString &workingDirectory,
- const QProcessEnvironment &environment,
- int timeout);
+ const QString &workingDirectory);
// index -> working tree
void diffFile(const QString &fileName);
// stagedFileNames: HEAD -> index
// unstagedFileNames: index -> working tree
- void diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames);
+ void diffFiles(const QStringList &stagedFileNames,
+ const QStringList &unstagedFileNames);
// index -> working tree
void diffProjects(const QStringList &projectPaths);
// index -> working tree
@@ -294,127 +121,82 @@ public:
private slots:
void slotShowDescriptionReceived(const QString &data);
- void slotFileListReceived(const QString &fileList);
- void slotFileContentsReceived(const QString &contents);
+ void slotDiffOutputReceived(const QString &contents);
private:
- void collectShowDescription(const QString &id);
- void collectFilesList(const QStringList &additionalArguments);
- void prepareForCollection();
- void collectFilesContents();
- void feedEditor();
- QString workingTreeContents(const QString &fileName) const;
+ void postCollectShowDescription(const QString &id);
+ void postCollectDiffOutput(const QStringList &arguments);
+ void postCollectDiffOutput(const QList<QStringList> &argumentsList);
+ void addJob(VcsBase::Command *command, const QStringList &arguments);
+ int timeout() const;
+ QProcessEnvironment processEnvironment() const;
+ QString gitPath() const;
QPointer<DiffEditor::DiffEditorController> m_editorController;
- const QString m_gitPath;
const QString m_workingDirectory;
- const QProcessEnvironment m_processEnvironment;
- const int m_timeout;
+ GitClient *m_gitClient;
const QString m_waitMessage;
- struct RevisionRange {
- RevisionRange() { }
- RevisionRange(const Revision &b, const Revision &e) : begin(b), end(e) { }
- Revision begin;
- Revision end;
- };
-
- // filename, revision range
- QMap<QString, QList<RevisionRange> > m_requestedRevisionRanges;
- // filename, revision, dummy
- QMap<QString, QMap<Revision, bool> > m_pendingRevisions;
- // filename, revision, contents
- QMap<QString, QMap<Revision, QString> > m_collectedRevisions;
-
- RevisionRange m_requestedRevisionRange;
+ QString m_id;
};
-inline bool operator<(const GitDiffHandler::Revision &rev1, const GitDiffHandler::Revision &rev2)
-{
- if (rev1.type != rev2.type)
- return rev1.type < rev2.type;
- return rev1.id < rev2.id;
-}
-
GitDiffHandler::GitDiffHandler(DiffEditor::DiffEditorController *editorController,
- const QString &gitPath,
- const QString &workingDirectory,
- const QProcessEnvironment &environment,
- int timeout)
+ const QString &workingDirectory)
: m_editorController(editorController),
- m_gitPath(gitPath),
m_workingDirectory(workingDirectory),
- m_processEnvironment(environment),
- m_timeout(timeout),
+ m_gitClient(GitPlugin::instance()->gitClient()),
m_waitMessage(tr("Waiting for data..."))
{
}
void GitDiffHandler::diffFile(const QString &fileName)
{
- m_requestedRevisionRange = RevisionRange(
- Revision(Index),
- Revision(WorkingTree));
-
- collectFilesList(QStringList() << QLatin1String("--") << fileName);
+ postCollectDiffOutput(QStringList() << QLatin1String("--") << fileName);
}
-void GitDiffHandler::diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames)
+void GitDiffHandler::diffFiles(const QStringList &stagedFileNames,
+ const QStringList &unstagedFileNames)
{
- RevisionRange stagedRange = RevisionRange(
- Revision(Other, QLatin1String(HEAD)),
- Revision(Index));
- RevisionRange unstagedRange = RevisionRange(
- Revision(Index),
- Revision(WorkingTree));
+ QList<QStringList> arguments;
- for (int i = 0; i < stagedFileNames.count(); i++)
- m_requestedRevisionRanges[stagedFileNames.at(i)].append(stagedRange);
+ QStringList stagedArguments;
+ stagedArguments << QLatin1String("--cached");
+ stagedArguments << QLatin1String("--");
+ stagedArguments << stagedFileNames;
+ arguments << stagedArguments;
- for (int i = 0; i < unstagedFileNames.count(); i++)
- m_requestedRevisionRanges[unstagedFileNames.at(i)].append(unstagedRange);
+ if (!unstagedFileNames.isEmpty()) {
+ QStringList unstagedArguments;
+ unstagedArguments << QLatin1String("--");
+ unstagedArguments << unstagedFileNames;
+ arguments << unstagedArguments;
+ }
- prepareForCollection();
- collectFilesContents();
+ postCollectDiffOutput(arguments);
}
void GitDiffHandler::diffProjects(const QStringList &projectPaths)
{
- m_requestedRevisionRange = RevisionRange(
- Revision(Index),
- Revision(WorkingTree));
-
- collectFilesList(QStringList() << QLatin1String("--") << projectPaths);
+ postCollectDiffOutput(QStringList() << QLatin1String("--") << projectPaths);
}
void GitDiffHandler::diffRepository()
{
- m_requestedRevisionRange = RevisionRange(
- Revision(Index),
- Revision(WorkingTree));
-
- collectFilesList(QStringList());
+ postCollectDiffOutput(QStringList());
}
void GitDiffHandler::diffBranch(const QString &branchName)
{
- m_requestedRevisionRange = RevisionRange(
- Revision(Other, branchName),
- Revision(WorkingTree));
-
- collectFilesList(QStringList() << branchName);
+ postCollectDiffOutput(QStringList() << branchName);
}
void GitDiffHandler::show(const QString &id)
{
- Revision begin(Other, id + QLatin1Char('^'));
- Revision end(Other, id);
- m_requestedRevisionRange = RevisionRange(begin, end);
-
- collectShowDescription(id);
+ m_id = id;
+ postCollectShowDescription(id);
}
-void GitDiffHandler::collectShowDescription(const QString &id)
+void GitDiffHandler::postCollectShowDescription(const QString &id)
{
if (m_editorController.isNull()) {
deleteLater();
@@ -422,14 +204,20 @@ void GitDiffHandler::collectShowDescription(const QString &id)
}
m_editorController->clear(m_waitMessage);
- VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
- command->setCodec(GitPlugin::instance()->gitClient()->encoding(m_workingDirectory,
- "i18n.commitEncoding"));
- connect(command, SIGNAL(output(QString)), this, SLOT(slotShowDescriptionReceived(QString)));
+ VcsBase::Command *command = new VcsBase::Command(gitPath(),
+ m_workingDirectory,
+ processEnvironment());
+ command->setCodec(m_gitClient->encoding(m_workingDirectory,
+ "i18n.commitEncoding"));
+ connect(command, SIGNAL(output(QString)),
+ this, SLOT(slotShowDescriptionReceived(QString)));
QStringList arguments;
- arguments << QLatin1String("show") << QLatin1String("-s")
- << QLatin1String(noColorOption) << QLatin1String(decorateOption) << id;
- command->addJob(arguments, m_timeout);
+ arguments << QLatin1String("show")
+ << QLatin1String("-s")
+ << QLatin1String(noColorOption)
+ << QLatin1String(decorateOption)
+ << id;
+ command->addJob(arguments, timeout());
command->execute();
}
@@ -440,15 +228,32 @@ void GitDiffHandler::slotShowDescriptionReceived(const QString &description)
return;
}
- m_editorController->setDescription(GitPlugin::instance()->gitClient()->
- extendedShowDescription(m_workingDirectory, description));
+ postCollectDiffOutput(QStringList() << m_id + QLatin1Char('^') << m_id);
+
+ // need to be called after postCollectDiffOutput(), since it clears the description
+ m_editorController->setDescription(
+ m_gitClient->extendedShowDescription(m_workingDirectory,
+ description));
+}
+
+void GitDiffHandler::addJob(VcsBase::Command *command, const QStringList &arguments)
+{
+ QStringList args;
+ args << QLatin1String("diff");
+ if (m_editorController->isIgnoreWhitespace())
+ args << QLatin1String("--ignore-space-change");
+ args << QLatin1String("--unified=") + QString::number(
+ m_editorController->contextLinesNumber());
+ args << arguments;
+ command->addJob(args, timeout());
+}
- collectFilesList(QStringList()
- << m_requestedRevisionRange.begin.id
- << m_requestedRevisionRange.end.id);
+void GitDiffHandler::postCollectDiffOutput(const QStringList &arguments)
+{
+ postCollectDiffOutput(QList<QStringList>() << arguments);
}
-void GitDiffHandler::collectFilesList(const QStringList &additionalArguments)
+void GitDiffHandler::postCollectDiffOutput(const QList<QStringList> &argumentsList)
{
if (m_editorController.isNull()) {
deleteLater();
@@ -456,175 +261,145 @@ void GitDiffHandler::collectFilesList(const QStringList &additionalArguments)
}
m_editorController->clear(m_waitMessage);
- VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
+ VcsBase::Command *command = new VcsBase::Command(gitPath(),
+ m_workingDirectory,
+ processEnvironment());
command->setCodec(Core::EditorManager::defaultTextCodec());
- connect(command, SIGNAL(output(QString)), this, SLOT(slotFileListReceived(QString)));
- QStringList arguments;
- arguments << QLatin1String("diff") << QLatin1String("--name-only") << additionalArguments;
- command->addJob(arguments, m_timeout);
+ connect(command, SIGNAL(output(QString)),
+ this, SLOT(slotDiffOutputReceived(QString)));
command->addFlags(diffExecutionFlags());
+
+ for (int i = 0; i < argumentsList.count(); i++)
+ addJob(command, argumentsList.at(i));
+
command->execute();
}
-void GitDiffHandler::slotFileListReceived(const QString &fileList)
+void GitDiffHandler::slotDiffOutputReceived(const QString &contents)
{
if (m_editorController.isNull()) {
deleteLater();
return;
}
- QStringList fileNames = fileList.split(QLatin1Char('\n'), QString::SkipEmptyParts);
- fileNames.removeDuplicates();
-
- for (int i = 0; i < fileNames.count(); i++)
- m_requestedRevisionRanges[fileNames.at(i)].append(m_requestedRevisionRange);
-
- prepareForCollection();
- collectFilesContents();
+ bool ok;
+ QList<DiffEditor::FileData> fileDataList
+ = DiffEditor::DiffUtils::readPatch(
+ contents, m_editorController->isIgnoreWhitespace(), &ok);
+ m_editorController->setDiffFiles(fileDataList, m_workingDirectory);
+ deleteLater();
}
-void GitDiffHandler::prepareForCollection()
+int GitDiffHandler::timeout() const
{
- QMap<QString, QList<RevisionRange> >::const_iterator it
- = m_requestedRevisionRanges.constBegin();
- QMap<QString, QList<RevisionRange> >::const_iterator itEnd
- = m_requestedRevisionRanges.constEnd();
- while (it != itEnd) {
- const QString fileName = it.key();
- const QList<RevisionRange> &ranges = it.value();
- for (int i = 0; i < ranges.count(); i++) {
- const RevisionRange &range = ranges.at(i);
- m_pendingRevisions[fileName][range.begin] = false;
- m_pendingRevisions[fileName][range.end] = false;
- }
-
- ++it;
- }
+ return m_gitClient->settings()->intValue(GitSettings::timeoutKey);
}
-void GitDiffHandler::collectFilesContents()
+QProcessEnvironment GitDiffHandler::processEnvironment() const
{
- QMap<QString, QMap<Revision, bool> >::iterator itFile
- = m_pendingRevisions.begin();
- QMap<QString, QMap<Revision, bool> >::iterator itFileEnd
- = m_pendingRevisions.end();
- while (itFile != itFileEnd) {
- const QString fileName = itFile.key();
- QMap<Revision, bool> &revisions = itFile.value();
- QMap<Revision, bool>::iterator itRev
- = revisions.begin();
- QMap<Revision, bool>::iterator itRevEnd
- = revisions.end();
- while (itRev != itRevEnd) {
- const Revision revision = itRev.key();
- if (revision.type == WorkingTree) {
- // collect file here
-
- m_collectedRevisions[fileName][revision] = workingTreeContents(fileName);
-
- itRev = revisions.erase(itRev); // iterate to the next revision
- } else {
- // prepare job here
+ return m_gitClient->processEnvironment();
+}
- VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
- command->setCodec(Core::EditorManager::defaultTextCodec());
- connect(command, SIGNAL(output(QString)), this, SLOT(slotFileContentsReceived(QString)));
+QString GitDiffHandler::gitPath() const
+{
+ return m_gitClient->gitBinaryPath();
+}
- QString revisionArgument = (revision.type == Other)
- ? revision.id : QString();
- revisionArgument += QLatin1Char(':');
- QStringList arguments;
- arguments << QLatin1String("show") << revisionArgument + fileName;
- command->addJob(arguments, m_timeout);
- command->execute();
+/////////////////////////////////////
- return;
- }
- }
+class GitDiffEditorReloader : public DiffEditor::DiffEditorReloader
+{
+ Q_OBJECT
+public:
+ enum DiffType {
+ DiffRepository,
+ DiffFile,
+ DiffFileList,
+ DiffProjectList,
+ DiffBranch,
+ DiffShow
+ };
- itFile = m_pendingRevisions.erase(itFile); // iterate to the next file
+ GitDiffEditorReloader(QObject *parent);
+ void setWorkingDirectory(const QString &workingDir) {
+ m_workingDirectory = workingDir;
}
-
- feedEditor();
-}
-
-void GitDiffHandler::slotFileContentsReceived(const QString &contents)
-{
- if (m_editorController.isNull()) {
- deleteLater();
- return;
+ void setDiffType(DiffType type) { m_diffType = type; }
+ void setFileName(const QString &fileName) { m_fileName = fileName; }
+ void setFileList(const QStringList &stagedFiles,
+ const QStringList &unstagedFiles) {
+ m_stagedFiles = stagedFiles;
+ m_unstagedFiles = unstagedFiles;
}
-
- QMap<QString, QMap<Revision, bool> >::iterator itFile
- = m_pendingRevisions.begin();
- QMap<QString, QMap<Revision, bool> >::iterator itFileEnd
- = m_pendingRevisions.end();
- if (itFile != itFileEnd) {
- const QString fileName = itFile.key();
- QMap<Revision, bool> &revisions = itFile.value();
- QMap<Revision, bool>::iterator itRev
- = revisions.begin();
- QMap<Revision, bool>::iterator itRevEnd
- = revisions.end();
- if (itRev != itRevEnd) {
- m_collectedRevisions[fileName][itRev.key()] = contents;
-
- itRev = revisions.erase(itRev);
- if (revisions.isEmpty())
- m_pendingRevisions.erase(itFile);
- }
+ void setProjectList(const QStringList &projectFiles) {
+ m_projectFiles = projectFiles;
+ }
+ void setBranchName(const QString &branchName) {
+ m_branchName = branchName;
+ }
+ void setId(const QString &id) { m_id = id; }
+ void setDisplayName(const QString &displayName) {
+ m_displayName = displayName;
}
- collectFilesContents();
-}
-void GitDiffHandler::feedEditor()
-{
- if (m_editorController.isNull()) {
- deleteLater();
- return;
- }
+protected:
+ void reload();
- QList<DiffEditor::DiffEditorController::DiffFilesContents> list;
-
- QMap<QString, QList<RevisionRange> >::const_iterator itFile
- = m_requestedRevisionRanges.constBegin();
- QMap<QString, QList<RevisionRange> >::const_iterator itFileEnd
- = m_requestedRevisionRanges.constEnd();
- while (itFile != itFileEnd) {
- const QString fileName = itFile.key();
- const QList<RevisionRange> &ranges = itFile.value();
- for (int i = 0; i < ranges.count(); i++) {
- const Revision leftRevision = ranges.at(i).begin;
- const Revision rightRevision = ranges.at(i).end;
-
- DiffEditor::DiffEditorController::DiffFilesContents dfc;
- dfc.leftFileInfo = DiffEditor::DiffEditorController::DiffFileInfo(fileName, leftRevision.infoText());
- dfc.leftText = m_collectedRevisions[fileName][leftRevision];
- dfc.rightFileInfo = DiffEditor::DiffEditorController::DiffFileInfo(fileName, rightRevision.infoText());
- dfc.rightText = m_collectedRevisions[fileName][rightRevision];
- list.append(dfc);
- }
+private:
+ GitClient *m_gitClient;
- ++itFile;
- }
+ QString m_workingDirectory;
+ DiffType m_diffType;
+ QString m_fileName;
+ QStringList m_stagedFiles;
+ QStringList m_unstagedFiles;
+ QStringList m_projectFiles;
+ QString m_branchName;
+ QString m_id;
+ QString m_displayName;
+};
- m_editorController->setDiffContents(list, m_workingDirectory);
- deleteLater();
+GitDiffEditorReloader::GitDiffEditorReloader(QObject *parent)
+ : DiffEditorReloader(parent),
+ m_gitClient(GitPlugin::instance()->gitClient())
+{
}
-QString GitDiffHandler::workingTreeContents(const QString &fileName) const
+void GitDiffEditorReloader::reload()
{
- QDir workingDir(m_workingDirectory);
- QString absoluteFileName = workingDir.absoluteFilePath(fileName);
+ const QString workingDirectory = m_diffType == DiffShow
+ ? m_gitClient->findRepositoryForDirectory(m_workingDirectory)
+ : m_workingDirectory;
+ GitDiffHandler *handler = new GitDiffHandler(diffEditorController(),
+ workingDirectory);
+ connect(handler, SIGNAL(destroyed()), this, SLOT(reloadFinished()));
- QFile file(absoluteFileName);
- if (file.open(QIODevice::ReadOnly | QIODevice::Text))
- return Core::EditorManager::defaultTextCodec()->toUnicode(file.readAll());
- return QString();
+ switch (m_diffType) {
+ case DiffRepository:
+ handler->diffRepository();
+ break;
+ case DiffFile:
+ handler->diffFile(m_fileName);
+ break;
+ case DiffFileList:
+ handler->diffFiles(m_stagedFiles, m_unstagedFiles);
+ break;
+ case DiffProjectList:
+ handler->diffProjects(m_projectFiles);
+ break;
+ case DiffBranch:
+ handler->diffBranch(m_branchName);
+ break;
+ case DiffShow:
+ handler->show(m_id);
+ break;
+ default:
+ break;
+ }
}
-///////////////////////////////////////////////////////////
+///////////////////////////////
class BaseGitDiffArgumentsWidget : public VcsBase::VcsBaseEditorParameterWidget
{
@@ -639,12 +414,17 @@ public:
QTC_ASSERT(!directory.isEmpty(), return);
QTC_ASSERT(m_client, return);
- m_patienceButton = addToggleButton(QLatin1String("--patience"), tr("Patience"),
- tr("Use the patience algorithm for calculating the differences."));
- mapSetting(m_patienceButton, client->settings()->boolPointer(GitSettings::diffPatienceKey));
- m_ignoreWSButton = addToggleButton(QLatin1String("--ignore-space-change"), tr("Ignore Whitespace"),
- tr("Ignore whitespace only changes."));
- mapSetting(m_ignoreWSButton, m_client->settings()->boolPointer(GitSettings::ignoreSpaceChangesInDiffKey));
+ m_patienceButton = addToggleButton(
+ QLatin1String("--patience"),
+ tr("Patience"),
+ tr("Use the patience algorithm for calculating the differences."));
+ mapSetting(m_patienceButton, client->settings()->boolPointer(
+ GitSettings::diffPatienceKey));
+ m_ignoreWSButton = addToggleButton(
+ QLatin1String("--ignore-space-change"), tr("Ignore Whitespace"),
+ tr("Ignore whitespace only changes."));
+ mapSetting(m_ignoreWSButton,
+ m_client->settings()->boolPointer(GitSettings::ignoreSpaceChangesInDiffKey));
setBaseArguments(args);
}
@@ -656,109 +436,6 @@ protected:
QToolButton *m_ignoreWSButton;
};
-class GitCommitDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
-{
- Q_OBJECT
-
-public:
- GitCommitDiffArgumentsWidget(Git::Internal::GitClient *client, const QString &directory,
- const QStringList &unstaged, const QStringList &staged) :
- BaseGitDiffArgumentsWidget(client, directory, QStringList())
- {
- setFileNames(unstaged, staged);
- }
-
- void setFileNames(const QStringList &unstaged, const QStringList &staged)
- {
- m_unstagedFileNames = unstaged;
- m_stagedFileNames = staged;
- }
-
- void executeCommand()
- {
- m_client->diff(m_workingDirectory, m_unstagedFileNames, m_stagedFileNames);
- }
-
-private:
- QStringList m_unstagedFileNames;
- QStringList m_stagedFileNames;
-};
-
-class GitFileDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
-{
- Q_OBJECT
-public:
- GitFileDiffArgumentsWidget(Git::Internal::GitClient *client, const QString &directory,
- const QString &file) :
- BaseGitDiffArgumentsWidget(client, directory, QStringList()),
- m_fileName(file)
- { }
-
- void executeCommand()
- {
- m_client->diff(m_workingDirectory, m_fileName);
- }
-
-private:
- const QString m_fileName;
-};
-
-class GitBranchDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
-{
- Q_OBJECT
-public:
- GitBranchDiffArgumentsWidget(Git::Internal::GitClient *client, const QString &directory,
- const QStringList &args, const QString &branch) :
- BaseGitDiffArgumentsWidget(client, directory, args),
- m_branchName(branch)
- { }
-
- void executeCommand()
- {
- m_client->diffBranch(m_workingDirectory, baseArguments(), m_branchName);
- }
-
-private:
- const QString m_branchName;
-};
-
-class GitShowArgumentsWidget : public BaseGitDiffArgumentsWidget
-{
- Q_OBJECT
-
-public:
- GitShowArgumentsWidget(Git::Internal::GitClient *client,
- const QString &directory,
- const QStringList &args,
- const QString &id) :
- BaseGitDiffArgumentsWidget(client, directory, args),
- m_client(client),
- m_workingDirectory(directory),
- m_id(id)
- {
- QList<ComboBoxItem> prettyChoices;
- prettyChoices << ComboBoxItem(tr("oneline"), QLatin1String("oneline"))
- << ComboBoxItem(tr("short"), QLatin1String("short"))
- << ComboBoxItem(tr("medium"), QLatin1String("medium"))
- << ComboBoxItem(tr("full"), QLatin1String("full"))
- << ComboBoxItem(tr("fuller"), QLatin1String("fuller"))
- << ComboBoxItem(tr("email"), QLatin1String("email"))
- << ComboBoxItem(tr("raw"), QLatin1String("raw"));
- mapSetting(addComboBox(QStringList(QLatin1String("--pretty=%1")), prettyChoices),
- m_client->settings()->intPointer(GitSettings::showPrettyFormatKey));
- }
-
- void executeCommand()
- {
- m_client->show(m_workingDirectory, m_id, baseArguments());
- }
-
-private:
- GitClient *m_client;
- QString m_workingDirectory;
- QString m_id;
-};
-
class GitBlameArgumentsWidget : public VcsBase::VcsBaseEditorParameterWidget
{
Q_OBJECT
@@ -1021,7 +698,9 @@ GitClient::GitClient(GitSettings *settings) :
m_cachedGitVersion(0),
m_msgWait(tr("Waiting for data...")),
m_settings(settings),
- m_disableEditor(false)
+ m_disableEditor(false),
+ m_contextDiffFileIndex(-1),
+ m_contextChunkIndex(-1)
{
QTC_CHECK(settings);
connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), this, SLOT(saveSettings()));
@@ -1102,9 +781,122 @@ DiffEditor::DiffEditorDocument *GitClient::createDiffEditor(const QString &docum
DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::findOrCreate(documentId, title);
QTC_ASSERT(diffEditorDocument, return 0);
VcsBasePlugin::setSource(diffEditorDocument, source);
+
+ connect(diffEditorDocument->controller(), SIGNAL(chunkActionsRequested(QMenu*,int,int)),
+ this, SLOT(slotChunkActionsRequested(QMenu*,int,int)));
+
return diffEditorDocument;
}
+void GitClient::slotChunkActionsRequested(QMenu *menu, int diffFileIndex, int chunkIndex)
+{
+ menu->addSeparator();
+ QAction *stageChunkAction = menu->addAction(tr("Stage Chunk"));
+ connect(stageChunkAction, SIGNAL(triggered()), this, SLOT(slotStageChunk()));
+ QAction *unstageChunkAction = menu->addAction(tr("Unstage Chunk"));
+ connect(unstageChunkAction, SIGNAL(triggered()), this, SLOT(slotUnstageChunk()));
+
+ m_contextDiffFileIndex = diffFileIndex;
+ m_contextChunkIndex = chunkIndex;
+ m_contextDocument = qobject_cast<DiffEditor::DiffEditorController *>(sender());
+
+ if (m_contextDiffFileIndex < 0 || m_contextChunkIndex < 0 || !m_contextDocument) {
+ stageChunkAction->setEnabled(false);
+ unstageChunkAction->setEnabled(false);
+ }
+}
+
+QString GitClient::makePatch(int diffFileIndex, int chunkIndex, bool revert) const
+{
+ if (m_contextDocument.isNull())
+ return QString();
+
+ if (diffFileIndex < 0 || chunkIndex < 0)
+ return QString();
+
+ QList<DiffEditor::FileData> fileDataList = m_contextDocument->diffFiles();
+
+ if (diffFileIndex >= fileDataList.count())
+ return QString();
+
+ const DiffEditor::FileData fileData = fileDataList.at(diffFileIndex);
+ if (chunkIndex >= fileData.chunks.count())
+ return QString();
+
+ const DiffEditor::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 DiffEditor::DiffUtils::makePatch(chunkData,
+ QLatin1String("a/") + fileName,
+ QLatin1String("b/") + fileName,
+ lastChunk && fileData.lastChunkAtTheEndOfFile);
+}
+
+void GitClient::slotStageChunk()
+{
+ if (m_contextDocument.isNull())
+ return;
+
+ const QString patch = makePatch(m_contextDiffFileIndex,
+ m_contextChunkIndex, false);
+ if (patch.isEmpty())
+ return;
+
+ stage(patch, false);
+}
+
+void GitClient::slotUnstageChunk()
+{
+ if (m_contextDocument.isNull())
+ return;
+
+ const QString patch = makePatch(m_contextDiffFileIndex,
+ m_contextChunkIndex, true);
+ if (patch.isEmpty())
+ return;
+
+ stage(patch, true);
+}
+
+void GitClient::stage(const QString &patch, bool revert)
+{
+ VcsBase::VcsBaseOutputWindow *outwin =
+ VcsBase::VcsBaseOutputWindow::instance();
+ QTemporaryFile patchFile;
+ if (!patchFile.open())
+ return;
+
+ const QString baseDir = m_contextDocument->workingDirectory();
+ QTextCodec *codec = Core::EditorManager::defaultTextCodec();
+ const QByteArray patchData = codec
+ ? codec->fromUnicode(patch) : patch.toLocal8Bit();
+ patchFile.write(patchData);
+ patchFile.close();
+
+ QStringList args = QStringList() << QLatin1String("--cached");
+ if (revert)
+ args << QLatin1String("--reverse");
+ QString errorMessage;
+ if (synchronousApplyPatch(baseDir, patchFile.fileName(),
+ &errorMessage, args)) {
+ if (errorMessage.isEmpty()) {
+ if (revert)
+ outwin->append(tr("Chunk successfully unstaged"));
+ else
+ outwin->append(tr("Chunk successfully staged"));
+ } else {
+ outwin->append(errorMessage);
+ }
+ m_contextDocument->requestReload();
+ } else {
+ outwin->appendError(errorMessage);
+ }
+}
+
/* Create an editor associated to VCS output of a source file/directory
* (using the file's codec). Makes use of a dynamic property to find an
* existing instance and to reuse it (in case, say, 'git diff foo' is
@@ -1149,235 +941,98 @@ void GitClient::diff(const QString &workingDirectory,
const QStringList &unstagedFileNames,
const QStringList &stagedFileNames)
{
- const QString title = tr("Git Diff");
- const int timeout = settings()->intValue(GitSettings::timeoutKey);
- Core::IDocument *newDocument = 0;
- if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
- const QString documentId = QLatin1String("sideBySideOriginalFileName") + workingDirectory;
- DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::find(documentId);
- if (!diffEditorDocument)
- newDocument = diffEditorDocument = createDiffEditor(documentId, workingDirectory, title);
-
- Core::EditorManager::activateEditorForDocument(diffEditorDocument);
-
- GitDiffHandler *handler = new GitDiffHandler(diffEditorDocument->controller(),
- gitBinaryPath(),
- workingDirectory,
- processEnvironment(),
- timeout);
-
- if (unstagedFileNames.empty() && stagedFileNames.empty()) {
- // local repository diff
- handler->diffRepository();
- } else if (!stagedFileNames.empty()) {
- // diff of selected files only with --cached option, used in commit editor
- handler->diffFiles(stagedFileNames, unstagedFileNames);
- } else {
- // current project diff
- handler->diffProjects(unstagedFileNames);
- }
- } else {
- const QString binary = settings()->stringValue(GitSettings::binaryPathKey);
- const char *propertyName = "originalFileName";
- VcsBase::VcsBaseEditorWidget *vcsEditor = findExistingVCSEditor(propertyName, workingDirectory);
- if (!vcsEditor) {
- GitCommitDiffArgumentsWidget *argWidget =
- new GitCommitDiffArgumentsWidget(this,
- workingDirectory,
- unstagedFileNames,
- stagedFileNames);
- vcsEditor = createVcsEditor(Git::Constants::GIT_DIFF_EDITOR_ID,
- title,
- workingDirectory,
- CodecSource,
- propertyName,
- workingDirectory,
- argWidget);
- newDocument = vcsEditor->editor()->document();
- connect(vcsEditor, SIGNAL(diffChunkApplied(VcsBase::DiffChunk)),
- argWidget, SLOT(executeCommand()));
- connect(vcsEditor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)),
- argWidget, SLOT(executeCommand()));
- }
+ GitDiffEditorReloader::DiffType diffType = GitDiffEditorReloader::DiffProjectList;
- GitCommitDiffArgumentsWidget *argWidget = qobject_cast<GitCommitDiffArgumentsWidget *>(
- vcsEditor->configurationWidget());
- argWidget->setFileNames(unstagedFileNames, stagedFileNames);
- QStringList userDiffArgs = argWidget->arguments();
- vcsEditor->setWorkingDirectory(workingDirectory);
-
- // Create a batch of 2 commands to be run after each other in case
- // we have a mixture of staged/unstaged files as is the case
- // when using the submit dialog.
- VcsBase::Command *command = createCommand(workingDirectory, vcsEditor);
- // Directory diff?
-
- QStringList cmdArgs;
- cmdArgs << QLatin1String("diff")
- << QLatin1String(noColorOption);
-
- if (unstagedFileNames.empty() && stagedFileNames.empty()) {
- QStringList arguments(cmdArgs);
- arguments << userDiffArgs;
- outputWindow()->appendCommand(workingDirectory, binary, arguments);
- command->addJob(arguments, timeout);
- } else {
- // Files diff.
- if (!unstagedFileNames.empty()) {
- QStringList arguments(cmdArgs);
- arguments << userDiffArgs
- << QLatin1String("--")
- << unstagedFileNames;
- outputWindow()->appendCommand(workingDirectory, binary, arguments);
- command->addJob(arguments, timeout);
- }
- if (!stagedFileNames.empty()) {
- QStringList arguments(cmdArgs);
- arguments << userDiffArgs
- << QLatin1String("--cached")
- << QLatin1String("--")
- << stagedFileNames;
- outputWindow()->appendCommand(workingDirectory, binary, arguments);
- command->addJob(arguments, timeout);
- }
- }
- command->addFlags(diffExecutionFlags());
- command->execute();
- }
- if (newDocument) {
- GitDiffSwitcher *switcher = new GitDiffSwitcher(newDocument, this);
- switcher->setWorkingDirectory(workingDirectory);
- if (unstagedFileNames.empty() && stagedFileNames.empty()) {
- // local repository diff
- switcher->setDiffType(GitDiffSwitcher::DiffRepository);
- } else if (!stagedFileNames.empty()) {
- // diff of selected files only with --cached option, used in commit editor
- switcher->setDiffType(GitDiffSwitcher::DiffFileList);
- switcher->setFileList(stagedFileNames, unstagedFileNames);
- } else {
- // current project diff
- switcher->setDiffType(GitDiffSwitcher::DiffProjectList);
- switcher->setProjectList(unstagedFileNames);
- }
+ if (unstagedFileNames.empty() && stagedFileNames.empty())
+ diffType = GitDiffEditorReloader::DiffRepository;
+ else if (!stagedFileNames.empty())
+ diffType = GitDiffEditorReloader::DiffFileList;
+
+ QString title = tr("Git Diff Projects");
+ QString documentTypeId = QLatin1String("Projects:");
+ if (diffType == GitDiffEditorReloader::DiffRepository) {
+ title = tr("Git Diff Repository");
+ documentTypeId = QLatin1String("Repository:");
+ } else if (diffType == GitDiffEditorReloader::DiffFileList) {
+ title = tr("Git Diff Files");
+ documentTypeId = QLatin1String("Files:");
+ }
+
+ const QString documentId = documentTypeId + workingDirectory;
+
+ DiffEditor::DiffEditorDocument *diffEditorDocument =
+ DiffEditor::DiffEditorManager::find(documentId);
+ if (!diffEditorDocument) {
+ diffEditorDocument = createDiffEditor(documentId, workingDirectory, title);
+
+ GitDiffEditorReloader *reloader =
+ new GitDiffEditorReloader(diffEditorDocument->controller());
+ reloader->setDiffEditorController(diffEditorDocument->controller());
+
+ reloader->setWorkingDirectory(workingDirectory);
+ reloader->setDiffType(diffType);
+ if (diffType == GitDiffEditorReloader::DiffFileList)
+ reloader->setFileList(stagedFileNames, unstagedFileNames);
+ else if (diffType == GitDiffEditorReloader::DiffProjectList)
+ reloader->setProjectList(unstagedFileNames);
}
+
+ diffEditorDocument->controller()->requestReload();
+
+ Core::EditorManager::activateEditorForDocument(diffEditorDocument);
}
void GitClient::diff(const QString &workingDirectory, const QString &fileName)
{
const QString title = tr("Git Diff \"%1\"").arg(fileName);
- const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
- Core::IDocument *newDocument = 0;
- if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
- const QString documentId = QLatin1String("sideBySideOriginalFileName") + sourceFile;
- DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::find(documentId);
- if (!diffEditorDocument)
- newDocument = diffEditorDocument = createDiffEditor(documentId, sourceFile, title);
-
- Core::EditorManager::activateEditorForDocument(diffEditorDocument);
-
- GitDiffHandler *handler = new GitDiffHandler(diffEditorDocument->controller(),
- gitBinaryPath(),
- workingDirectory,
- processEnvironment(),
- settings()->intValue(GitSettings::timeoutKey));
- handler->diffFile(fileName);
- } else {
- const char *propertyName = "originalFileName";
- VcsBase::VcsBaseEditorWidget *vcsEditor = findExistingVCSEditor(propertyName, sourceFile);
- if (!vcsEditor) {
- GitFileDiffArgumentsWidget *argWidget =
- new GitFileDiffArgumentsWidget(this, workingDirectory, fileName);
-
- vcsEditor = createVcsEditor(Git::Constants::GIT_DIFF_EDITOR_ID,
- title,
- sourceFile,
- CodecSource,
- propertyName,
- sourceFile,
- argWidget);
- newDocument = vcsEditor->editor()->document();
- connect(vcsEditor, SIGNAL(diffChunkApplied(VcsBase::DiffChunk)),
- argWidget, SLOT(executeCommand()));
- connect(vcsEditor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)),
- argWidget, SLOT(executeCommand()));
- }
- vcsEditor->setWorkingDirectory(workingDirectory);
+ const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(
+ workingDirectory, fileName);
+ const QString documentId = QLatin1String("File:") + sourceFile;
+ DiffEditor::DiffEditorDocument *diffEditorDocument =
+ DiffEditor::DiffEditorManager::find(documentId);
+ if (!diffEditorDocument) {
+ diffEditorDocument = createDiffEditor(documentId, sourceFile, title);
- QStringList cmdArgs;
- cmdArgs << QLatin1String("diff")
- << QLatin1String(noColorOption)
- << vcsEditor->configurationWidget()->arguments();
+ GitDiffEditorReloader *reloader =
+ new GitDiffEditorReloader(diffEditorDocument->controller());
+ reloader->setDiffEditorController(diffEditorDocument->controller());
- if (!fileName.isEmpty())
- cmdArgs << QLatin1String("--") << fileName;
- executeGit(workingDirectory, cmdArgs, vcsEditor, false, diffExecutionFlags());
- }
- if (newDocument) {
- GitDiffSwitcher *switcher = new GitDiffSwitcher(newDocument, this);
- switcher->setWorkingDirectory(workingDirectory);
- switcher->setDiffType(GitDiffSwitcher::DiffFile);
- switcher->setFileName(fileName);
+ reloader->setWorkingDirectory(workingDirectory);
+ reloader->setDiffType(GitDiffEditorReloader::DiffFile);
+ reloader->setFileName(fileName);
}
+
+ diffEditorDocument->controller()->requestReload();
+
+ Core::EditorManager::activateEditorForDocument(diffEditorDocument);
}
void GitClient::diffBranch(const QString &workingDirectory,
- const QStringList &diffArgs,
const QString &branchName)
{
const QString title = tr("Git Diff Branch \"%1\"").arg(branchName);
- Core::IDocument *newDocument = 0;
- if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
- const QString documentId = QLatin1String("sideBySideBranchName") + branchName;
- DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::find(documentId);
- if (!diffEditorDocument)
- newDocument = diffEditorDocument = createDiffEditor(documentId, workingDirectory, title);
-
- Core::EditorManager::activateEditorForDocument(diffEditorDocument);
-
- GitDiffHandler *handler = new GitDiffHandler(diffEditorDocument->controller(),
- gitBinaryPath(),
- workingDirectory,
- processEnvironment(),
- settings()->intValue(GitSettings::timeoutKey));
- handler->diffBranch(branchName);
- } else {
- const char *propertyName = "BranchName";
- const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, QStringList());
-
- VcsBase::VcsBaseEditorWidget *vcsEditor = findExistingVCSEditor(propertyName, branchName);
- if (!vcsEditor) {
- vcsEditor = createVcsEditor(Git::Constants::GIT_DIFF_EDITOR_ID,
- title,
- sourceFile,
- CodecSource,
- propertyName,
- branchName,
- new GitBranchDiffArgumentsWidget(this,
- workingDirectory,
- diffArgs,
- branchName));
- newDocument = vcsEditor->editor()->document();
- }
- vcsEditor->setWorkingDirectory(workingDirectory);
+ const QString documentId = QLatin1String("Branch:") + branchName;
+ DiffEditor::DiffEditorDocument *diffEditorDocument =
+ DiffEditor::DiffEditorManager::find(documentId);
+ if (!diffEditorDocument) {
+ diffEditorDocument = createDiffEditor(documentId, workingDirectory, title);
- QStringList cmdArgs;
- cmdArgs << QLatin1String("diff")
- << QLatin1String(noColorOption)
- << vcsEditor->configurationWidget()->arguments()
- << branchName;
+ GitDiffEditorReloader *reloader =
+ new GitDiffEditorReloader(diffEditorDocument->controller());
+ reloader->setDiffEditorController(diffEditorDocument->controller());
- executeGit(workingDirectory, cmdArgs, vcsEditor, false, diffExecutionFlags());
- }
- if (newDocument) {
- GitDiffSwitcher *switcher = new GitDiffSwitcher(newDocument, this);
- switcher->setWorkingDirectory(workingDirectory);
- switcher->setDiffType(GitDiffSwitcher::DiffBranch);
- switcher->setBaseArguments(diffArgs);
- switcher->setBranchName(branchName);
+ reloader->setWorkingDirectory(workingDirectory);
+ reloader->setDiffType(GitDiffEditorReloader::DiffBranch);
+ reloader->setBranchName(branchName);
}
+
+ diffEditorDocument->controller()->requestReload();
+
+ Core::EditorManager::activateEditorForDocument(diffEditorDocument);
}
-void GitClient::merge(const QString &workingDirectory, const QStringList &unmergedFileNames)
+void GitClient::merge(const QString &workingDirectory,
+ const QStringList &unmergedFileNames)
{
MergeTool *mergeTool = new MergeTool(this);
if (!mergeTool->start(workingDirectory, unmergedFileNames))
@@ -1469,8 +1124,7 @@ static inline QString msgCannotShow(const QString &sha)
return GitClient::tr("Cannot describe \"%1\".").arg(sha);
}
-void GitClient::show(const QString &source, const QString &id,
- const QStringList &args, const QString &name)
+void GitClient::show(const QString &source, const QString &id, const QString &name)
{
if (!canShow(id)) {
outputWindow()->appendError(msgCannotShow(id));
@@ -1479,59 +1133,28 @@ void GitClient::show(const QString &source, const QString &id,
const QString title = tr("Git Show \"%1\"").arg(name.isEmpty() ? id : name);
const QFileInfo sourceFi(source);
- const QString workingDirectory = sourceFi.isDir() ? sourceFi.absoluteFilePath() : sourceFi.absolutePath();
- Core::IDocument *newDocument = 0;
- if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
- const QString documentId = QLatin1String("sideBySideShow") + id;
- DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::find(documentId);
- if (!diffEditorDocument)
- newDocument = diffEditorDocument = createDiffEditor(documentId, source, title);
-
+ const QString workingDirectory = sourceFi.isDir()
+ ? sourceFi.absoluteFilePath() : sourceFi.absolutePath();
+ const QString documentId = QLatin1String("Show:") + id;
+ DiffEditor::DiffEditorDocument *diffEditorDocument =
+ DiffEditor::DiffEditorManager::find(documentId);
+ if (!diffEditorDocument) {
+ diffEditorDocument = createDiffEditor(documentId, source, title);
diffEditorDocument->controller()->setDescriptionEnabled(true);
- Core::EditorManager::activateEditorForDocument(diffEditorDocument);
-
- GitDiffHandler *handler = new GitDiffHandler(diffEditorDocument->controller(),
- gitBinaryPath(),
- findRepositoryForDirectory(workingDirectory),
- processEnvironment(),
- settings()->intValue(GitSettings::timeoutKey));
- handler->show(id);
- } else {
- const char *propertyName = "show";
- const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
- VcsBase::VcsBaseEditorWidget *vcsEditor = findExistingVCSEditor(propertyName, id);
- if (!vcsEditor) {
- vcsEditor = createVcsEditor(editorId,
- title,
- source,
- CodecSource,
- propertyName,
- id,
- new GitShowArgumentsWidget(this,
- source,
- args,
- id));
- newDocument = vcsEditor->editor()->document();
- }
+ GitDiffEditorReloader *reloader =
+ new GitDiffEditorReloader(diffEditorDocument->controller());
+ reloader->setDiffEditorController(diffEditorDocument->controller());
- QStringList arguments;
- arguments << QLatin1String("show")
- << QLatin1String(noColorOption)
- << QLatin1String(decorateOption)
- << vcsEditor->configurationWidget()->arguments()
- << id;
-
- vcsEditor->setWorkingDirectory(workingDirectory);
- executeGit(workingDirectory, arguments, vcsEditor);
- }
- if (newDocument) {
- GitDiffSwitcher *switcher = new GitDiffSwitcher(newDocument, this);
- switcher->setDiffType(GitDiffSwitcher::DiffShow);
- switcher->setFileName(source);
- switcher->setBaseArguments(args);
- switcher->setId(id);
+ reloader->setWorkingDirectory(workingDirectory);
+ reloader->setDiffType(GitDiffEditorReloader::DiffShow);
+ reloader->setFileName(source);
+ reloader->setId(id);
}
+
+ diffEditorDocument->controller()->requestReload();
+
+ Core::EditorManager::activateEditorForDocument(diffEditorDocument);
}
void GitClient::saveSettings()
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index 85d50c796c..810019355a 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -45,6 +45,7 @@ class QCheckBox;
class QSignalMapper;
class QDebug;
class QProcessEnvironment;
+class QMenu;
QT_END_NAMESPACE
namespace Core { class ICore; }
@@ -58,7 +59,10 @@ namespace VcsBase {
namespace Utils { struct SynchronousProcessResponse; }
-namespace DiffEditor { class DiffEditorDocument; }
+namespace DiffEditor {
+class DiffEditorDocument;
+class DiffEditorController;
+}
namespace Git {
namespace Internal {
@@ -141,7 +145,6 @@ public:
const QStringList &unstagedFileNames,
const QStringList &stagedFileNames = QStringList());
void diffBranch(const QString &workingDirectory,
- const QStringList &diffArgs,
const QString &branchName);
void merge(const QString &workingDirectory, const QStringList &unmergedFileNames = QStringList());
@@ -332,7 +335,6 @@ public:
public slots:
void show(const QString &source,
const QString &id,
- const QStringList &args = QStringList(),
const QString &name = QString());
void saveSettings();
@@ -341,8 +343,13 @@ private slots:
QString change, int lineNumber);
void finishSubmoduleUpdate();
void fetchFinished(const QVariant &cookie);
+ void slotChunkActionsRequested(QMenu *menu, int diffFileIndex, int chunkIndex);
+ void slotStageChunk();
+ void slotUnstageChunk();
private:
+ QString makePatch(int diffFileIndex, int chunkIndex, bool revert) const;
+ void stage(const QString &patch, bool revert);
QByteArray readConfigBytes(const QString &workingDirectory, const QString &configVar) const;
QTextCodec *getSourceCodec(const QString &file) const;
VcsBase::VcsBaseEditorWidget *findExistingVCSEditor(const char *registerDynamicProperty,
@@ -421,6 +428,9 @@ private:
QMap<QString, StashInfo> m_stashInfo;
QStringList m_updatedSubmodules;
bool m_disableEditor;
+ int m_contextDiffFileIndex;
+ int m_contextChunkIndex;
+ QPointer<DiffEditor::DiffEditorController> m_contextDocument;
};
} // namespace Internal
diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp
index 70a1522970..177c49884a 100644
--- a/src/plugins/git/gitsettings.cpp
+++ b/src/plugins/git/gitsettings.cpp
@@ -35,7 +35,6 @@
namespace Git {
namespace Internal {
-const QLatin1String GitSettings::useDiffEditorKey("UseDiffEditor");
const QLatin1String GitSettings::pullRebaseKey("PullRebase");
const QLatin1String GitSettings::showTagsKey("ShowTags");
const QLatin1String GitSettings::omitAnnotationDateKey("OmitAnnotationDate");
@@ -56,7 +55,6 @@ GitSettings::GitSettings()
declareKey(binaryPathKey, QLatin1String("git"));
declareKey(timeoutKey, Utils::HostOsInfo::isWindowsHost() ? 60 : 30);
- declareKey(useDiffEditorKey, true);
declareKey(pullRebaseKey, false);
declareKey(showTagsKey, false);
declareKey(omitAnnotationDateKey, false);
diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h
index 701af49229..0c813caee3 100644
--- a/src/plugins/git/gitsettings.h
+++ b/src/plugins/git/gitsettings.h
@@ -48,7 +48,6 @@ class GitSettings : public VcsBase::VcsBaseClientSettings
public:
GitSettings();
- static const QLatin1String useDiffEditorKey;
static const QLatin1String pullRebaseKey;
static const QLatin1String showTagsKey;
static const QLatin1String omitAnnotationDateKey;
diff --git a/src/plugins/vcsbase/basevcseditorfactory.cpp b/src/plugins/vcsbase/basevcseditorfactory.cpp
index 9045388836..baca25d3cc 100644
--- a/src/plugins/vcsbase/basevcseditorfactory.cpp
+++ b/src/plugins/vcsbase/basevcseditorfactory.cpp
@@ -33,6 +33,8 @@
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/texteditorsettings.h>
+#include <diffeditor/diffeditorconstants.h>
+
#include <QCoreApplication>
#include <QStringList>
@@ -67,7 +69,8 @@ BaseVcsEditorFactory::BaseVcsEditorFactory(const VcsBaseEditorParameters *t,
d->m_describeSlot = describeSlot;
setId(t->id);
setDisplayName(QCoreApplication::translate("VCS", t->displayName));
- addMimeType(t->mimeType);
+ if (QLatin1String(t->mimeType) != QLatin1String(DiffEditor::Constants::DIFF_EDITOR_MIMETYPE))
+ addMimeType(t->mimeType);
new TextEditor::TextEditorActionHandler(this, t->context);
}
@@ -84,7 +87,8 @@ Core::IEditor *BaseVcsEditorFactory::createEditor()
if (d->m_describeReceiver)
connect(vcsEditor, SIGNAL(describeRequested(QString,QString)), d->m_describeReceiver, d->m_describeSlot);
- vcsEditor->baseTextDocument()->setMimeType(mimeTypes().front());
+ if (!mimeTypes().isEmpty())
+ vcsEditor->baseTextDocument()->setMimeType(mimeTypes().front());
TextEditor::TextEditorSettings::initializeEditor(vcsEditor);
return vcsEditor->editor();