From 97a86c50dc00551818e9fcef37908ef7ebfb242b Mon Sep 17 00:00:00 2001 From: jkobus <jaroslaw.kobus@digia.com> Date: Tue, 7 May 2013 14:02:08 +0200 Subject: [PATCH] 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> --- src/plugins/diffeditor/diffeditor.pro | 12 +- src/plugins/diffeditor/diffeditor.qbs | 12 +- src/plugins/diffeditor/diffeditoreditable.cpp | 162 +++++++ src/plugins/diffeditor/diffeditoreditable.h | 82 ++++ src/plugins/diffeditor/diffeditorfile.cpp | 96 ++++ src/plugins/diffeditor/diffeditorfile.h | 78 ++++ src/plugins/diffeditor/diffeditorplugin.cpp | 202 +-------- src/plugins/diffeditor/diffeditorplugin.h | 74 +-- src/plugins/diffeditor/diffeditorwidget.cpp | 160 ++++--- src/plugins/diffeditor/diffeditorwidget.h | 66 +-- src/plugins/git/git.qbs | 1 + src/plugins/git/git_dependencies.pri | 3 +- src/plugins/git/gitclient.cpp | 423 +++++++++++++++--- src/plugins/git/gitclient.h | 10 + src/plugins/git/giteditor.cpp | 2 + src/plugins/git/gitsettings.cpp | 2 + src/plugins/git/gitsettings.h | 1 + src/plugins/git/settingspage.cpp | 2 + src/plugins/git/settingspage.ui | 7 + src/plugins/vcsbase/command.cpp | 12 +- 20 files changed, 965 insertions(+), 442 deletions(-) create mode 100644 src/plugins/diffeditor/diffeditoreditable.cpp create mode 100644 src/plugins/diffeditor/diffeditoreditable.h create mode 100644 src/plugins/diffeditor/diffeditorfile.cpp create mode 100644 src/plugins/diffeditor/diffeditorfile.h diff --git a/src/plugins/diffeditor/diffeditor.pro b/src/plugins/diffeditor/diffeditor.pro index 9e36bdbf774..2d6b13fc23b 100644 --- a/src/plugins/diffeditor/diffeditor.pro +++ b/src/plugins/diffeditor/diffeditor.pro @@ -1,13 +1,17 @@ DEFINES += DIFFEDITOR_LIBRARY include(../../qtcreatorplugin.pri) -HEADERS += diffeditorplugin.h \ - diffeditorwidget.h \ +HEADERS += diffeditor_global.h \ diffeditorconstants.h \ - diffeditor_global.h \ + diffeditoreditable.h \ + diffeditorfile.h \ + diffeditorplugin.h \ + diffeditorwidget.h \ differ.h -SOURCES += diffeditorplugin.cpp \ +SOURCES += diffeditoreditable.cpp \ + diffeditorfile.cpp \ + diffeditorplugin.cpp \ diffeditorwidget.cpp \ differ.cpp diff --git a/src/plugins/diffeditor/diffeditor.qbs b/src/plugins/diffeditor/diffeditor.qbs index 568321d1498..6d472cbc75e 100644 --- a/src/plugins/diffeditor/diffeditor.qbs +++ b/src/plugins/diffeditor/diffeditor.qbs @@ -13,14 +13,18 @@ QtcPlugin { Depends { name: "cpp" } files: [ + "diffeditor_global.h", + "diffeditorconstants.h", + "diffeditoreditable.cpp", + "diffeditoreditable.h", + "diffeditorfile.cpp", + "diffeditorfile.h", "diffeditorplugin.cpp", "diffeditorplugin.h", - "differ.cpp", - "differ.h", "diffeditorwidget.cpp", "diffeditorwidget.h", - "diffeditorconstants.h", - "diffeditor_global.h", + "differ.cpp", + "differ.h", ] } diff --git a/src/plugins/diffeditor/diffeditoreditable.cpp b/src/plugins/diffeditor/diffeditoreditable.cpp new file mode 100644 index 00000000000..9f2c9e1a8a3 --- /dev/null +++ b/src/plugins/diffeditor/diffeditoreditable.cpp @@ -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 diff --git a/src/plugins/diffeditor/diffeditoreditable.h b/src/plugins/diffeditor/diffeditoreditable.h new file mode 100644 index 00000000000..8047fce312a --- /dev/null +++ b/src/plugins/diffeditor/diffeditoreditable.h @@ -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 diff --git a/src/plugins/diffeditor/diffeditorfile.cpp b/src/plugins/diffeditor/diffeditorfile.cpp new file mode 100644 index 00000000000..ede0392bd29 --- /dev/null +++ b/src/plugins/diffeditor/diffeditorfile.cpp @@ -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 diff --git a/src/plugins/diffeditor/diffeditorfile.h b/src/plugins/diffeditor/diffeditorfile.h new file mode 100644 index 00000000000..4a45fb4aa5c --- /dev/null +++ b/src/plugins/diffeditor/diffeditorfile.h @@ -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 diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp index 30fa36ca83f..a4a1429539a 100644 --- a/src/plugins/diffeditor/diffeditorplugin.cpp +++ b/src/plugins/diffeditor/diffeditorplugin.cpp @@ -28,17 +28,14 @@ ****************************************************************************/ #include "diffeditorplugin.h" +#include "diffeditoreditable.h" #include "diffeditorwidget.h" #include "diffeditorconstants.h" -#include <coreplugin/icore.h> #include <QCoreApplication> -#include <QToolButton> -#include <QSpinBox> -#include <QStyle> -#include <QLabel> #include <QFileDialog> #include <QTextCodec> +#include <QtPlugin> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -46,195 +43,14 @@ #include <coreplugin/editormanager/editormanager.h> 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(DiffEditorPlugin *owner) : - m_mimeTypes(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE)), - m_owner(owner) +namespace Internal { + +DiffEditorFactory::DiffEditorFactory(DiffEditorPlugin *owner) + : m_mimeTypes(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE)), + m_owner(owner) { } @@ -329,9 +145,9 @@ void DiffEditorPlugin::diff() const QString text2 = getFileContents(fileName2, editorWidget->codec()); DiffEditorWidget::DiffFilesContents dfc; - dfc.leftFileName = fileName1; + dfc.leftFileInfo = fileName1; dfc.leftText = text1; - dfc.rightFileName = fileName2; + dfc.rightFileInfo = fileName2; dfc.rightText = text2; QList<DiffEditorWidget::DiffFilesContents> list; list.append(dfc); diff --git a/src/plugins/diffeditor/diffeditorplugin.h b/src/plugins/diffeditor/diffeditorplugin.h index cc0de258fd7..39b6e4bf510 100644 --- a/src/plugins/diffeditor/diffeditorplugin.h +++ b/src/plugins/diffeditor/diffeditorplugin.h @@ -30,86 +30,22 @@ #ifndef DIFFEDITORPLUGIN_H #define DIFFEDITORPLUGIN_H +#include "diffeditor_global.h" + #include <extensionsystem/iplugin.h> #include <coreplugin/editormanager/ieditorfactory.h> -#include <coreplugin/editormanager/ieditor.h> #include <coreplugin/icontext.h> #include <coreplugin/idocument.h> -#include <QtPlugin> -#include <QPointer> #include <QStringList> -#include <QAction> -#include <QToolBar> + +namespace Core { class IEditor; } namespace DiffEditor { + class DiffEditorWidget; 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 { diff --git a/src/plugins/diffeditor/diffeditorwidget.cpp b/src/plugins/diffeditor/diffeditorwidget.cpp index bf6630d6c9d..fd8a43e9589 100644 --- a/src/plugins/diffeditor/diffeditorwidget.cpp +++ b/src/plugins/diffeditor/diffeditorwidget.cpp @@ -35,9 +35,7 @@ #include <QTextBlock> #include <QScrollBar> #include <QPainter> -#include <QTime> - -#include <QDebug> +#include <QDir> #include <texteditor/basetexteditor.h> #include <texteditor/snippets/snippeteditor.h> @@ -54,6 +52,46 @@ using namespace TextEditor; 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 @@ -86,12 +124,15 @@ public: QMap<int, int> skippedLines() const { return m_skippedLines; } + void setWorkingDirectory(const QString &workingDirectory) { m_workingDirectory = workingDirectory; } 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 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); } + void clearAll(); + void clearAll(const QString &message); void clearAllData(); QTextBlock firstVisibleBlock() const { return SnippetEditorWidget::firstVisibleBlock(); } @@ -116,10 +157,11 @@ private: void paintSeparator(QPainter &painter, const QString &text, const QTextBlock &block, int top); void jumpToOriginalFile(const QTextCursor &cursor); + QString m_workingDirectory; QMap<int, int> m_lineNumbers; int m_lineNumberDigits; - // block number, fileName - QMap<int, QString> m_fileNames; + // block number, fileInfo + QMap<int, DiffEditorWidget::DiffFileInfo> m_fileInfo; // block number, skipped lines QMap<int, int> m_skippedLines; // 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()); } +void DiffViewEditorWidget::clearAll() +{ + clearAll(tr("No difference")); +} + +void DiffViewEditorWidget::clearAll(const QString &message) +{ + setBlockSelection(false); + clear(); + clearAllData(); + setPlainText(message); +} + void DiffViewEditorWidget::clearAllData() { m_lineNumberDigits = 1; m_lineNumbers.clear(); - m_fileNames.clear(); + m_fileInfo.clear(); m_skippedLines.clear(); m_separators.clear(); } @@ -253,7 +308,7 @@ void DiffViewEditorWidget::mouseDoubleClickEvent(QMouseEvent *e) void DiffViewEditorWidget::jumpToOriginalFile(const QTextCursor &cursor) { - if (m_fileNames.isEmpty()) + if (m_fileInfo.isEmpty()) return; const int blockNumber = cursor.blockNumber(); @@ -262,10 +317,11 @@ void DiffViewEditorWidget::jumpToOriginalFile(const QTextCursor &cursor) return; const int lineNr = m_lineNumbers.value(blockNumber); - QMap<int, QString>::const_iterator it = m_fileNames.upperBound(blockNumber); - if (it != m_fileNames.constBegin()) + QMap<int, DiffEditorWidget::DiffFileInfo>::const_iterator it = m_fileInfo.upperBound(blockNumber); + if (it != m_fileInfo.constBegin()) --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); if (TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor *>(ed)) @@ -300,9 +356,12 @@ void DiffViewEditorWidget::paintEvent(QPaintEvent *e) paintSeparator(painter, skippedRowsText, currentBlock, top); } - const QString fileName = m_fileNames.value(blockNumber); - if (!fileName.isEmpty()) { - paintSeparator(painter, fileName, currentBlock, top); + const DiffEditorWidget::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); + paintSeparator(painter, fileNameText, currentBlock, top); } } } @@ -416,7 +475,7 @@ void DiffViewEditorWidget::drawCollapsedBlockPopup(QPainter &painter, DiffEditorWidget::DiffEditorWidget(QWidget *parent) : QWidget(parent), - m_contextLinesNumber(1), + m_contextLinesNumber(3), m_ignoreWhitespaces(true), m_foldingBlocker(false) { @@ -457,6 +516,8 @@ DiffEditorWidget::DiffEditorWidget(QWidget *parent) m_splitter->addWidget(m_rightEditor); QVBoxLayout *l = new QVBoxLayout(this); l->addWidget(m_splitter); + + clear(); } 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; QList<DiffList> diffList; for (int i = 0; i < diffFileList.count(); i++) { DiffFilesContents dfc = diffFileList.at(i); DiffList dl; - dl.leftFileName = dfc.leftFileName; - dl.rightFileName = dfc.rightFileName; + dl.leftFileInfo = dfc.leftFileInfo; + dl.rightFileInfo = dfc.rightFileInfo; dl.diffList = differ.cleanupSemantics(differ.diff(dfc.leftText, dfc.rightText)); diffList.append(dl); } @@ -490,8 +565,8 @@ void DiffEditorWidget::setDiff(const QList<DiffList> &diffList) ChunkData chunkData = calculateOriginalData(dl.diffList); m_originalChunkData.append(chunkData); FileData fileData = calculateContextData(chunkData); - fileData.leftFileName = dl.leftFileName; - fileData.rightFileName = dl.rightFileName; + fileData.leftFileInfo = dl.leftFileInfo; + fileData.rightFileInfo = dl.rightFileInfo; m_contextFileData.append(fileData); } showDiff(); @@ -506,8 +581,8 @@ void DiffEditorWidget::setContextLinesNumber(int lines) for (int i = 0; i < m_diffList.count(); i++) { const FileData oldFileData = m_contextFileData.at(i); FileData newFileData = calculateContextData(m_originalChunkData.at(i)); - newFileData.leftFileName = oldFileData.leftFileName; - newFileData.rightFileName = oldFileData.rightFileName; + newFileData.leftFileInfo = oldFileData.leftFileInfo; + newFileData.rightFileInfo = oldFileData.rightFileInfo; m_contextFileData[i] = newFileData; } @@ -855,8 +930,8 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c RowData rowData = originalData.rows.at(i); chunkData.rows.append(rowData); - leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it - rightCharCounter += rowData.rightLine.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 '\n' i++; } while (leftChangedIt != originalData.changedLeftPositions.constEnd()) { @@ -889,8 +964,8 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c RowData rowData = originalData.rows.at(i); chunkData.rows.append(rowData); - leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it - rightCharCounter += rowData.rightLine.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 '\n' i++; } fileData.chunks.append(chunkData); @@ -901,21 +976,12 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c void DiffEditorWidget::showDiff() { -// QTime time; -// time.start(); - // TODO: remember the line number of the line in the middle const int verticalValue = m_leftEditor->verticalScrollBar()->value(); const int leftHorizontalValue = m_leftEditor->horizontalScrollBar()->value(); const int rightHorizontalValue = m_rightEditor->horizontalScrollBar()->value(); - m_leftEditor->setBlockSelection(false); - m_rightEditor->setBlockSelection(false); - m_leftEditor->clear(); - m_rightEditor->clear(); - m_leftEditor->clearAllData(); - m_rightEditor->clearAllData(); -// int ela1 = time.elapsed(); + clear(); QString leftText, rightText; int blockNumber = 0; @@ -925,8 +991,8 @@ void DiffEditorWidget::showDiff() int leftLineNumber = 0; int rightLineNumber = 0; - m_leftEditor->setFileName(blockNumber, contextFileData.leftFileName); - m_rightEditor->setFileName(blockNumber, contextFileData.rightFileName); + m_leftEditor->setFileInfo(blockNumber, contextFileData.leftFileInfo); + m_rightEditor->setFileInfo(blockNumber, contextFileData.rightFileInfo); leftText += separator; rightText += separator; blockNumber++; @@ -967,13 +1033,12 @@ void DiffEditorWidget::showDiff() } } } -// int ela2 = time.elapsed(); + + if (leftText.isEmpty() && rightText.isEmpty()) + return; m_leftEditor->setPlainText(leftText); m_rightEditor->setPlainText(rightText); -// int ela3 = time.elapsed(); - -// int ela4 = time.elapsed(); colorDiff(m_contextFileData); @@ -1029,14 +1094,10 @@ void DiffEditorWidget::showDiff() } m_foldingBlocker = false; -// int ela5 = time.elapsed(); - m_leftEditor->verticalScrollBar()->setValue(verticalValue); m_rightEditor->verticalScrollBar()->setValue(verticalValue); m_leftEditor->horizontalScrollBar()->setValue(leftHorizontalValue); m_rightEditor->horizontalScrollBar()->setValue(rightHorizontalValue); -// int ela6 = time.elapsed(); -// qDebug() << ela1 << ela2 << ela3 << ela4 << ela5 << ela6; m_leftEditor->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 rightPos = 0; - // startPos, endPos QMap<int, int> leftLinePos; QMap<int, int> rightLinePos; QMap<int, int> leftCharPos; @@ -1154,8 +1214,8 @@ void DiffEditorWidget::colorDiff(const QList<FileData> &fileDataList) for (int k = 0; k < chunkData.rows.count(); 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 - rightPos += rowData.rightLine.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 '\n' if (!rowData.equal) { if (rowData.leftLine.textLineType == TextLineData::TextLine) { diff --git a/src/plugins/diffeditor/diffeditorwidget.h b/src/plugins/diffeditor/diffeditorwidget.h index 17b51074857..b4e7fe83b4b 100644 --- a/src/plugins/diffeditor/diffeditorwidget.h +++ b/src/plugins/diffeditor/diffeditorwidget.h @@ -52,59 +52,26 @@ QT_END_NAMESPACE namespace DiffEditor { class DiffViewEditorWidget; - -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; - QString leftFileName; - QString rightFileName; -}; - -struct DiffData { - QList<FileData> files; -}; +struct TextLineData; +struct ChunkData; +struct FileData; class DIFFEDITOR_EXPORT DiffEditorWidget : public QWidget { Q_OBJECT 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 { - QString leftFileName; + DiffFileInfo leftFileInfo; QString leftText; - QString rightFileName; + DiffFileInfo rightFileInfo; QString rightText; }; @@ -112,7 +79,8 @@ public: ~DiffEditorWidget(); 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; public slots: @@ -131,8 +99,8 @@ private slots: private: struct DiffList { - QString leftFileName; - QString rightFileName; + DiffFileInfo leftFileInfo; + DiffFileInfo rightFileInfo; QList<Diff> diffList; }; diff --git a/src/plugins/git/git.qbs b/src/plugins/git/git.qbs index 95fa416ea2e..80acf97b6ed 100644 --- a/src/plugins/git/git.qbs +++ b/src/plugins/git/git.qbs @@ -10,6 +10,7 @@ QtcPlugin { Depends { name: "TextEditor" } Depends { name: "Find" } Depends { name: "VcsBase" } + Depends { name: "DiffEditor" } Depends { name: "Locator" } files: [ diff --git a/src/plugins/git/git_dependencies.pri b/src/plugins/git/git_dependencies.pri index fccc00210e0..e21fdc7299d 100644 --- a/src/plugins/git/git_dependencies.pri +++ b/src/plugins/git/git_dependencies.pri @@ -5,4 +5,5 @@ QTC_PLUGIN_DEPENDS += \ projectexplorer \ texteditor \ coreplugin \ - vcsbase + vcsbase \ + diffeditor diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 7ed91ff072f..134ab12030a 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -56,6 +56,10 @@ #include <vcsbase/vcsbaseoutputwindow.h> #include <vcsbase/vcsbaseplugin.h> +#include <diffeditor/diffeditorwidget.h> +#include <diffeditor/diffeditoreditable.h> +#include <diffeditor/diffeditorconstants.h> + #include <QCoreApplication> #include <QDir> #include <QFileInfo> @@ -75,6 +79,224 @@ static const char graphLogFormatC[] = "%h %d %an %s %ci"; namespace Git { 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 { Q_OBJECT @@ -506,6 +728,22 @@ VcsBase::VcsBaseEditorWidget *GitClient::findExistingVCSEditor(const char *regis 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 * (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 @@ -549,94 +787,153 @@ VcsBase::VcsBaseEditorWidget *GitClient::createVcsEditor(const Core::Id &id, 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, const QStringList &diffArgs, const QStringList &unstagedFileNames, const QStringList &stagedFileNames) { - const QString binary = settings()->stringValue(GitSettings::binaryPathKey); - const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID; - const QString title = tr("Git Diff"); - - VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", workingDirectory); - if (!editor) { - GitCommitDiffArgumentsWidget *argWidget = - new GitCommitDiffArgumentsWidget(this, workingDirectory, diffArgs, - unstagedFileNames, stagedFileNames); + if (settings()->boolValue(GitSettings::useDiffEditorKey)) { + const Core::Id editorId = DiffEditor::Constants::DIFF_EDITOR_ID; + QString title = tr("Git Diff"); - editor = createVcsEditor(editorId, title, - workingDirectory, CodecSource, "originalFileName", workingDirectory, argWidget); - connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand())); - } - - GitCommitDiffArgumentsWidget *argWidget = qobject_cast<GitCommitDiffArgumentsWidget *>(editor->configurationWidget()); - QStringList userDiffArgs = argWidget->arguments(); - editor->setDiffBaseDirectory(workingDirectory); + DiffEditor::DiffEditorWidget *editorWidget = findExistingDiffEditor("originalFileName", 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, editor); - // Directory diff? + 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 - QStringList cmdArgs; - cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption); + editorWidget = diffEditorWidget(outputEditor); + } - int timeout = settings()->intValue(GitSettings::timeoutKey); + int timeout = settings()->intValue(GitSettings::timeoutKey); + GitDiffHandler *handler = new GitDiffHandler(gitBinaryPath(), workingDirectory, processEnvironment(), editorWidget, timeout); - if (unstagedFileNames.empty() && stagedFileNames.empty()) { - QStringList arguments(cmdArgs); - arguments << userDiffArgs; - outputWindow()->appendCommand(workingDirectory, binary, arguments); - command->addJob(arguments, 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 { - // Files diff. - if (!unstagedFileNames.empty()) { - QStringList arguments(cmdArgs); - arguments << userDiffArgs; - arguments << QLatin1String("--") << unstagedFileNames; - outputWindow()->appendCommand(workingDirectory, binary, arguments); - command->addJob(arguments, timeout); + const QString binary = settings()->stringValue(GitSettings::binaryPathKey); + const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID; + const QString title = tr("Git Diff"); + + VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", workingDirectory); + if (!editor) { + GitCommitDiffArgumentsWidget *argWidget = + new GitCommitDiffArgumentsWidget(this, workingDirectory, diffArgs, + unstagedFileNames, stagedFileNames); + + editor = createVcsEditor(editorId, title, + workingDirectory, CodecSource, "originalFileName", workingDirectory, argWidget); + connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand())); } - if (!stagedFileNames.empty()) { - QStringList arguments(cmdArgs); - arguments << userDiffArgs; - arguments << QLatin1String("--cached") << diffArgs << QLatin1String("--") << stagedFileNames; - outputWindow()->appendCommand(workingDirectory, binary, arguments); - command->addJob(arguments, timeout); + + GitCommitDiffArgumentsWidget *argWidget = qobject_cast<GitCommitDiffArgumentsWidget *>(editor->configurationWidget()); + QStringList userDiffArgs = argWidget->arguments(); + editor->setDiffBaseDirectory(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, editor); + // Directory diff? + + QStringList cmdArgs; + cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption); + + int timeout = settings()->intValue(GitSettings::timeoutKey); + + 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; + arguments << QLatin1String("--") << unstagedFileNames; + outputWindow()->appendCommand(workingDirectory, binary, arguments); + command->addJob(arguments, timeout); + } + if (!stagedFileNames.empty()) { + QStringList arguments(cmdArgs); + arguments << userDiffArgs; + arguments << QLatin1String("--cached") << diffArgs << QLatin1String("--") << stagedFileNames; + outputWindow()->appendCommand(workingDirectory, binary, arguments); + command->addJob(arguments, timeout); + } } + command->execute(); } - command->execute(); } void GitClient::diff(const QString &workingDirectory, const QStringList &diffArgs, const QString &fileName) { - const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID; - const QString title = tr("Git Diff \"%1\"").arg(fileName); - const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, 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); - VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", sourceFile); - if (!editor) { - GitFileDiffArgumentsWidget *argWidget = - new GitFileDiffArgumentsWidget(this, workingDirectory, diffArgs, 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); - editor = createVcsEditor(editorId, title, sourceFile, CodecSource, "originalFileName", sourceFile, argWidget); - connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand())); - } - editor->setDiffBaseDirectory(workingDirectory); + editorWidget = diffEditorWidget(outputEditor); + } - GitFileDiffArgumentsWidget *argWidget = qobject_cast<GitFileDiffArgumentsWidget *>(editor->configurationWidget()); - QStringList userDiffArgs = argWidget->arguments(); + 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 QString title = tr("Git Diff \"%1\"").arg(fileName); + const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName); - QStringList cmdArgs; - cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption) - << userDiffArgs; + VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", sourceFile); + if (!editor) { + GitFileDiffArgumentsWidget *argWidget = + new GitFileDiffArgumentsWidget(this, workingDirectory, diffArgs, fileName); - if (!fileName.isEmpty()) - cmdArgs << QLatin1String("--") << fileName; - executeGit(workingDirectory, cmdArgs, editor); + editor = createVcsEditor(editorId, title, sourceFile, CodecSource, "originalFileName", sourceFile, argWidget); + connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand())); + } + editor->setDiffBaseDirectory(workingDirectory); + + GitFileDiffArgumentsWidget *argWidget = qobject_cast<GitFileDiffArgumentsWidget *>(editor->configurationWidget()); + QStringList userDiffArgs = argWidget->arguments(); + + QStringList cmdArgs; + cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption) + << userDiffArgs; + + if (!fileName.isEmpty()) + cmdArgs << QLatin1String("--") << fileName; + executeGit(workingDirectory, cmdArgs, editor); + } } void GitClient::diffBranch(const QString &workingDirectory, diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index b02ed7cffb3..003cf6b92cd 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -60,11 +60,16 @@ namespace Utils { struct SynchronousProcessResponse; } +namespace DiffEditor { + class DiffEditorWidget; +} + namespace Git { namespace Internal { class GitPlugin; class GitOutputWindow; +class GitDiffEditorWidget; class CommitData; struct GitSubmitEditorPanelData; class Stash; @@ -128,6 +133,8 @@ public: QString findRepositoryForDirectory(const QString &dir); 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 QStringList &unstagedFileNames, const QStringList &stagedFileNames= QStringList()); @@ -319,6 +326,9 @@ private: QTextCodec *getSourceCodec(const QString &file) const; VcsBase::VcsBaseEditorWidget *findExistingVCSEditor(const char *registerDynamicProperty, const QString &dynamicPropertyValue) const; + DiffEditor::DiffEditorWidget *findExistingDiffEditor(const char *registerDynamicProperty, + const QString &dynamicPropertyValue) const; + enum CodecType { CodecSource, CodecLogOutput, CodecNone }; VcsBase::VcsBaseEditorWidget *createVcsEditor(const Core::Id &kind, QString title, diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp index 1b4d3e3fd64..fb6bea95f9a 100644 --- a/src/plugins/git/giteditor.cpp +++ b/src/plugins/git/giteditor.cpp @@ -179,6 +179,8 @@ void GitEditor::setPlainTextDataFiltered(const QByteArray &a) break; } case VcsBase::DiffOutput: { + if (array.isEmpty()) + array = QByteArray("No difference to HEAD"); const QFileInfo fi(source()); const QString workingDirectory = fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath(); QByteArray precedes, follows; diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp index 2c1e27fc8ac..7bacca13070 100644 --- a/src/plugins/git/gitsettings.cpp +++ b/src/plugins/git/gitsettings.cpp @@ -35,6 +35,7 @@ namespace Git { namespace Internal { +const QLatin1String GitSettings::useDiffEditorKey("UseDiffEditor"); const QLatin1String GitSettings::pullRebaseKey("PullRebase"); const QLatin1String GitSettings::showTagsKey("ShowTags"); const QLatin1String GitSettings::omitAnnotationDateKey("OmitAnnotationDate"); @@ -54,6 +55,7 @@ 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 eaf01280175..dcf5fd940e4 100644 --- a/src/plugins/git/gitsettings.h +++ b/src/plugins/git/gitsettings.h @@ -48,6 +48,7 @@ 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/git/settingspage.cpp b/src/plugins/git/settingspage.cpp index a76d07bc2cc..08b4875e44c 100644 --- a/src/plugins/git/settingspage.cpp +++ b/src/plugins/git/settingspage.cpp @@ -70,6 +70,7 @@ GitSettings SettingsPageWidget::settings() const rc.setValue(GitSettings::pathKey, m_ui.pathLineEdit->text()); rc.setValue(GitSettings::logCountKey, m_ui.logCountSpinBox->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::showTagsKey, m_ui.showTagsCheckBox->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.logCountSpinBox->setValue(s.intValue(GitSettings::logCountKey)); 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.showTagsCheckBox->setChecked(s.boolValue(GitSettings::showTagsKey)); m_ui.promptToSubmitCheckBox->setChecked(s.boolValue(GitSettings::promptOnSubmitKey)); diff --git a/src/plugins/git/settingspage.ui b/src/plugins/git/settingspage.ui index d67a59369ea..881ae3570a4 100644 --- a/src/plugins/git/settingspage.ui +++ b/src/plugins/git/settingspage.ui @@ -137,6 +137,13 @@ </property> </widget> </item> + <item row="2" column="3"> + <widget class="QCheckBox" name="useDiffEditorCheckBox"> + <property name="text"> + <string>Use Diff Editor</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/plugins/vcsbase/command.cpp b/src/plugins/vcsbase/command.cpp index 556f86515f6..d6edff387ad 100644 --- a/src/plugins/vcsbase/command.cpp +++ b/src/plugins/vcsbase/command.cpp @@ -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")) - removeColorCodes(&stdOut); - } + if (ok && d->m_jobs.front().arguments.at(0) == QLatin1String("status")) + removeColorCodes(&stdOut); d->m_lastExecSuccess = ok; d->m_lastExecExitCode = exitCode; - if (ok && !stdOut.isEmpty()) + if (ok) emit outputData(stdOut); if (!error.isEmpty()) -- GitLab