Basic integration of diff editor inside git plugin

Change-Id: I7675fc1d994020f94f42f6bd7b4f75aa29e6edf6
Reviewed-by: David Schulz <david.schulz@digia.com>
Reviewed-by: Tobias Hunger <tobias.hunger@digia.com>
This commit is contained in:
jkobus
2013-05-07 14:02:08 +02:00
committed by Tobias Hunger
parent 60b1aaeae4
commit 97a86c50dc
20 changed files with 967 additions and 444 deletions

View File

@@ -1,13 +1,17 @@
DEFINES += DIFFEDITOR_LIBRARY DEFINES += DIFFEDITOR_LIBRARY
include(../../qtcreatorplugin.pri) include(../../qtcreatorplugin.pri)
HEADERS += diffeditorplugin.h \ HEADERS += diffeditor_global.h \
diffeditorwidget.h \
diffeditorconstants.h \ diffeditorconstants.h \
diffeditor_global.h \ diffeditoreditable.h \
diffeditorfile.h \
diffeditorplugin.h \
diffeditorwidget.h \
differ.h differ.h
SOURCES += diffeditorplugin.cpp \ SOURCES += diffeditoreditable.cpp \
diffeditorfile.cpp \
diffeditorplugin.cpp \
diffeditorwidget.cpp \ diffeditorwidget.cpp \
differ.cpp differ.cpp

View File

@@ -13,14 +13,18 @@ QtcPlugin {
Depends { name: "cpp" } Depends { name: "cpp" }
files: [ files: [
"diffeditor_global.h",
"diffeditorconstants.h",
"diffeditoreditable.cpp",
"diffeditoreditable.h",
"diffeditorfile.cpp",
"diffeditorfile.h",
"diffeditorplugin.cpp", "diffeditorplugin.cpp",
"diffeditorplugin.h", "diffeditorplugin.h",
"differ.cpp",
"differ.h",
"diffeditorwidget.cpp", "diffeditorwidget.cpp",
"diffeditorwidget.h", "diffeditorwidget.h",
"diffeditorconstants.h", "differ.cpp",
"diffeditor_global.h", "differ.h",
] ]
} }

View File

