forked from qt-creator/qt-creator
Experimental integration of DiffEditor
After enabling the plugin go to Tools|Diff... Change-Id: I793b6faedb93f58039df0a62e82fe04a017978ee Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
20
src/plugins/diffeditor/DiffEditor.pluginspec.in
Normal file
20
src/plugins/diffeditor/DiffEditor.pluginspec.in
Normal file
@@ -0,0 +1,20 @@
|
||||
<plugin name=\"DiffEditor\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\" experimental=\"true\">
|
||||
<vendor>Digia Plc</vendor>
|
||||
<copyright>(C) 2013 Digia Plc</copyright>
|
||||
<license>
|
||||
Commercial Usage
|
||||
|
||||
Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Digia.
|
||||
|
||||
GNU Lesser General Public License Usage
|
||||
|
||||
Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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.
|
||||
</license>
|
||||
<category>Qt Creator</category>
|
||||
<description>Diff editor component.</description>
|
||||
<url>http://www.qt-project.org</url>
|
||||
<dependencyList>
|
||||
<dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
|
||||
<dependency name=\"TextEditor\" version=\"$$QTCREATOR_VERSION\"/>
|
||||
</dependencyList>
|
||||
</plugin>
|
||||
3
src/plugins/diffeditor/diffeditor.pri
Normal file
3
src/plugins/diffeditor/diffeditor.pri
Normal file
@@ -0,0 +1,3 @@
|
||||
include(diffeditor_dependencies.pri)
|
||||
|
||||
LIBS *= -l$$qtLibraryName(DiffEditor)
|
||||
17
src/plugins/diffeditor/diffeditor.pro
Normal file
17
src/plugins/diffeditor/diffeditor.pro
Normal file
@@ -0,0 +1,17 @@
|
||||
TEMPLATE = lib
|
||||
TARGET = DiffEditor
|
||||
DEFINES += DIFFEDITOR_LIBRARY
|
||||
include(../../qtcreatorplugin.pri)
|
||||
include(diffeditor_dependencies.pri)
|
||||
|
||||
HEADERS += diffeditorplugin.h \
|
||||
diffeditorwidget.h \
|
||||
diffeditorconstants.h \
|
||||
diffeditor_global.h \
|
||||
differ.h
|
||||
|
||||
SOURCES += diffeditorplugin.cpp \
|
||||
diffeditorwidget.cpp \
|
||||
differ.cpp
|
||||
|
||||
RESOURCES +=
|
||||
26
src/plugins/diffeditor/diffeditor.qbs
Normal file
26
src/plugins/diffeditor/diffeditor.qbs
Normal file
@@ -0,0 +1,26 @@
|
||||
import qbs.base 1.0
|
||||
|
||||
import "../QtcPlugin.qbs" as QtcPlugin
|
||||
|
||||
QtcPlugin {
|
||||
name: "DiffEditor"
|
||||
|
||||
Depends { name: "Qt.widgets" }
|
||||
Depends { name: "Core" }
|
||||
Depends { name: "TextEditor" }
|
||||
Depends { name: "Find" }
|
||||
|
||||
Depends { name: "cpp" }
|
||||
|
||||
files: [
|
||||
"diffeditorplugin.cpp",
|
||||
"diffeditorplugin.h",
|
||||
"differ.cpp",
|
||||
"differ.h",
|
||||
"diffeditorwidget.cpp",
|
||||
"diffeditorwidget.h",
|
||||
"diffeditorconstants.h",
|
||||
"diffeditor_global.h",
|
||||
]
|
||||
}
|
||||
|
||||
3
src/plugins/diffeditor/diffeditor_dependencies.pri
Normal file
3
src/plugins/diffeditor/diffeditor_dependencies.pri
Normal file
@@ -0,0 +1,3 @@
|
||||
include(../../libs/utils/utils.pri)
|
||||
include(../../plugins/texteditor/texteditor.pri)
|
||||
include(../../plugins/coreplugin/coreplugin.pri)
|
||||
41
src/plugins/diffeditor/diffeditor_global.h
Normal file
41
src/plugins/diffeditor/diffeditor_global.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 DIFFEDITOR_GLOBAL_H
|
||||
#define DIFFEDITOR_GLOBAL_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(DIFFEDITOR_LIBRARY)
|
||||
# define DIFFEDITOR_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define DIFFEDITOR_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
#endif // DIFFEDITOR_GLOBAL_H
|
||||
44
src/plugins/diffeditor/diffeditorconstants.h
Normal file
44
src/plugins/diffeditor/diffeditorconstants.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 DIFFEDITORCONSTANTS_H
|
||||
#define DIFFEDITORCONSTANTS_H
|
||||
|
||||
namespace DIFFEditor {
|
||||
namespace Constants {
|
||||
|
||||
const char DIFF_EDITOR_ID[] = "Diff Editor";
|
||||
const char DIFF_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("DiffEditor", "Diff Editor");
|
||||
const char DIFF_EDITOR_MIMETYPE[] = "text/x-patch";
|
||||
const char G_TOOLS_DIFF[] = "QtCreator.Group.Tools.Options";
|
||||
|
||||
} // namespace Constants
|
||||
} // namespace DIFFEditor
|
||||
|
||||
#endif // DIFFEDITORCONSTANTS_H
|
||||
352
src/plugins/diffeditor/diffeditorplugin.cpp
Normal file
352
src/plugins/diffeditor/diffeditorplugin.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "diffeditorplugin.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 <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
|
||||
using namespace DIFFEditor;
|
||||
using namespace DIFFEditor::Internal;
|
||||
|
||||
///////////////////////////////// DiffEditor //////////////////////////////////
|
||||
|
||||
DiffEditor::DiffEditor(DiffEditorWidget *editorWidget)
|
||||
:
|
||||
IEditor(0),
|
||||
m_file(new DiffFile(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE), this)),
|
||||
m_editorWidget(editorWidget),
|
||||
m_toolWidget(0)
|
||||
{
|
||||
setWidget(editorWidget);
|
||||
}
|
||||
|
||||
DiffEditor::~DiffEditor()
|
||||
{
|
||||
delete m_toolWidget;
|
||||
if (m_widget)
|
||||
delete m_widget;
|
||||
}
|
||||
|
||||
bool DiffEditor::createNew(const QString &contents)
|
||||
{
|
||||
Q_UNUSED(contents)
|
||||
// setFileContents(contents);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiffEditor::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 *DiffEditor::document()
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
|
||||
QString DiffEditor::displayName() const
|
||||
{
|
||||
if (m_displayName.isEmpty())
|
||||
m_displayName = QCoreApplication::translate("DiffEditor", Constants::DIFF_EDITOR_DISPLAY_NAME);
|
||||
return m_displayName;
|
||||
}
|
||||
|
||||
void DiffEditor::setDisplayName(const QString &title)
|
||||
{
|
||||
m_displayName = title;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
bool DiffEditor::duplicateSupported() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::IEditor *DiffEditor::duplicate(QWidget * /*parent*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Core::Id DiffEditor::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 *DiffEditor::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 DiffEditor::saveState() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
bool DiffEditor::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)
|
||||
{
|
||||
}
|
||||
|
||||
Core::Id DiffEditorFactory::id() const
|
||||
{
|
||||
return Constants::DIFF_EDITOR_ID;
|
||||
}
|
||||
|
||||
QString DiffEditorFactory::displayName() const
|
||||
{
|
||||
return qApp->translate("DiffEditorFactory", Constants::DIFF_EDITOR_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
Core::IEditor *DiffEditorFactory::createEditor(QWidget *parent)
|
||||
{
|
||||
DiffEditorWidget *editorWidget = new DiffEditorWidget(parent);
|
||||
DiffEditor *editor = new DiffEditor(editorWidget);
|
||||
return editor;
|
||||
}
|
||||
|
||||
QStringList DiffEditorFactory::mimeTypes() const
|
||||
{
|
||||
return m_mimeTypes;
|
||||
}
|
||||
|
||||
///////////////////////////////// DiffEditorPlugin //////////////////////////////////
|
||||
|
||||
DiffEditorPlugin::DiffEditorPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
DiffEditorPlugin::~DiffEditorPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
void DiffEditorPlugin::initializeEditor(DiffEditorWidget *editor)
|
||||
{
|
||||
Q_UNUSED(editor)
|
||||
}
|
||||
|
||||
bool DiffEditorPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
||||
{
|
||||
Q_UNUSED(arguments)
|
||||
Q_UNUSED(errorMessage)
|
||||
|
||||
//register actions
|
||||
Core::ActionContainer *toolsContainer =
|
||||
Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
|
||||
toolsContainer->insertGroup(Core::Constants::G_TOOLS_OPTIONS, Constants::G_TOOLS_DIFF);
|
||||
|
||||
Core::Context globalcontext(Core::Constants::C_GLOBAL);
|
||||
|
||||
QAction *diffAction = new QAction(tr("Diff..."), this);
|
||||
Core::Command *diffCommand = Core::ActionManager::registerAction(diffAction,
|
||||
"DiffEditor.Diff", globalcontext);
|
||||
connect(diffAction, SIGNAL(triggered()), this, SLOT(diff()));
|
||||
toolsContainer->addAction(diffCommand, Constants::G_TOOLS_DIFF);
|
||||
|
||||
addAutoReleasedObject(new DiffEditorFactory(this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DiffEditorPlugin::extensionsInitialized()
|
||||
{
|
||||
}
|
||||
|
||||
void DiffEditorPlugin::diff()
|
||||
{
|
||||
QString fileName1 = QFileDialog::getOpenFileName(0,
|
||||
tr("Select the first file for diff"),
|
||||
QString());
|
||||
if (fileName1.isNull())
|
||||
return;
|
||||
|
||||
QString fileName2 = QFileDialog::getOpenFileName(0,
|
||||
tr("Select the second file for diff"),
|
||||
QString());
|
||||
if (fileName2.isNull())
|
||||
return;
|
||||
|
||||
|
||||
const Core::Id editorId = Constants::DIFF_EDITOR_ID;
|
||||
QString title = tr("Diff \"%1\", \"%2\"").arg(fileName1).arg(fileName2);
|
||||
Core::IEditor *outputEditor = Core::EditorManager::openEditorWithContents(editorId, &title, QString());
|
||||
Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
|
||||
|
||||
DiffEditorWidget *editorWidget = getDiffEditorWidget(outputEditor);
|
||||
if (editorWidget) {
|
||||
const QString text1 = getFileContents(fileName1, editorWidget->codec());
|
||||
const QString text2 = getFileContents(fileName2, editorWidget->codec());
|
||||
|
||||
editorWidget->setDiff(text1, text2);
|
||||
}
|
||||
}
|
||||
|
||||
DiffEditorWidget *DiffEditorPlugin::getDiffEditorWidget(const Core::IEditor *editor) const
|
||||
{
|
||||
if (const DiffEditor *de = qobject_cast<const DiffEditor *>(editor))
|
||||
return de->editorWidget();
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString DiffEditorPlugin::getFileContents(const QString &fileName, QTextCodec *codec) const
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return codec->toUnicode(file.readAll());
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
Q_EXPORT_PLUGIN(DiffEditorPlugin)
|
||||
158
src/plugins/diffeditor/diffeditorplugin.h
Normal file
158
src/plugins/diffeditor/diffeditorplugin.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 DIFFEDITORPLUGIN_H
|
||||
#define DIFFEDITORPLUGIN_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 DIFFEditor {
|
||||
class DiffEditorWidget;
|
||||
|
||||
namespace Internal {
|
||||
class DiffFile;
|
||||
|
||||
class DiffEditor : public Core::IEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DiffEditor(DiffEditorWidget *editorWidget);
|
||||
virtual ~DiffEditor();
|
||||
|
||||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "DiffEditor.json")
|
||||
|
||||
public:
|
||||
DiffEditorPlugin();
|
||||
~DiffEditorPlugin();
|
||||
|
||||
bool initialize(const QStringList &arguments, QString *errorMessage = 0);
|
||||
void extensionsInitialized();
|
||||
|
||||
// Connect editor to settings changed signals.
|
||||
void initializeEditor(DiffEditorWidget *editor);
|
||||
|
||||
private slots:
|
||||
void diff();
|
||||
|
||||
private:
|
||||
DiffEditorWidget *getDiffEditorWidget(const Core::IEditor *editor) const;
|
||||
QString getFileContents(const QString &fileName, QTextCodec *codec) const;
|
||||
|
||||
};
|
||||
|
||||
class DiffEditorFactory : public Core::IEditorFactory
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DiffEditorFactory(DiffEditorPlugin *owner);
|
||||
|
||||
QStringList mimeTypes() const;
|
||||
Core::IEditor *createEditor(QWidget *parent);
|
||||
Core::Id id() const;
|
||||
QString displayName() const;
|
||||
|
||||
private:
|
||||
const QStringList m_mimeTypes;
|
||||
DiffEditorPlugin *m_owner;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace DIFFEditor
|
||||
|
||||
#endif // DIFFEDITORPLUGIN_H
|
||||
1027
src/plugins/diffeditor/diffeditorwidget.cpp
Normal file
1027
src/plugins/diffeditor/diffeditorwidget.cpp
Normal file
File diff suppressed because it is too large
Load Diff
159
src/plugins/diffeditor/diffeditorwidget.h
Normal file
159
src/plugins/diffeditor/diffeditorwidget.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 DIFFEDITORWIDGET_H
|
||||
#define DIFFEDITORWIDGET_H
|
||||
|
||||
#include "diffeditor_global.h"
|
||||
#include "differ.h"
|
||||
|
||||
#include <QTextEdit>
|
||||
|
||||
namespace TextEditor {
|
||||
class BaseTextEditorWidget;
|
||||
class SnippetEditorWidget;
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPlainTextEdit;
|
||||
class QSplitter;
|
||||
class QSyntaxHighlighter;
|
||||
class QTextCharFormat;
|
||||
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) {}
|
||||
RowData(const QString &txt)
|
||||
: text(txt), equal(true) {}
|
||||
TextLineData leftLine;
|
||||
TextLineData rightLine;
|
||||
QString text; // file of context description
|
||||
bool equal; // true if left and right lines are equal, taking whitespaces into account (or both invalid)
|
||||
};
|
||||
|
||||
struct ChunkData {
|
||||
ChunkData() : skippedLinesBefore(0), leftOffset(0), rightOffset(0) {}
|
||||
QList<RowData> rows;
|
||||
int skippedLinesBefore; // info for text
|
||||
int leftOffset;
|
||||
int rightOffset;
|
||||
// <absolute position in the file, absolute position in the file>
|
||||
QMap<int, int> changedLeftPositions; // counting from the beginning of the chunk
|
||||
QMap<int, int> changedRightPositions; // counting from the beginning of the chunk
|
||||
QString text;
|
||||
};
|
||||
|
||||
struct FileData {
|
||||
FileData() {}
|
||||
FileData(const ChunkData &chunkData) { chunks.append(chunkData); }
|
||||
QList<ChunkData> chunks;
|
||||
QList<int> chunkOffset;
|
||||
QString text;
|
||||
};
|
||||
|
||||
struct DiffData {
|
||||
QList<FileData> files;
|
||||
QString text;
|
||||
};
|
||||
|
||||
class DIFFEDITOR_EXPORT DiffEditorWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiffEditorWidget(QWidget *parent = 0);
|
||||
~DiffEditorWidget();
|
||||
|
||||
void setDiff(const QString &leftText, const QString &rightText);
|
||||
void setDiff(const QList<Diff> &diffList);
|
||||
QTextCodec *codec() const;
|
||||
|
||||
public slots:
|
||||
void setContextLinesNumber(int lines);
|
||||
void setIgnoreWhitespaces(bool ignore);
|
||||
|
||||
protected:
|
||||
TextEditor::SnippetEditorWidget *leftEditor() const;
|
||||
TextEditor::SnippetEditorWidget *rightEditor() const;
|
||||
|
||||
private slots:
|
||||
void leftSliderChanged();
|
||||
void rightSliderChanged();
|
||||
|
||||
private:
|
||||
bool isWhitespace(const QChar &c) const;
|
||||
bool isWhitespace(const Diff &diff) const;
|
||||
bool isEqual(const QList<Diff> &diffList, int diffNumber) const;
|
||||
QList<QTextEdit::ExtraSelection> colorPositions(const QTextCharFormat &format,
|
||||
QTextCursor &cursor,
|
||||
const QMap<int, int> &positions) const;
|
||||
void colorDiff(const FileData &fileData);
|
||||
ChunkData calculateOriginalData(const QList<Diff> &diffList) const;
|
||||
FileData calculateContextData(const ChunkData &originalData) const;
|
||||
void showDiff();
|
||||
|
||||
DiffViewEditorWidget *m_leftEditor;
|
||||
DiffViewEditorWidget *m_rightEditor;
|
||||
QSplitter *m_splitter;
|
||||
|
||||
QList<Diff> m_diffList;
|
||||
int m_contextLinesNumber;
|
||||
bool m_ignoreWhitespaces;
|
||||
|
||||
ChunkData m_originalChunkData;
|
||||
FileData m_contextFileData;
|
||||
int m_leftSafePosHack;
|
||||
int m_rightSafePosHack;
|
||||
};
|
||||
|
||||
} // namespace DIFFEditor
|
||||
|
||||
#endif // DIFFEDITORWIDGET_H
|
||||
499
src/plugins/diffeditor/differ.cpp
Normal file
499
src/plugins/diffeditor/differ.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "differ.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
#include <QMap>
|
||||
|
||||
namespace DIFFEditor {
|
||||
|
||||
Diff::Diff() :
|
||||
command(Diff::Equal)
|
||||
{
|
||||
}
|
||||
|
||||
Diff::Diff(Command com, const QString &txt) :
|
||||
command(com),
|
||||
text(txt)
|
||||
{
|
||||
}
|
||||
|
||||
Differ::Differ()
|
||||
: m_diffMode(Differ::LineMode),
|
||||
m_currentDiffMode(Differ::LineMode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QList<Diff> Differ::diff(const QString &text1, const QString &text2)
|
||||
{
|
||||
m_currentDiffMode = m_diffMode;
|
||||
return merge(preprocess1AndDiff(text1, text2));
|
||||
}
|
||||
|
||||
void Differ::setDiffMode(Differ::DiffMode mode)
|
||||
{
|
||||
m_diffMode = mode;
|
||||
}
|
||||
|
||||
bool Differ::diffMode() const
|
||||
{
|
||||
return m_diffMode;
|
||||
}
|
||||
|
||||
QList<Diff> Differ::preprocess1AndDiff(const QString &text1, const QString &text2)
|
||||
{
|
||||
if (text1.isNull() && text2.isNull())
|
||||
return QList<Diff>();
|
||||
|
||||
if (text1 == text2) {
|
||||
QList<Diff> diffList;
|
||||
if (!text1.isEmpty())
|
||||
diffList.append(Diff(Diff::Equal, text1));
|
||||
return diffList;
|
||||
}
|
||||
|
||||
QString newText1 = text1;
|
||||
QString newText2 = text2;
|
||||
QString prefix;
|
||||
QString suffix;
|
||||
const int prefixCount = commonPrefix(text1, text2);
|
||||
if (prefixCount) {
|
||||
prefix = text1.left(prefixCount);
|
||||
newText1 = text1.mid(prefixCount);
|
||||
newText2 = text2.mid(prefixCount);
|
||||
}
|
||||
const int suffixCount = commonSuffix(newText1, newText2);
|
||||
if (suffixCount) {
|
||||
suffix = newText1.right(suffixCount);
|
||||
newText1 = newText1.left(newText1.count() - suffixCount);
|
||||
newText2 = newText2.left(newText2.count() - suffixCount);
|
||||
}
|
||||
QList<Diff> diffList = preprocess2AndDiff(newText1, newText2);
|
||||
if (prefixCount)
|
||||
diffList.prepend(Diff(Diff::Equal, prefix));
|
||||
if (suffixCount)
|
||||
diffList.append(Diff(Diff::Equal, suffix));
|
||||
return diffList;
|
||||
}
|
||||
|
||||
QList<Diff> Differ::preprocess2AndDiff(const QString &text1, const QString &text2)
|
||||
{
|
||||
QList<Diff> diffList;
|
||||
|
||||
if (text1.isEmpty()) {
|
||||
diffList.append(Diff(Diff::Insert, text2));
|
||||
return diffList;
|
||||
}
|
||||
|
||||
if (text2.isEmpty()) {
|
||||
diffList.append(Diff(Diff::Delete, text1));
|
||||
return diffList;
|
||||
}
|
||||
|
||||
if (text1.count() != text2.count())
|
||||
{
|
||||
const QString longtext = text1.count() > text2.count() ? text1 : text2;
|
||||
const QString shorttext = text1.count() > text2.count() ? text2 : text1;
|
||||
const int i = longtext.indexOf(shorttext);
|
||||
if (i != -1) {
|
||||
const Diff::Command command = (text1.count() > text2.count()) ? Diff::Delete : Diff::Insert;
|
||||
diffList.append(Diff(command, longtext.left(i)));
|
||||
diffList.append(Diff(Diff::Equal, shorttext));
|
||||
diffList.append(Diff(command, longtext.mid(i + shorttext.count())));
|
||||
return diffList;
|
||||
}
|
||||
|
||||
if (shorttext.count() == 1) {
|
||||
diffList.append(Diff(Diff::Delete, text1));
|
||||
diffList.append(Diff(Diff::Insert, text2));
|
||||
return diffList;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_currentDiffMode != Differ::CharMode && text1.count() > 80 && text2.count() > 80)
|
||||
return diffNonCharMode(text1, text2);
|
||||
|
||||
return diffMyers(text1, text2);
|
||||
}
|
||||
|
||||
QList<Diff> Differ::diffMyers(const QString &text1, const QString &text2)
|
||||
{
|
||||
const int n = text1.count();
|
||||
const int m = text2.count();
|
||||
const bool odd = (n + m) % 2;
|
||||
const int D = odd ? (n + m) / 2 + 1 : (n + m) / 2;
|
||||
const int delta = n - m;
|
||||
const int vShift = D;
|
||||
int *forwardV = new int[2 * D + 1]; // free me
|
||||
int *reverseV = new int[2 * D + 1]; // free me
|
||||
for (int i = 0; i <= 2 * D; i++) {
|
||||
forwardV[i] = -1;
|
||||
reverseV[i] = -1;
|
||||
}
|
||||
forwardV[vShift + 1] = 0;
|
||||
reverseV[vShift + 1] = 0;
|
||||
int kMinForward = -D;
|
||||
int kMaxForward = D;
|
||||
int kMinReverse = -D;
|
||||
int kMaxReverse = D;
|
||||
for (int d = 0; d <= D; d++) {
|
||||
// going forward
|
||||
for (int k = qMax(-d, kMinForward + qAbs(d + kMinForward) % 2);
|
||||
k <= qMin(d, kMaxForward - qAbs(d + kMaxForward) % 2);
|
||||
k = k + 2) {
|
||||
int x;
|
||||
if (k == -d || (k < d && forwardV[k + vShift - 1] < forwardV[k + vShift + 1])) {
|
||||
x = forwardV[k + vShift + 1]; // copy vertically from diagonal k + 1, y increases, y may exceed the graph
|
||||
} else {
|
||||
x = forwardV[k + vShift - 1] + 1; // copy horizontally from diagonal k - 1, x increases, x may exceed the graph
|
||||
}
|
||||
int y = x - k;
|
||||
|
||||
if (x > n) {
|
||||
kMaxForward = k - 1; // we are beyond the graph (right border), don't check diagonals >= current k anymore
|
||||
} else if (y > m) {
|
||||
kMinForward = k + 1; // we are beyond the graph (bottom border), don't check diagonals <= current k anymore
|
||||
} else {
|
||||
// find snake
|
||||
while (x < n && y < m) {
|
||||
if (text1.at(x) != text2.at(y))
|
||||
break;
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
forwardV[k + vShift] = x;
|
||||
if (odd) { // check if overlap
|
||||
if (k >= delta - (d - 1) && k <= delta + (d - 1)) {
|
||||
if (n - reverseV[delta - k + vShift] <= x) {
|
||||
delete [] forwardV;
|
||||
delete [] reverseV;
|
||||
return diffMyersSplit(text1, x, text2, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// in reverse direction
|
||||
for (int k = qMax(-d, kMinReverse + qAbs(d + kMinReverse) % 2);
|
||||
k <= qMin(d, kMaxReverse - qAbs(d + kMaxReverse) % 2);
|
||||
k = k + 2) {
|
||||
int x;
|
||||
if (k == -d || (k < d && reverseV[k + vShift - 1] < reverseV[k + vShift + 1])) {
|
||||
x = reverseV[k + vShift + 1];
|
||||
} else {
|
||||
x = reverseV[k + vShift - 1] + 1;
|
||||
}
|
||||
int y = x - k;
|
||||
|
||||
if (x > n) {
|
||||
kMaxReverse = k - 1; // we are beyond the graph (right border), don't check diagonals >= current k anymore
|
||||
} else if (y > m) {
|
||||
kMinReverse = k + 1; // we are beyond the graph (bottom border), don't check diagonals <= current k anymore
|
||||
} else {
|
||||
// find snake
|
||||
while (x < n && y < m) {
|
||||
if (text1.at(n - x - 1) != text2.at(m - y - 1))
|
||||
break;
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
reverseV[k + vShift] = x;
|
||||
if (!odd) { // check if overlap
|
||||
if (k >= delta - d && k <= delta + d) {
|
||||
if (n - forwardV[delta - k + vShift] <= x) {
|
||||
delete [] forwardV;
|
||||
delete [] reverseV;
|
||||
return diffMyersSplit(text1, n - x, text2, m - x + k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete [] forwardV;
|
||||
delete [] reverseV;
|
||||
|
||||
// Completely different
|
||||
QList<Diff> diffList;
|
||||
diffList.append(Diff(Diff::Delete, text1));
|
||||
diffList.append(Diff(Diff::Insert, text2));
|
||||
return diffList;
|
||||
}
|
||||
|
||||
QList<Diff> Differ::diffMyersSplit(
|
||||
const QString &text1, int x,
|
||||
const QString &text2, int y)
|
||||
{
|
||||
const QString text11 = text1.left(x);
|
||||
const QString text12 = text1.mid(x);
|
||||
const QString text21 = text2.left(y);
|
||||
const QString text22 = text2.mid(y);
|
||||
|
||||
QList<Diff> diffList1 = preprocess1AndDiff(text11, text21);
|
||||
QList<Diff> diffList2 = preprocess1AndDiff(text12, text22);
|
||||
return diffList1 + diffList2;
|
||||
}
|
||||
|
||||
QList<Diff> Differ::diffNonCharMode(const QString text1, const QString text2)
|
||||
{
|
||||
QString encodedText1;
|
||||
QString encodedText2;
|
||||
QStringList subtexts = encode(text1, text2, &encodedText1, &encodedText2);
|
||||
|
||||
DiffMode diffMode = m_currentDiffMode;
|
||||
m_currentDiffMode = CharMode;
|
||||
|
||||
// Each different subtext is a separate symbol
|
||||
// process these symbols as text with bigger alphabet
|
||||
QList<Diff> diffList = preprocess1AndDiff(encodedText1, encodedText2);
|
||||
|
||||
diffList = decode(diffList, subtexts);
|
||||
|
||||
QString lastDelete;
|
||||
QString lastInsert;
|
||||
QList<Diff> newDiffList;
|
||||
for (int i = 0; i <= diffList.count(); i++) {
|
||||
const Diff diffItem = i < diffList.count()
|
||||
? diffList.at(i)
|
||||
: Diff(Diff::Equal, QLatin1String("")); // dummy, ensure we process to the end even when diffList doesn't end with equality
|
||||
if (diffItem.command == Diff::Delete) {
|
||||
lastDelete += diffItem.text;
|
||||
} else if (diffItem.command == Diff::Insert) {
|
||||
lastInsert += diffItem.text;
|
||||
} else { // Diff::Equal
|
||||
if (lastDelete.count() || lastInsert.count()) {
|
||||
// Rediff here on char basis
|
||||
newDiffList += preprocess1AndDiff(lastDelete, lastInsert);
|
||||
|
||||
lastDelete = QString();
|
||||
lastInsert = QString();
|
||||
}
|
||||
newDiffList.append(diffItem);
|
||||
}
|
||||
}
|
||||
|
||||
m_currentDiffMode = diffMode;
|
||||
return newDiffList;
|
||||
}
|
||||
|
||||
QStringList Differ::encode(const QString &text1,
|
||||
const QString &text2,
|
||||
QString *encodedText1,
|
||||
QString *encodedText2)
|
||||
{
|
||||
QStringList lines;
|
||||
lines.append(QLatin1String("")); // don't use code: 0
|
||||
QMap<QString, int> lineToCode;
|
||||
|
||||
*encodedText1 = encode(text1, &lines, &lineToCode);
|
||||
*encodedText2 = encode(text2, &lines, &lineToCode);
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
int Differ::findSubtextEnd(const QString &text,
|
||||
int subtextStart)
|
||||
{
|
||||
if (m_currentDiffMode == Differ::LineMode) {
|
||||
int subtextEnd = text.indexOf(QLatin1Char('\n'), subtextStart);
|
||||
if (subtextEnd == -1) {
|
||||
subtextEnd = text.count() - 1;
|
||||
}
|
||||
return ++subtextEnd;
|
||||
} else if (m_currentDiffMode == Differ::WordMode) {
|
||||
if (!text.at(subtextStart).isLetter())
|
||||
return subtextStart + 1;
|
||||
int i = subtextStart + 1;
|
||||
|
||||
const int count = text.count();
|
||||
while (i < count && text.at(i).isLetter())
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
return subtextStart + 1; // CharMode
|
||||
}
|
||||
|
||||
QString Differ::encode(const QString &text,
|
||||
QStringList *lines,
|
||||
QMap<QString, int> *lineToCode)
|
||||
{
|
||||
int subtextStart = 0;
|
||||
int subtextEnd = -1;
|
||||
QString codes;
|
||||
while (subtextEnd < text.count()) {
|
||||
subtextEnd = findSubtextEnd(text, subtextStart);
|
||||
const QString line = text.mid(subtextStart, subtextEnd - subtextStart);
|
||||
subtextStart = subtextEnd;
|
||||
|
||||
if (lineToCode->contains(line)) {
|
||||
int code = lineToCode->value(line);
|
||||
codes += QChar(static_cast<ushort>(code));
|
||||
} else {
|
||||
lines->append(line);
|
||||
lineToCode->insert(line, lines->count() - 1);
|
||||
codes += QChar(static_cast<ushort>(lines->count() - 1));
|
||||
}
|
||||
}
|
||||
return codes;
|
||||
}
|
||||
|
||||
QList<Diff> Differ::decode(const QList<Diff> &diffList,
|
||||
const QStringList &lines)
|
||||
{
|
||||
QList<Diff> newDiffList;
|
||||
for (int i = 0; i < diffList.count(); i++) {
|
||||
Diff diff = diffList.at(i);
|
||||
QString text;
|
||||
for (int j = 0; j < diff.text.count(); j++) {
|
||||
const int idx = static_cast<ushort>(diff.text.at(j).unicode());
|
||||
text += lines.value(idx);
|
||||
}
|
||||
diff.text = text;
|
||||
newDiffList.append(diff);
|
||||
}
|
||||
return newDiffList;
|
||||
}
|
||||
|
||||
QList<Diff> Differ::merge(const QList<Diff> &diffList)
|
||||
{
|
||||
QString lastDelete;
|
||||
QString lastInsert;
|
||||
QList<Diff> newDiffList;
|
||||
for (int i = 0; i <= diffList.count(); i++) {
|
||||
const Diff diff = i < diffList.count()
|
||||
? diffList.at(i)
|
||||
: Diff(Diff::Equal, QString()); // dummy, ensure we process to the end even when diffList doesn't end with equality
|
||||
if (diff.command == Diff::Delete) {
|
||||
lastDelete += diff.text;
|
||||
} else if (diff.command == Diff::Insert) {
|
||||
lastInsert += diff.text;
|
||||
} else { // Diff::Equal
|
||||
if (lastDelete.count() || lastInsert.count()) {
|
||||
// common prefix and suffix?
|
||||
|
||||
if (lastDelete.count())
|
||||
newDiffList.append(Diff(Diff::Delete, lastDelete));
|
||||
if (lastInsert.count())
|
||||
newDiffList.append(Diff(Diff::Insert, lastInsert));
|
||||
if (diff.text.count())
|
||||
newDiffList.append(diff);
|
||||
lastDelete = QString();
|
||||
lastInsert = QString();
|
||||
} else { // join with last equal diff
|
||||
if (newDiffList.count()
|
||||
&& newDiffList.last().command == Diff::Equal) {
|
||||
newDiffList.last().text += diff.text;
|
||||
} else {
|
||||
if (diff.text.count())
|
||||
newDiffList.append(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<Diff> squashedDiffList = squashEqualities(newDiffList);
|
||||
if (squashedDiffList.count() != newDiffList.count())
|
||||
return merge(squashedDiffList);
|
||||
|
||||
return squashedDiffList;
|
||||
}
|
||||
|
||||
QList<Diff> Differ::squashEqualities(const QList<Diff> &diffList)
|
||||
{
|
||||
if (diffList.count() <= 3) // we need at least 3 items
|
||||
return diffList;
|
||||
QList<Diff> squashedDiffList;
|
||||
Diff prevDiff = diffList.at(0);
|
||||
Diff thisDiff = diffList.at(1);
|
||||
Diff nextDiff = diffList.at(2);
|
||||
int i = 2;
|
||||
while (i < diffList.count()) {
|
||||
if (prevDiff.command == Diff::Equal
|
||||
&& nextDiff.command == Diff::Equal) {
|
||||
if (thisDiff.text.endsWith(prevDiff.text)) {
|
||||
thisDiff.text = prevDiff.text
|
||||
+ thisDiff.text.left(thisDiff.text.count()
|
||||
- prevDiff.text.count());
|
||||
nextDiff.text = prevDiff.text + nextDiff.text;
|
||||
} else if (thisDiff.text.startsWith(nextDiff.text)) {
|
||||
prevDiff.text += nextDiff.text;
|
||||
thisDiff.text = thisDiff.text.mid(nextDiff.text.count())
|
||||
+ nextDiff.text;
|
||||
i++;
|
||||
if (i < diffList.count())
|
||||
nextDiff = diffList.at(i);
|
||||
squashedDiffList.append(prevDiff);
|
||||
} else {
|
||||
squashedDiffList.append(prevDiff);
|
||||
}
|
||||
} else {
|
||||
squashedDiffList.append(prevDiff);
|
||||
}
|
||||
prevDiff = thisDiff;
|
||||
thisDiff = nextDiff;
|
||||
i++;
|
||||
if (i < diffList.count())
|
||||
nextDiff = diffList.at(i);
|
||||
}
|
||||
squashedDiffList.append(prevDiff);
|
||||
if (i == diffList.count())
|
||||
squashedDiffList.append(thisDiff);
|
||||
return squashedDiffList;
|
||||
}
|
||||
|
||||
int Differ::commonPrefix(const QString &text1, const QString &text2) const
|
||||
{
|
||||
int i = 0;
|
||||
const int minCount = qMin(text1.count(), text2.count());
|
||||
while (i < minCount) {
|
||||
if (text1.at(i) != text2.at(i))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int Differ::commonSuffix(const QString &text1, const QString &text2) const
|
||||
{
|
||||
int i = 0;
|
||||
const int text1Count = text1.count();
|
||||
const int text2Count = text2.count();
|
||||
const int minCount = qMin(text1.count(), text2.count());
|
||||
while (i < minCount) {
|
||||
if (text1.at(text1Count - i - 1) != text2.at(text2Count - i - 1))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
} // namespace DIFFEditor
|
||||
98
src/plugins/diffeditor/differ.h
Normal file
98
src/plugins/diffeditor/differ.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 DIFFER_H
|
||||
#define DIFFER_H
|
||||
|
||||
#include "diffeditor_global.h"
|
||||
#include <QString>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
template <class K, class T>
|
||||
class QMap;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace DIFFEditor {
|
||||
|
||||
class DIFFEDITOR_EXPORT Diff
|
||||
{
|
||||
public:
|
||||
enum Command {
|
||||
Delete,
|
||||
Insert,
|
||||
Equal
|
||||
};
|
||||
Command command;
|
||||
QString text;
|
||||
Diff(Command com, const QString &txt);
|
||||
Diff();
|
||||
};
|
||||
|
||||
class DIFFEDITOR_EXPORT Differ
|
||||
{
|
||||
public:
|
||||
enum DiffMode
|
||||
{
|
||||
CharMode,
|
||||
WordMode,
|
||||
LineMode
|
||||
};
|
||||
Differ();
|
||||
QList<Diff> diff(const QString &text1, const QString &text2);
|
||||
void setDiffMode(DiffMode mode);
|
||||
bool diffMode() const;
|
||||
private:
|
||||
QList<Diff> preprocess1AndDiff(const QString &text1, const QString &text2);
|
||||
QList<Diff> preprocess2AndDiff(const QString &text1, const QString &text2);
|
||||
QList<Diff> diffMyers(const QString &text1, const QString &text2);
|
||||
QList<Diff> diffMyersSplit(const QString &text1, int x,
|
||||
const QString &text2, int y);
|
||||
QList<Diff> merge(const QList<Diff> &diffList);
|
||||
QList<Diff> squashEqualities(const QList<Diff> &diffList);
|
||||
QList<Diff> diffNonCharMode(const QString text1, const QString text2);
|
||||
QStringList encode(const QString &text1,
|
||||
const QString &text2,
|
||||
QString *encodedText1,
|
||||
QString *encodedText2);
|
||||
QString encode(const QString &text,
|
||||
QStringList *lines,
|
||||
QMap<QString, int> *lineToCode);
|
||||
QList<Diff> decode(const QList<Diff> &diffList,
|
||||
const QStringList &lines);
|
||||
int findSubtextEnd(const QString &text,
|
||||
int subTextStart);
|
||||
int commonPrefix(const QString &text1, const QString &text2) const;
|
||||
int commonSuffix(const QString &text1, const QString &text2) const;
|
||||
DiffMode m_diffMode;
|
||||
DiffMode m_currentDiffMode;
|
||||
};
|
||||
|
||||
} // namespace DIFFEditor
|
||||
|
||||
#endif // DIFF_H
|
||||
Reference in New Issue
Block a user