@@ -0,0 +1,162 @@
/****************************************************************************
**
** Copyright (C) 2013 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 "diffeditoreditable.h"
#include "diffeditorfile.h"
#include "diffeditorwidget.h"
#include "diffeditorconstants.h"
#include <coreplugin/icore.h>
#include <QCoreApplication>
#include <QToolButton>
#include <QSpinBox>
#include <QStyle>
#include <QLabel>
namespace DiffEditor {
///////////////////////////////// DiffEditorEditable //////////////////////////////////
DiffEditorEditable::DiffEditorEditable(DiffEditorWidget *editorWidget)
: IEditor(0),
m_file(new Internal::DiffEditorFile(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE), this)),
m_editorWidget(editorWidget),
m_toolWidget(0)
{
setWidget(editorWidget);
}
DiffEditorEditable::~DiffEditorEditable()
{
delete m_toolWidget;
if (m_widget)
delete m_widget;
}
bool DiffEditorEditable::createNew(const QString &contents)
{
Q_UNUSED(contents)
return true;
}
bool DiffEditorEditable::open(QString *errorString, const QString &fileName, const QString &realFileName)
{
Q_UNUSED(errorString)
Q_UNUSED(fileName)
Q_UNUSED(realFileName)
return true;
}
Core::IDocument *DiffEditorEditable::document()
{
return m_file;
}
QString DiffEditorEditable::displayName() const
{
if (m_displayName.isEmpty())
m_displayName = QCoreApplication::translate("DiffEditor", Constants::DIFF_EDITOR_DISPLAY_NAME);
return m_displayName;
}
void DiffEditorEditable::setDisplayName(const QString &title)
{
m_displayName = title;
emit changed();
}
bool DiffEditorEditable::duplicateSupported() const
{
return false;
}
Core::IEditor *DiffEditorEditable::duplicate(QWidget *parent)
{
Q_UNUSED(parent)
return 0;
}
Core::Id DiffEditorEditable::id() const
{
return Constants::DIFF_EDITOR_ID;
}
static QToolBar *createToolBar(const QWidget *someWidget)
{
// Create
QToolBar *toolBar = new QToolBar;
toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
const int size = someWidget->style()->pixelMetric(QStyle::PM_SmallIconSize);
toolBar->setIconSize(QSize(size, size));
toolBar->addSeparator();
return toolBar;
}
QWidget *DiffEditorEditable::toolBar()
{
if (m_toolWidget)
return m_toolWidget;
// Create
m_toolWidget = createToolBar(m_editorWidget);
QToolButton *whitespaceButton = new QToolButton(m_toolWidget);
whitespaceButton->setText(tr("Ignore Whitespaces"));
whitespaceButton->setCheckable(true);
whitespaceButton->setChecked(true);
connect(whitespaceButton, SIGNAL(clicked(bool)),
m_editorWidget, SLOT(setIgnoreWhitespaces(bool)));
m_toolWidget->addWidget(whitespaceButton);
QLabel *contextLabel = new QLabel(tr("Context Lines:"), m_toolWidget);
m_toolWidget->addWidget(contextLabel);
QSpinBox *contextSpinBox = new QSpinBox(m_toolWidget);
contextSpinBox->setRange(-1, 100);
contextSpinBox->setValue(3);
connect(contextSpinBox, SIGNAL(valueChanged(int)),
m_editorWidget, SLOT(setContextLinesNumber(int)));
m_toolWidget->addWidget(contextSpinBox);
return m_toolWidget;
}
QByteArray DiffEditorEditable::saveState() const
{
return QByteArray();
}
bool DiffEditorEditable::restoreState(const QByteArray &state)
{
Q_UNUSED(state)
return true;
}
} // namespace DiffEditor

View File

@@ -0,0 +1,82 @@
/****************************************************************************
**
** Copyright (C) 2013 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 DIFFEDITOREDITABLE_H
#define DIFFEDITOREDITABLE_H
#include "diffeditor_global.h"
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/idocument.h>
#include <QToolBar>
namespace DiffEditor {
class DiffEditorWidget;
namespace Internal {
class DiffEditorFile;
}
class DIFFEDITOR_EXPORT DiffEditorEditable : public Core::IEditor
{
Q_OBJECT
public:
explicit DiffEditorEditable(DiffEditorWidget *editorWidget);
virtual ~DiffEditorEditable();
public:
// Core::IEditor
bool createNew(const QString &contents);
bool open(QString *errorString, const QString &fileName, const QString &realFileName);
Core::IDocument *document();
QString displayName() const;
void setDisplayName(const QString &title);
bool duplicateSupported() const;
Core::IEditor *duplicate(QWidget *parent);
Core::Id id() const;
bool isTemporary() const { return true; }
DiffEditorWidget *editorWidget() const { return m_editorWidget; }
QWidget *toolBar();
QByteArray saveState() const;
bool restoreState(const QByteArray &state);
private:
Internal::DiffEditorFile *m_file;
DiffEditorWidget *m_editorWidget;
QToolBar *m_toolWidget;
mutable QString m_displayName;
};
} // namespace DiffEditor
#endif // DIFFEDITOREDITABLE_H

View File

@@ -0,0 +1,96 @@
/****************************************************************************
**
** Copyright (C) 2013 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 "diffeditorfile.h"
namespace DiffEditor {
namespace Internal {
///////////////////////////////// DiffFile //////////////////////////////////
DiffEditorFile::DiffEditorFile(const QString &mimeType, QObject *parent) :
Core::IDocument(parent),
m_mimeType(mimeType),
m_modified(false)
{
}
void DiffEditorFile::rename(const QString &newName)
{
Q_UNUSED(newName);
return;
}
void DiffEditorFile::setFileName(const QString &name)
{
if (m_fileName == name)
return;
m_fileName = name;
emit changed();
}
void DiffEditorFile::setModified(bool modified)
{
if (m_modified == modified)
return;
m_modified = modified;
emit changed();
}
bool DiffEditorFile::save(QString *errorString, const QString &fileName, bool autoSave)
{
emit saveMe(errorString, fileName, autoSave);
if (!errorString->isEmpty())
return false;
emit changed();
return true;
}
QString DiffEditorFile::mimeType() const
{
return m_mimeType;
}
Core::IDocument::ReloadBehavior DiffEditorFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
Q_UNUSED(state)
Q_UNUSED(type)
return BehaviorSilent;
}
bool DiffEditorFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
{
Q_UNUSED(errorString)
Q_UNUSED(flag)
Q_UNUSED(type)
return true;
}
} // namespace Internal
} // namespace DiffEditor

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2013 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 DIFFEDITORFILE_H
#define DIFFEDITORFILE_H
#include "diffeditor_global.h"
#include <coreplugin/idocument.h>
namespace DiffEditor {
class DiffEditorWidget;
class DiffEditorFile;
namespace Internal {
class DiffEditorFile : public Core::IDocument
{
Q_OBJECT
public:
explicit DiffEditorFile(const QString &mimeType,
QObject *parent = 0);
QString fileName() const { return m_fileName; }
QString defaultPath() const { return QString(); }
QString suggestedFileName() const { return QString(); }
bool isModified() const { return m_modified; }
QString mimeType() const;
bool isSaveAsAllowed() const { return false; }
bool save(QString *errorString, const QString &fileName, bool autoSave);
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
void rename(const QString &newName);
void setFileName(const QString &name);
void setModified(bool modified = true);
signals:
void saveMe(QString *errorString, const QString &fileName, bool autoSave);
private:
const QString m_mimeType;
bool m_modified;
QString m_fileName;
};
} // namespace Internal
} // namespace DiffEditor
#endif // DIFFEDITOREDITABLE_H

View File

@@ -28,17 +28,14 @@
****************************************************************************/ ****************************************************************************/
#include "diffeditorplugin.h" #include "diffeditorplugin.h"
#include "diffeditoreditable.h"
#include "diffeditorwidget.h" #include "diffeditorwidget.h"
#include "diffeditorconstants.h" #include "diffeditorconstants.h"
#include <coreplugin/icore.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QToolButton>
#include <QSpinBox>
#include <QStyle>
#include <QLabel>
#include <QFileDialog> #include <QFileDialog>
#include <QTextCodec> #include <QTextCodec>
#include <QtPlugin>
#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
@@ -46,194 +43,13 @@
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
namespace DiffEditor { namespace DiffEditor {
namespace Internal {
///////////////////////////////// DiffEditor //////////////////////////////////
DiffEditorEditable::DiffEditorEditable(DiffEditorWidget *editorWidget)
:
IEditor(0),
m_file(new DiffFile(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE), this)),
m_editorWidget(editorWidget),
m_toolWidget(0)
{
setWidget(editorWidget);
}
DiffEditorEditable::~DiffEditorEditable()
{
delete m_toolWidget;
if (m_widget)
delete m_widget;
}
bool DiffEditorEditable::createNew(const QString &contents)
{
Q_UNUSED(contents)
// setFileContents(contents);
return true;
}
bool DiffEditorEditable::open(QString *errorString, const QString &fileName, const QString &realFileName)
{
Q_UNUSED(errorString)
Q_UNUSED(fileName)
Q_UNUSED(realFileName)
const QString text = QLatin1String("Open");
if (!createNew(text))
return false;
return true;
}
Core::IDocument *DiffEditorEditable::document()
{
return m_file;
}
QString DiffEditorEditable::displayName() const
{
if (m_displayName.isEmpty())
m_displayName = QCoreApplication::translate("DiffEditor", Constants::DIFF_EDITOR_DISPLAY_NAME);
return m_displayName;
}
void DiffEditorEditable::setDisplayName(const QString &title)
{
m_displayName = title;
emit changed();
}
bool DiffEditorEditable::duplicateSupported() const
{
return false;
}
Core::IEditor *DiffEditorEditable::duplicate(QWidget * /*parent*/)
{
return 0;
}
Core::Id DiffEditorEditable::id() const
{
return Constants::DIFF_EDITOR_ID;
}
static QToolBar *createToolBar(const QWidget *someWidget)
{
// Create
QToolBar *toolBar = new QToolBar;
toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
const int size = someWidget->style()->pixelMetric(QStyle::PM_SmallIconSize);
toolBar->setIconSize(QSize(size, size));
toolBar->addSeparator();
return toolBar;
}
QWidget *DiffEditorEditable::toolBar()
{
if (m_toolWidget)
return m_toolWidget;
// Create
m_toolWidget = createToolBar(m_editorWidget);
QToolButton *whitespaceButton = new QToolButton(m_toolWidget);
whitespaceButton->setText(tr("Ignore Whitespaces"));
whitespaceButton->setCheckable(true);
whitespaceButton->setChecked(true);
connect(whitespaceButton, SIGNAL(clicked(bool)),
m_editorWidget, SLOT(setIgnoreWhitespaces(bool)));
m_toolWidget->addWidget(whitespaceButton);
QLabel *contextLabel = new QLabel(tr("Context Lines:"), m_toolWidget);
m_toolWidget->addWidget(contextLabel);
QSpinBox *contextSpinBox = new QSpinBox(m_toolWidget);
contextSpinBox->setRange(-1, 100);
contextSpinBox->setValue(1);
connect(contextSpinBox, SIGNAL(valueChanged(int)),
m_editorWidget, SLOT(setContextLinesNumber(int)));
m_toolWidget->addWidget(contextSpinBox);
return m_toolWidget;
}
QByteArray DiffEditorEditable::saveState() const
{
return QByteArray();
}
bool DiffEditorEditable::restoreState(const QByteArray &/*state*/)
{
return true;
}
///////////////////////////////// DiffFile //////////////////////////////////
DiffFile::DiffFile(const QString &mimeType, QObject *parent) :
Core::IDocument(parent),
m_mimeType(mimeType),
m_modified(false)
{
}
void DiffFile::rename(const QString &newName)
{
Q_UNUSED(newName);
return;
}
void DiffFile::setFileName(const QString &name)
{
if (m_fileName == name)
return;
m_fileName = name;
emit changed();
}
void DiffFile::setModified(bool modified)
{
if (m_modified == modified)
return;
m_modified = modified;
emit changed();
}
bool DiffFile::save(QString *errorString, const QString &fileName, bool autoSave)
{
emit saveMe(errorString, fileName, autoSave);
if (!errorString->isEmpty())
return false;
emit changed();
return true;
}
QString DiffFile::mimeType() const
{
return m_mimeType;
}
Core::IDocument::ReloadBehavior DiffFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
Q_UNUSED(state)
Q_UNUSED(type)
return BehaviorSilent;
}
bool DiffFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
{
Q_UNUSED(errorString)
Q_UNUSED(flag)
Q_UNUSED(type)
return true;
}
///////////////////////////////// DiffEditorFactory ////////////////////////////////// ///////////////////////////////// DiffEditorFactory //////////////////////////////////
DiffEditorFactory::DiffEditorFactory(DiffEditorPlugin *owner) : namespace Internal {
m_mimeTypes(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE)),
DiffEditorFactory::DiffEditorFactory(DiffEditorPlugin *owner)
: m_mimeTypes(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE)),
m_owner(owner) m_owner(owner)
{ {
} }
@@ -329,9 +145,9 @@ void DiffEditorPlugin::diff()
const QString text2 = getFileContents(fileName2, editorWidget->codec()); const QString text2 = getFileContents(fileName2, editorWidget->codec());
DiffEditorWidget::DiffFilesContents dfc; DiffEditorWidget::DiffFilesContents dfc;
dfc.leftFileName = fileName1; dfc.leftFileInfo = fileName1;
dfc.leftText = text1; dfc.leftText = text1;
dfc.rightFileName = fileName2; dfc.rightFileInfo = fileName2;
dfc.rightText = text2; dfc.rightText = text2;
QList<DiffEditorWidget::DiffFilesContents> list; QList<DiffEditorWidget::DiffFilesContents> list;
list.append(dfc); list.append(dfc);

View File

@@ -30,86 +30,22 @@
#ifndef DIFFEDITORPLUGIN_H #ifndef DIFFEDITORPLUGIN_H
#define DIFFEDITORPLUGIN_H #define DIFFEDITORPLUGIN_H
#include "diffeditor_global.h"
#include <extensionsystem/iplugin.h> #include <extensionsystem/iplugin.h>
#include <coreplugin/editormanager/ieditorfactory.h> #include <coreplugin/editormanager/ieditorfactory.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icontext.h> #include <coreplugin/icontext.h>
#include <coreplugin/idocument.h> #include <coreplugin/idocument.h>
#include <QtPlugin>
#include <QPointer>
#include <QStringList> #include <QStringList>
#include <QAction>
#include <QToolBar> namespace Core { class IEditor; }
namespace DiffEditor { namespace DiffEditor {
class DiffEditorWidget; class DiffEditorWidget;
namespace Internal { namespace Internal {
class DiffFile;
class DiffEditorEditable : public Core::IEditor
{
Q_OBJECT
public:
explicit DiffEditorEditable(DiffEditorWidget *editorWidget);
virtual ~DiffEditorEditable();
public:
// Core::IEditor
bool createNew(const QString &contents);
bool open(QString *errorString, const QString &fileName, const QString &realFileName);
Core::IDocument *document();
QString displayName() const;
void setDisplayName(const QString &title);
bool duplicateSupported() const;
Core::IEditor *duplicate(QWidget *parent);
Core::Id id() const;
bool isTemporary() const { return true; }
DiffEditorWidget *editorWidget() const { return m_editorWidget; }
QWidget *toolBar();
QByteArray saveState() const;
bool restoreState(const QByteArray &state);
private:
DiffFile *m_file;
DiffEditorWidget *m_editorWidget;
QToolBar *m_toolWidget;
mutable QString m_displayName;
};
class DiffFile : public Core::IDocument
{
Q_OBJECT
public:
explicit DiffFile(const QString &mimeType,
QObject *parent = 0);
QString fileName() const { return m_fileName; }
QString defaultPath() const { return QString(); }
QString suggestedFileName() const { return QString(); }
bool isModified() const { return m_modified; }
QString mimeType() const;
bool isSaveAsAllowed() const { return false; }
bool save(QString *errorString, const QString &fileName, bool autoSave);
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
void rename(const QString &newName);
void setFileName(const QString &name);
void setModified(bool modified = true);
signals:
void saveMe(QString *errorString, const QString &fileName, bool autoSave);
private:
const QString m_mimeType;
bool m_modified;
QString m_fileName;
};
class DiffEditorPlugin : public ExtensionSystem::IPlugin class DiffEditorPlugin : public ExtensionSystem::IPlugin
{ {

View File

@@ -35,9 +35,7 @@
#include <QTextBlock> #include <QTextBlock>
#include <QScrollBar> #include <QScrollBar>
#include <QPainter> #include <QPainter>
#include <QTime> #include <QDir>
#include <QDebug>
#include <texteditor/basetexteditor.h> #include <texteditor/basetexteditor.h>
#include <texteditor/snippets/snippeteditor.h> #include <texteditor/snippets/snippeteditor.h>
@@ -54,6 +52,46 @@ using namespace TextEditor;
namespace DiffEditor { namespace DiffEditor {
struct TextLineData {
enum TextLineType {
TextLine,
Separator,
Invalid
};
TextLineData() : textLineType(Invalid) {}
TextLineData(const QString &txt) : textLineType(TextLine), text(txt) {}
TextLineData(TextLineType t) : textLineType(t) {}
TextLineType textLineType;
QString text;
};
struct RowData {
RowData() : equal(true) {}
RowData(const TextLineData &l)
: leftLine(l), rightLine(l), equal(true) {}
RowData(const TextLineData &l, const TextLineData &r, bool e = false)
: leftLine(l), rightLine(r), equal(e) {}
TextLineData leftLine;
TextLineData rightLine;
bool equal; // true if left and right lines are equal, taking whitespaces into account (or both invalid)
};
struct ChunkData {
ChunkData() : contextChunk(false) {}
QList<RowData> rows;
bool contextChunk;
QMap<int, int> changedLeftPositions; // counting from the beginning of the chunk
QMap<int, int> changedRightPositions; // counting from the beginning of the chunk
};
struct FileData {
FileData() {}
FileData(const ChunkData &chunkData) { chunks.append(chunkData); }
QList<ChunkData> chunks;
DiffEditorWidget::DiffFileInfo leftFileInfo;
DiffEditorWidget::DiffFileInfo rightFileInfo;
};
////////////////////// //////////////////////
class DiffViewEditorEditable : public BaseTextEditor class DiffViewEditorEditable : public BaseTextEditor
@@ -86,12 +124,15 @@ public:
QMap<int, int> skippedLines() const { return m_skippedLines; } QMap<int, int> skippedLines() const { return m_skippedLines; }
void setWorkingDirectory(const QString &workingDirectory) { m_workingDirectory = workingDirectory; }
void setLineNumber(int blockNumber, int lineNumber); void setLineNumber(int blockNumber, int lineNumber);
void setFileName(int blockNumber, const QString &fileName) { m_fileNames[blockNumber] = fileName; setSeparator(blockNumber, true); } void setFileInfo(int blockNumber, const DiffEditorWidget::DiffFileInfo &fileInfo) { m_fileInfo[blockNumber] = fileInfo; setSeparator(blockNumber, true); }
void setSkippedLines(int blockNumber, int skippedLines) { m_skippedLines[blockNumber] = skippedLines; setSeparator(blockNumber, true); } void setSkippedLines(int blockNumber, int skippedLines) { m_skippedLines[blockNumber] = skippedLines; setSeparator(blockNumber, true); }
void setSeparator(int blockNumber, bool separator) { m_separators[blockNumber] = separator; } void setSeparator(int blockNumber, bool separator) { m_separators[blockNumber] = separator; }
bool isFileLine(int blockNumber) const { return m_fileNames.contains(blockNumber); } bool isFileLine(int blockNumber) const { return m_fileInfo.contains(blockNumber); }
bool isChunkLine(int blockNumber) const { return m_skippedLines.contains(blockNumber); } bool isChunkLine(int blockNumber) const { return m_skippedLines.contains(blockNumber); }
void clearAll();
void clearAll(const QString &message);
void clearAllData(); void clearAllData();
QTextBlock firstVisibleBlock() const { return SnippetEditorWidget::firstVisibleBlock(); } QTextBlock firstVisibleBlock() const { return SnippetEditorWidget::firstVisibleBlock(); }
@@ -116,10 +157,11 @@ private:
void paintSeparator(QPainter &painter, const QString &text, const QTextBlock &block, int top); void paintSeparator(QPainter &painter, const QString &text, const QTextBlock &block, int top);
void jumpToOriginalFile(const QTextCursor &cursor); void jumpToOriginalFile(const QTextCursor &cursor);
QString m_workingDirectory;
QMap<int, int> m_lineNumbers; QMap<int, int> m_lineNumbers;
int m_lineNumberDigits; int m_lineNumberDigits;
// block number, fileName // block number, fileInfo
QMap<int, QString> m_fileNames; QMap<int, DiffEditorWidget::DiffFileInfo> m_fileInfo;
// block number, skipped lines // block number, skipped lines
QMap<int, int> m_skippedLines; QMap<int, int> m_skippedLines;
// block number, separator. Separator used as lines alignment and inside skipped lines // block number, separator. Separator used as lines alignment and inside skipped lines
@@ -200,11 +242,24 @@ void DiffViewEditorWidget::setLineNumber(int blockNumber, int lineNumber)
m_lineNumberDigits = qMax(m_lineNumberDigits, lineNumberString.count()); m_lineNumberDigits = qMax(m_lineNumberDigits, lineNumberString.count());
} }
void DiffViewEditorWidget::clearAll()
{
clearAll(tr("No difference"));
}
void DiffViewEditorWidget::clearAll(const QString &message)
{
setBlockSelection(false);
clear();
clearAllData();
setPlainText(message);
}
void DiffViewEditorWidget::clearAllData() void DiffViewEditorWidget::clearAllData()
{ {
m_lineNumberDigits = 1; m_lineNumberDigits = 1;
m_lineNumbers.clear(); m_lineNumbers.clear();
m_fileNames.clear(); m_fileInfo.clear();
m_skippedLines.clear(); m_skippedLines.clear();
m_separators.clear(); m_separators.clear();
} }
@@ -253,7 +308,7 @@ void DiffViewEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
void DiffViewEditorWidget::jumpToOriginalFile(const QTextCursor &cursor) void DiffViewEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
{ {
if (m_fileNames.isEmpty()) if (m_fileInfo.isEmpty())
return; return;
const int blockNumber = cursor.blockNumber(); const int blockNumber = cursor.blockNumber();
@@ -262,10 +317,11 @@ void DiffViewEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
return; return;
const int lineNr = m_lineNumbers.value(blockNumber); const int lineNr = m_lineNumbers.value(blockNumber);
QMap<int, QString>::const_iterator it = m_fileNames.upperBound(blockNumber); QMap<int, DiffEditorWidget::DiffFileInfo>::const_iterator it = m_fileInfo.upperBound(blockNumber);
if (it != m_fileNames.constBegin()) if (it != m_fileInfo.constBegin())
--it; --it;
const QString fileName = it.value(); const QDir dir(m_workingDirectory);
const QString fileName = dir.absoluteFilePath(it.value().fileName);
Core::IEditor *ed = Core::EditorManager::openEditor(fileName, Core::Id(), Core::EditorManager::ModeSwitch); Core::IEditor *ed = Core::EditorManager::openEditor(fileName, Core::Id(), Core::EditorManager::ModeSwitch);
if (TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor *>(ed)) if (TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor *>(ed))
@@ -300,9 +356,12 @@ void DiffViewEditorWidget::paintEvent(QPaintEvent *e)
paintSeparator(painter, skippedRowsText, currentBlock, top); paintSeparator(painter, skippedRowsText, currentBlock, top);
} }
const QString fileName = m_fileNames.value(blockNumber); const DiffEditorWidget::DiffFileInfo fileInfo = m_fileInfo.value(blockNumber);
if (!fileName.isEmpty()) { if (!fileInfo.fileName.isEmpty()) {
paintSeparator(painter, fileName, currentBlock, top); const QString fileNameText = fileInfo.typeInfo.isEmpty()
? fileInfo.fileName
: tr("[%1] %2").arg(fileInfo.typeInfo).arg(fileInfo.fileName);
paintSeparator(painter, fileNameText, currentBlock, top);
} }
} }
} }
@@ -416,7 +475,7 @@ void DiffViewEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
DiffEditorWidget::DiffEditorWidget(QWidget *parent) DiffEditorWidget::DiffEditorWidget(QWidget *parent)
: QWidget(parent), : QWidget(parent),
m_contextLinesNumber(1), m_contextLinesNumber(3),
m_ignoreWhitespaces(true), m_ignoreWhitespaces(true),
m_foldingBlocker(false) m_foldingBlocker(false)
{ {
@@ -457,6 +516,8 @@ DiffEditorWidget::DiffEditorWidget(QWidget *parent)
m_splitter->addWidget(m_rightEditor); m_splitter->addWidget(m_rightEditor);
QVBoxLayout *l = new QVBoxLayout(this); QVBoxLayout *l = new QVBoxLayout(this);
l->addWidget(m_splitter); l->addWidget(m_splitter);
clear();
} }
DiffEditorWidget::~DiffEditorWidget() DiffEditorWidget::~DiffEditorWidget()
@@ -464,15 +525,29 @@ DiffEditorWidget::~DiffEditorWidget()
} }
void DiffEditorWidget::setDiff(const QList<DiffFilesContents> &diffFileList) void DiffEditorWidget::clear()
{ {
m_leftEditor->clearAll();
m_rightEditor->clearAll();
}
void DiffEditorWidget::clear(const QString &message)
{
m_leftEditor->clearAll(message);
m_rightEditor->clearAll(message);
}
void DiffEditorWidget::setDiff(const QList<DiffFilesContents> &diffFileList, const QString &workingDirectory)
{
m_leftEditor->setWorkingDirectory(workingDirectory);
m_rightEditor->setWorkingDirectory(workingDirectory);
Differ differ; Differ differ;
QList<DiffList> diffList; QList<DiffList> diffList;
for (int i = 0; i < diffFileList.count(); i++) { for (int i = 0; i < diffFileList.count(); i++) {
DiffFilesContents dfc = diffFileList.at(i); DiffFilesContents dfc = diffFileList.at(i);
DiffList dl; DiffList dl;
dl.leftFileName = dfc.leftFileName; dl.leftFileInfo = dfc.leftFileInfo;
dl.rightFileName = dfc.rightFileName; dl.rightFileInfo = dfc.rightFileInfo;
dl.diffList = differ.cleanupSemantics(differ.diff(dfc.leftText, dfc.rightText)); dl.diffList = differ.cleanupSemantics(differ.diff(dfc.leftText, dfc.rightText));
diffList.append(dl); diffList.append(dl);
} }
@@ -490,8 +565,8 @@ void DiffEditorWidget::setDiff(const QList<DiffList> &diffList)
ChunkData chunkData = calculateOriginalData(dl.diffList); ChunkData chunkData = calculateOriginalData(dl.diffList);
m_originalChunkData.append(chunkData); m_originalChunkData.append(chunkData);
FileData fileData = calculateContextData(chunkData); FileData fileData = calculateContextData(chunkData);
fileData.leftFileName = dl.leftFileName; fileData.leftFileInfo = dl.leftFileInfo;
fileData.rightFileName = dl.rightFileName; fileData.rightFileInfo = dl.rightFileInfo;
m_contextFileData.append(fileData); m_contextFileData.append(fileData);
} }
showDiff(); showDiff();
@@ -506,8 +581,8 @@ void DiffEditorWidget::setContextLinesNumber(int lines)
for (int i = 0; i < m_diffList.count(); i++) { for (int i = 0; i < m_diffList.count(); i++) {
const FileData oldFileData = m_contextFileData.at(i); const FileData oldFileData = m_contextFileData.at(i);
FileData newFileData = calculateContextData(m_originalChunkData.at(i)); FileData newFileData = calculateContextData(m_originalChunkData.at(i));
newFileData.leftFileName = oldFileData.leftFileName; newFileData.leftFileInfo = oldFileData.leftFileInfo;
newFileData.rightFileName = oldFileData.rightFileName; newFileData.rightFileInfo = oldFileData.rightFileInfo;
m_contextFileData[i] = newFileData; m_contextFileData[i] = newFileData;
} }
@@ -855,8 +930,8 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c
RowData rowData = originalData.rows.at(i); RowData rowData = originalData.rows.at(i);
chunkData.rows.append(rowData); chunkData.rows.append(rowData);
leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for '\n'
rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for '\n'
i++; i++;
} }
while (leftChangedIt != originalData.changedLeftPositions.constEnd()) { while (leftChangedIt != originalData.changedLeftPositions.constEnd()) {
@@ -889,8 +964,8 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c
RowData rowData = originalData.rows.at(i); RowData rowData = originalData.rows.at(i);
chunkData.rows.append(rowData); chunkData.rows.append(rowData);
leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for '\n'
rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for '\n'
i++; i++;
} }
fileData.chunks.append(chunkData); fileData.chunks.append(chunkData);
@@ -901,21 +976,12 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c
void DiffEditorWidget::showDiff() void DiffEditorWidget::showDiff()
{ {
// QTime time;
// time.start();
// TODO: remember the line number of the line in the middle // TODO: remember the line number of the line in the middle
const int verticalValue = m_leftEditor->verticalScrollBar()->value(); const int verticalValue = m_leftEditor->verticalScrollBar()->value();
const int leftHorizontalValue = m_leftEditor->horizontalScrollBar()->value(); const int leftHorizontalValue = m_leftEditor->horizontalScrollBar()->value();
const int rightHorizontalValue = m_rightEditor->horizontalScrollBar()->value(); const int rightHorizontalValue = m_rightEditor->horizontalScrollBar()->value();
m_leftEditor->setBlockSelection(false); clear();
m_rightEditor->setBlockSelection(false);
m_leftEditor->clear();
m_rightEditor->clear();
m_leftEditor->clearAllData();
m_rightEditor->clearAllData();
// int ela1 = time.elapsed();
QString leftText, rightText; QString leftText, rightText;
int blockNumber = 0; int blockNumber = 0;
@@ -925,8 +991,8 @@ void DiffEditorWidget::showDiff()
int leftLineNumber = 0; int leftLineNumber = 0;
int rightLineNumber = 0; int rightLineNumber = 0;
m_leftEditor->setFileName(blockNumber, contextFileData.leftFileName); m_leftEditor->setFileInfo(blockNumber, contextFileData.leftFileInfo);
m_rightEditor->setFileName(blockNumber, contextFileData.rightFileName); m_rightEditor->setFileInfo(blockNumber, contextFileData.rightFileInfo);
leftText += separator; leftText += separator;
rightText += separator; rightText += separator;
blockNumber++; blockNumber++;
@@ -967,13 +1033,12 @@ void DiffEditorWidget::showDiff()
} }
} }
} }
// int ela2 = time.elapsed();
if (leftText.isEmpty() && rightText.isEmpty())
return;
m_leftEditor->setPlainText(leftText); m_leftEditor->setPlainText(leftText);
m_rightEditor->setPlainText(rightText); m_rightEditor->setPlainText(rightText);
// int ela3 = time.elapsed();
// int ela4 = time.elapsed();
colorDiff(m_contextFileData); colorDiff(m_contextFileData);
@@ -1029,14 +1094,10 @@ void DiffEditorWidget::showDiff()
} }
m_foldingBlocker = false; m_foldingBlocker = false;
// int ela5 = time.elapsed();
m_leftEditor->verticalScrollBar()->setValue(verticalValue); m_leftEditor->verticalScrollBar()->setValue(verticalValue);
m_rightEditor->verticalScrollBar()->setValue(verticalValue); m_rightEditor->verticalScrollBar()->setValue(verticalValue);
m_leftEditor->horizontalScrollBar()->setValue(leftHorizontalValue); m_leftEditor->horizontalScrollBar()->setValue(leftHorizontalValue);
m_rightEditor->horizontalScrollBar()->setValue(rightHorizontalValue); m_rightEditor->horizontalScrollBar()->setValue(rightHorizontalValue);
// int ela6 = time.elapsed();
// qDebug() << ela1 << ela2 << ela3 << ela4 << ela5 << ela6;
m_leftEditor->updateFoldingHighlight(QPoint(-1, -1)); m_leftEditor->updateFoldingHighlight(QPoint(-1, -1));
m_rightEditor->updateFoldingHighlight(QPoint(-1, -1)); m_rightEditor->updateFoldingHighlight(QPoint(-1, -1));
} }
@@ -1099,7 +1160,6 @@ void DiffEditorWidget::colorDiff(const QList<FileData> &fileDataList)
int leftPos = 0; int leftPos = 0;
int rightPos = 0; int rightPos = 0;
// startPos, endPos
QMap<int, int> leftLinePos; QMap<int, int> leftLinePos;
QMap<int, int> rightLinePos; QMap<int, int> rightLinePos;
QMap<int, int> leftCharPos; QMap<int, int> leftCharPos;
@@ -1154,8 +1214,8 @@ void DiffEditorWidget::colorDiff(const QList<FileData> &fileDataList)
for (int k = 0; k < chunkData.rows.count(); k++) { for (int k = 0; k < chunkData.rows.count(); k++) {
RowData rowData = chunkData.rows.at(k); RowData rowData = chunkData.rows.at(k);
leftPos += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it leftPos += rowData.leftLine.text.count() + 1; // +1 for '\n'
rightPos += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it rightPos += rowData.rightLine.text.count() + 1; // +1 for '\n'
if (!rowData.equal) { if (!rowData.equal) {
if (rowData.leftLine.textLineType == TextLineData::TextLine) { if (rowData.leftLine.textLineType == TextLineData::TextLine) {

View File

@@ -52,59 +52,26 @@ QT_END_NAMESPACE
namespace DiffEditor { namespace DiffEditor {
class DiffViewEditorWidget; class DiffViewEditorWidget;
struct TextLineData;
struct TextLineData { struct ChunkData;
enum TextLineType { struct FileData;
TextLine,
Separator,
Invalid
};
TextLineData() : textLineType(Invalid) {}
TextLineData(const QString &txt) : textLineType(TextLine), text(txt) {}
TextLineData(TextLineType t) : textLineType(t) {}
TextLineType textLineType;
QString text;
};
struct RowData {
RowData() : equal(true) {}
RowData(const TextLineData &l)
: leftLine(l), rightLine(l), equal(true) {}
RowData(const TextLineData &l, const TextLineData &r, bool e = false)
: leftLine(l), rightLine(r), equal(e) {}
TextLineData leftLine;
TextLineData rightLine;
bool equal; // true if left and right lines are equal, taking whitespaces into account (or both invalid)
};
struct ChunkData {
ChunkData() : contextChunk(false) {}
QList<RowData> rows;
bool contextChunk;
QMap<int, int> changedLeftPositions; // counting from the beginning of the chunk
QMap<int, int> changedRightPositions; // counting from the beginning of the chunk
};
struct FileData {
FileData() {}
FileData(const ChunkData &chunkData) { chunks.append(chunkData); }
QList<ChunkData> chunks;
QString leftFileName;
QString rightFileName;
};
struct DiffData {
QList<FileData> files;
};
class DIFFEDITOR_EXPORT DiffEditorWidget : public QWidget class DIFFEDITOR_EXPORT DiffEditorWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
struct DiffFileInfo {
DiffFileInfo() {}
DiffFileInfo(const QString &file) : fileName(file) {}
DiffFileInfo(const QString &file, const QString &type) : fileName(file), typeInfo(type) {}
QString fileName;
QString typeInfo;
};
struct DiffFilesContents { struct DiffFilesContents {
QString leftFileName; DiffFileInfo leftFileInfo;
QString leftText; QString leftText;
QString rightFileName; DiffFileInfo rightFileInfo;
QString rightText; QString rightText;
}; };
@@ -112,7 +79,8 @@ public:
~DiffEditorWidget(); ~DiffEditorWidget();
void clear(); void clear();
void setDiff(const QList<DiffFilesContents> &diffFileList); void clear(const QString &message);
void setDiff(const QList<DiffFilesContents> &diffFileList, const QString &workingDirectory = QString());
QTextCodec *codec() const; QTextCodec *codec() const;
public slots: public slots:
@@ -131,8 +99,8 @@ private slots:
private: private:
struct DiffList { struct DiffList {
QString leftFileName; DiffFileInfo leftFileInfo;
QString rightFileName; DiffFileInfo rightFileInfo;
QList<Diff> diffList; QList<Diff> diffList;
}; };

View File

@@ -10,6 +10,7 @@ QtcPlugin {
Depends { name: "TextEditor" } Depends { name: "TextEditor" }
Depends { name: "Find" } Depends { name: "Find" }
Depends { name: "VcsBase" } Depends { name: "VcsBase" }
Depends { name: "DiffEditor" }
Depends { name: "Locator" } Depends { name: "Locator" }
files: [ files: [

View File

@@ -5,4 +5,5 @@ QTC_PLUGIN_DEPENDS += \
projectexplorer \ projectexplorer \
texteditor \ texteditor \
coreplugin \ coreplugin \
vcsbase vcsbase \
diffeditor

View File

@@ -56,6 +56,10 @@
#include <vcsbase/vcsbaseoutputwindow.h> #include <vcsbase/vcsbaseoutputwindow.h>
#include <vcsbase/vcsbaseplugin.h> #include <vcsbase/vcsbaseplugin.h>
#include <diffeditor/diffeditorwidget.h>
#include <diffeditor/diffeditoreditable.h>
#include <diffeditor/diffeditorconstants.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@@ -75,6 +79,224 @@ static const char graphLogFormatC[] = "%h %d %an %s %ci";
namespace Git { namespace Git {
namespace Internal { namespace Internal {
class GitDiffHandler : public QObject
{
Q_OBJECT
public:
GitDiffHandler(const QString &gitPath,
const QString &workingDirectory,
const QProcessEnvironment &environment,
DiffEditor::DiffEditorWidget *editor,
int timeout);
// index -> working tree
void diffFile(const QString &fileName);
// stagedFileNames - files in index, diff will compare the state in HEAD to the one in the index
// unstagedFileNames - diff will compare the state in the index to the one in the working tree
void diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames);
// index -> working tree
void diffProjects(const QStringList &projectPaths);
// index -> working tree
void diffRepository();
private slots:
void slotFileListReceived(const QByteArray &data);
void slotFileContentsReceived(const QByteArray &data);
private:
void collectFilesList(const QStringList &additionalArguments);
void collectFilesContents();
void feedEditor();
QString workingTreeContents(const QString &fileName) const;
const QString m_gitPath;
const QString m_workingDirectory;
const QProcessEnvironment m_processEnvironment;
QWeakPointer<DiffEditor::DiffEditorWidget> m_editor;
const int m_timeout;
const QString m_waitMessage;
enum RevisionType {
Head = 0x01,
Index = 0x02,
WorkingTree = 0x04
};
QStringList m_requestedHeadFileNames;
QStringList m_requestedIndexFileNames;
QStringList m_headFileNames;
QStringList m_indexFileNames;
QStringList m_headContents;
QStringList m_indexContents;
};
GitDiffHandler::GitDiffHandler(const QString &gitPath,
const QString &workingDirectory,
const QProcessEnvironment &environment,
DiffEditor::DiffEditorWidget *editor,
int timeout)
: m_gitPath(gitPath),
m_workingDirectory(workingDirectory),
m_processEnvironment(environment),
m_editor(editor),
m_timeout(timeout),
m_waitMessage(tr("Waiting for data..."))
{
}
void GitDiffHandler::diffFile(const QString &fileName)
{
m_requestedIndexFileNames << fileName;
collectFilesList(QStringList() << QLatin1String("--") << m_requestedIndexFileNames);
}
void GitDiffHandler::diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames)
{
m_requestedHeadFileNames = stagedFileNames;
m_requestedHeadFileNames.removeDuplicates();
m_requestedIndexFileNames = unstagedFileNames;
m_requestedIndexFileNames.removeDuplicates();
m_headFileNames = m_requestedHeadFileNames;
m_indexFileNames = m_requestedIndexFileNames;
for (int i = 0; i < m_headFileNames.count(); i++) {
const QString headFileName = m_headFileNames.at(i);
if (!m_indexFileNames.contains(headFileName))
m_indexFileNames.append(headFileName);
}
collectFilesContents();
}
void GitDiffHandler::diffProjects(const QStringList &projectPaths)
{
collectFilesList(QStringList() << QLatin1String("--") << projectPaths);
}
void GitDiffHandler::diffRepository()
{
collectFilesList(QStringList());
}
void GitDiffHandler::collectFilesList(const QStringList &additionalArguments)
{
m_editor.data()->clear(m_waitMessage);
VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileListReceived(QByteArray)));
QStringList arguments;
arguments << QLatin1String("diff") << QLatin1String("--name-only") << additionalArguments;
command->addJob(arguments, m_timeout);
command->execute();
}
void GitDiffHandler::slotFileListReceived(const QByteArray &data)
{
if (m_editor.isNull())
return;
const QString fileList = m_editor.data()->codec()->toUnicode(data);
m_requestedIndexFileNames = fileList.split(QLatin1Char('\n'), QString::SkipEmptyParts);
m_requestedIndexFileNames.removeDuplicates();
m_indexFileNames = m_requestedIndexFileNames;
collectFilesContents();
}
void GitDiffHandler::collectFilesContents()
{
const int headFilesReceived = m_headContents.count();
const int indexFilesReceived = m_indexContents.count();
if (headFilesReceived < m_headFileNames.count()) {
VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileContentsReceived(QByteArray)));
QStringList arguments;
arguments << QLatin1String("show") << QLatin1String("HEAD:./") + m_headFileNames.at(headFilesReceived);
command->addJob(arguments, m_timeout);
command->execute();
} else if (indexFilesReceived < m_indexFileNames.count()) {
VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileContentsReceived(QByteArray)));
QStringList arguments;
arguments << QLatin1String("show") << QLatin1String(":./") + m_indexFileNames.at(indexFilesReceived);
command->addJob(arguments, m_timeout);
command->execute();
} else {
feedEditor();
}
}
void GitDiffHandler::slotFileContentsReceived(const QByteArray &data)
{
if (m_editor.isNull())
return;
const int headFilesReceived = m_headContents.count();
const int indexFilesReceived = m_indexContents.count();
const QString contents = m_editor.data()->codec()->toUnicode(data);
if (headFilesReceived < m_headFileNames.count())
m_headContents.append(contents);
else if (indexFilesReceived < m_indexFileNames.count())
m_indexContents.append(contents);
collectFilesContents();
}
void GitDiffHandler::feedEditor()
{
QList<DiffEditor::DiffEditorWidget::DiffFilesContents> list;
for (int i = 0; i < m_requestedHeadFileNames.count(); i++) {
const QString fileName = m_requestedHeadFileNames.at(i);
const QString original = m_headContents.at(i);
const int idx = m_indexFileNames.indexOf(fileName);
if (idx >= 0) {
const QString modified = m_indexContents.at(idx);
if (original != modified) {
DiffEditor::DiffEditorWidget::DiffFilesContents dfc;
dfc.leftFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Head"));
dfc.leftText = original;
dfc.rightFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Index"));
dfc.rightText = modified;
list.append(dfc);
}
}
}
for (int i = 0; i < m_requestedIndexFileNames.count(); i++) {
const QString fileName = m_requestedIndexFileNames.at(i);
const QString original = m_indexContents.at(i);
const QString modified = workingTreeContents(fileName);
if (original != modified) {
DiffEditor::DiffEditorWidget::DiffFilesContents dfc;
dfc.leftFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Index"));
dfc.leftText = original;
dfc.rightFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Working tree"));
dfc.rightText = modified;
list.append(dfc);
}
}
m_editor.data()->setDiff(list, m_workingDirectory);
deleteLater();
}
QString GitDiffHandler::workingTreeContents(const QString &fileName) const
{
QDir workingDir(m_workingDirectory);
QString absoluteFileName = workingDir.absoluteFilePath(fileName);
QFile file(absoluteFileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return m_editor.data()->codec()->toUnicode(file.readAll());
}
return QString();
}
///////////////////////////////////////////////////////////
class BaseGitDiffArgumentsWidget : public VcsBase::VcsBaseEditorParameterWidget class BaseGitDiffArgumentsWidget : public VcsBase::VcsBaseEditorParameterWidget
{ {
Q_OBJECT Q_OBJECT
@@ -506,6 +728,22 @@ VcsBase::VcsBaseEditorWidget *GitClient::findExistingVCSEditor(const char *regis
return rc; return rc;
} }
DiffEditor::DiffEditorWidget *GitClient::findExistingDiffEditor(const char *registerDynamicProperty,
const QString &dynamicPropertyValue) const
{
DiffEditor::DiffEditorWidget *editorWidget = 0;
Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
if (!outputEditor)
return 0;
// Exists already
Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
outputEditor->createNew(m_msgWait);
editorWidget = diffEditorWidget(outputEditor);
return editorWidget;
}
/* Create an editor associated to VCS output of a source file/directory /* 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 * (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 * existing instance and to reuse it (in case, say, 'git diff foo' is
@@ -549,11 +787,48 @@ VcsBase::VcsBaseEditorWidget *GitClient::createVcsEditor(const Core::Id &id,
return rc; return rc;
} }
DiffEditor::DiffEditorWidget *GitClient::diffEditorWidget(const Core::IEditor *editor) const
{
if (const DiffEditor::DiffEditorEditable *de = qobject_cast<const DiffEditor::DiffEditorEditable *>(editor))
return de->editorWidget();
return 0;
}
void GitClient::diff(const QString &workingDirectory, void GitClient::diff(const QString &workingDirectory,
const QStringList &diffArgs, const QStringList &diffArgs,
const QStringList &unstagedFileNames, const QStringList &unstagedFileNames,
const QStringList &stagedFileNames) const QStringList &stagedFileNames)
{ {
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const Core::Id editorId = DiffEditor::Constants::DIFF_EDITOR_ID;
QString title = tr("Git Diff");
DiffEditor::DiffEditorWidget *editorWidget = findExistingDiffEditor("originalFileName", workingDirectory);
if (!editorWidget) {
Core::IEditor *outputEditor = Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait);
outputEditor->document()->setProperty("originalFileName", workingDirectory);
Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch); // should probably go outside this block
editorWidget = diffEditorWidget(outputEditor);
}
int timeout = settings()->intValue(GitSettings::timeoutKey);
GitDiffHandler *handler = new GitDiffHandler(gitBinaryPath(), workingDirectory, processEnvironment(), editorWidget, 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 if (!unstagedFileNames.empty()) {
// current project diff
handler->diffProjects(unstagedFileNames);
}
}
} else {
const QString binary = settings()->stringValue(GitSettings::binaryPathKey); const QString binary = settings()->stringValue(GitSettings::binaryPathKey);
const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID; const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
const QString title = tr("Git Diff"); const QString title = tr("Git Diff");
@@ -608,11 +883,32 @@ void GitClient::diff(const QString &workingDirectory,
} }
command->execute(); command->execute();
} }
}
void GitClient::diff(const QString &workingDirectory, void GitClient::diff(const QString &workingDirectory,
const QStringList &diffArgs, const QStringList &diffArgs,
const QString &fileName) const QString &fileName)
{ {
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const Core::Id editorId = DiffEditor::Constants::DIFF_EDITOR_ID;
QString title = tr("Git Diff \"%1\"").arg(fileName);
const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
DiffEditor::DiffEditorWidget *editorWidget = findExistingDiffEditor("originalFileName", sourceFile);
if (!editorWidget) {
Core::IEditor *outputEditor = Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait);
outputEditor->document()->setProperty("originalFileName", sourceFile);
Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
editorWidget = diffEditorWidget(outputEditor);
}
if (!fileName.isEmpty()) {
int timeout = settings()->intValue(GitSettings::timeoutKey);
GitDiffHandler *handler = new GitDiffHandler(gitBinaryPath(), workingDirectory, processEnvironment(), editorWidget, timeout);
handler->diffFile(fileName);
}
} else {
const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID; const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
const QString title = tr("Git Diff \"%1\"").arg(fileName); const QString title = tr("Git Diff \"%1\"").arg(fileName);
const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName); const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
@@ -638,6 +934,7 @@ void GitClient::diff(const QString &workingDirectory,
cmdArgs << QLatin1String("--") << fileName; cmdArgs << QLatin1String("--") << fileName;
executeGit(workingDirectory, cmdArgs, editor); executeGit(workingDirectory, cmdArgs, editor);
} }
}
void GitClient::diffBranch(const QString &workingDirectory, void GitClient::diffBranch(const QString &workingDirectory,
const QStringList &diffArgs, const QStringList &diffArgs,

View File

@@ -60,11 +60,16 @@ namespace Utils {
struct SynchronousProcessResponse; struct SynchronousProcessResponse;
} }
namespace DiffEditor {
class DiffEditorWidget;
}
namespace Git { namespace Git {
namespace Internal { namespace Internal {
class GitPlugin; class GitPlugin;
class GitOutputWindow; class GitOutputWindow;
class GitDiffEditorWidget;
class CommitData; class CommitData;
struct GitSubmitEditorPanelData; struct GitSubmitEditorPanelData;
class Stash; class Stash;
@@ -128,6 +133,8 @@ public:
QString findRepositoryForDirectory(const QString &dir); QString findRepositoryForDirectory(const QString &dir);
QString findGitDirForRepository(const QString &repositoryDir) const; QString findGitDirForRepository(const QString &repositoryDir) const;
DiffEditor::DiffEditorWidget *diffEditorWidget(const Core::IEditor *editor) const;
void diff(const QString &workingDirectory, const QStringList &diffArgs, const QString &fileName); void diff(const QString &workingDirectory, const QStringList &diffArgs, const QString &fileName);
void diff(const QString &workingDirectory, const QStringList &diffArgs, void diff(const QString &workingDirectory, const QStringList &diffArgs,
const QStringList &unstagedFileNames, const QStringList &stagedFileNames= QStringList()); const QStringList &unstagedFileNames, const QStringList &stagedFileNames= QStringList());
@@ -319,6 +326,9 @@ private:
QTextCodec *getSourceCodec(const QString &file) const; QTextCodec *getSourceCodec(const QString &file) const;
VcsBase::VcsBaseEditorWidget *findExistingVCSEditor(const char *registerDynamicProperty, VcsBase::VcsBaseEditorWidget *findExistingVCSEditor(const char *registerDynamicProperty,
const QString &dynamicPropertyValue) const; const QString &dynamicPropertyValue) const;
DiffEditor::DiffEditorWidget *findExistingDiffEditor(const char *registerDynamicProperty,
const QString &dynamicPropertyValue) const;
enum CodecType { CodecSource, CodecLogOutput, CodecNone }; enum CodecType { CodecSource, CodecLogOutput, CodecNone };
VcsBase::VcsBaseEditorWidget *createVcsEditor(const Core::Id &kind, VcsBase::VcsBaseEditorWidget *createVcsEditor(const Core::Id &kind,
QString title, QString title,

View File

@@ -179,6 +179,8 @@ void GitEditor::setPlainTextDataFiltered(const QByteArray &a)
break; break;
} }
case VcsBase::DiffOutput: { case VcsBase::DiffOutput: {
if (array.isEmpty())
array = QByteArray("No difference to HEAD");
const QFileInfo fi(source()); const QFileInfo fi(source());
const QString workingDirectory = fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath(); const QString workingDirectory = fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath();
QByteArray precedes, follows; QByteArray precedes, follows;

View File

@@ -35,6 +35,7 @@
namespace Git { namespace Git {
namespace Internal { namespace Internal {
const QLatin1String GitSettings::useDiffEditorKey("UseDiffEditor");
const QLatin1String GitSettings::pullRebaseKey("PullRebase"); const QLatin1String GitSettings::pullRebaseKey("PullRebase");
const QLatin1String GitSettings::showTagsKey("ShowTags"); const QLatin1String GitSettings::showTagsKey("ShowTags");
const QLatin1String GitSettings::omitAnnotationDateKey("OmitAnnotationDate"); const QLatin1String GitSettings::omitAnnotationDateKey("OmitAnnotationDate");
@@ -54,6 +55,7 @@ GitSettings::GitSettings()
declareKey(binaryPathKey, QLatin1String("git")); declareKey(binaryPathKey, QLatin1String("git"));
declareKey(timeoutKey, Utils::HostOsInfo::isWindowsHost() ? 60 : 30); declareKey(timeoutKey, Utils::HostOsInfo::isWindowsHost() ? 60 : 30);
declareKey(useDiffEditorKey, true);
declareKey(pullRebaseKey, false); declareKey(pullRebaseKey, false);
declareKey(showTagsKey, false); declareKey(showTagsKey, false);
declareKey(omitAnnotationDateKey, false); declareKey(omitAnnotationDateKey, false);

View File

@@ -48,6 +48,7 @@ class GitSettings : public VcsBase::VcsBaseClientSettings
public: public:
GitSettings(); GitSettings();
static const QLatin1String useDiffEditorKey;
static const QLatin1String pullRebaseKey; static const QLatin1String pullRebaseKey;
static const QLatin1String showTagsKey; static const QLatin1String showTagsKey;
static const QLatin1String omitAnnotationDateKey; static const QLatin1String omitAnnotationDateKey;

View File

@@ -70,6 +70,7 @@ GitSettings SettingsPageWidget::settings() const
rc.setValue(GitSettings::pathKey, m_ui.pathLineEdit->text()); rc.setValue(GitSettings::pathKey, m_ui.pathLineEdit->text());
rc.setValue(GitSettings::logCountKey, m_ui.logCountSpinBox->value()); rc.setValue(GitSettings::logCountKey, m_ui.logCountSpinBox->value());
rc.setValue(GitSettings::timeoutKey, m_ui.timeoutSpinBox->value()); rc.setValue(GitSettings::timeoutKey, m_ui.timeoutSpinBox->value());
rc.setValue(GitSettings::useDiffEditorKey, m_ui.useDiffEditorCheckBox->isChecked());
rc.setValue(GitSettings::pullRebaseKey, m_ui.pullRebaseCheckBox->isChecked()); rc.setValue(GitSettings::pullRebaseKey, m_ui.pullRebaseCheckBox->isChecked());
rc.setValue(GitSettings::showTagsKey, m_ui.showTagsCheckBox->isChecked()); rc.setValue(GitSettings::showTagsKey, m_ui.showTagsCheckBox->isChecked());
rc.setValue(GitSettings::promptOnSubmitKey, m_ui.promptToSubmitCheckBox->isChecked()); rc.setValue(GitSettings::promptOnSubmitKey, m_ui.promptToSubmitCheckBox->isChecked());
@@ -84,6 +85,7 @@ void SettingsPageWidget::setSettings(const GitSettings &s)
m_ui.pathLineEdit->setText(s.stringValue(GitSettings::pathKey)); m_ui.pathLineEdit->setText(s.stringValue(GitSettings::pathKey));
m_ui.logCountSpinBox->setValue(s.intValue(GitSettings::logCountKey)); m_ui.logCountSpinBox->setValue(s.intValue(GitSettings::logCountKey));
m_ui.timeoutSpinBox->setValue(s.intValue(GitSettings::timeoutKey)); m_ui.timeoutSpinBox->setValue(s.intValue(GitSettings::timeoutKey));
m_ui.useDiffEditorCheckBox->setChecked(s.boolValue(GitSettings::useDiffEditorKey));
m_ui.pullRebaseCheckBox->setChecked(s.boolValue(GitSettings::pullRebaseKey)); m_ui.pullRebaseCheckBox->setChecked(s.boolValue(GitSettings::pullRebaseKey));
m_ui.showTagsCheckBox->setChecked(s.boolValue(GitSettings::showTagsKey)); m_ui.showTagsCheckBox->setChecked(s.boolValue(GitSettings::showTagsKey));
m_ui.promptToSubmitCheckBox->setChecked(s.boolValue(GitSettings::promptOnSubmitKey)); m_ui.promptToSubmitCheckBox->setChecked(s.boolValue(GitSettings::promptOnSubmitKey));

View File

@@ -137,6 +137,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="3">
<widget class="QCheckBox" name="useDiffEditorCheckBox">
<property name="text">
<string>Use Diff Editor</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@@ -271,19 +271,13 @@ void Command::run()
} }
} }
// Special hack: Always produce output for diff
if (ok && stdOut.isEmpty() && d->m_jobs.front().arguments.at(0) == QLatin1String("diff")) {
stdOut += "No difference to HEAD";
} else {
// @TODO: Remove, see below
if (ok && d->m_jobs.front().arguments.at(0) == QLatin1String("status")) if (ok && d->m_jobs.front().arguments.at(0) == QLatin1String("status"))
removeColorCodes(&stdOut); removeColorCodes(&stdOut);
}
d->m_lastExecSuccess = ok; d->m_lastExecSuccess = ok;
d->m_lastExecExitCode = exitCode; d->m_lastExecExitCode = exitCode;
if (ok && !stdOut.isEmpty()) if (ok)
emit outputData(stdOut); emit outputData(stdOut);
if (!error.isEmpty()) if (!error.isEmpty())