Implement unified diff editor

Change-Id: I93e0bfd71a8a650afbe2ca9e0f1f3dbfc9d57db0
Reviewed-by: Jarek Kobus <jaroslaw.kobus@digia.com>
This commit is contained in:
jkobus
2014-02-13 16:43:28 +01:00
committed by Jarek Kobus
parent 8cb25f9e3e
commit 8cad94534f
39 changed files with 4075 additions and 1441 deletions

View File

@@ -30,8 +30,11 @@
#include "diffeditorplugin.h"
#include "diffeditor.h"
#include "diffeditorconstants.h"
#include "diffeditordocument.h"
#include "diffeditorfactory.h"
#include "diffeditormanager.h"
#include "diffeditorreloader.h"
#include "differ.h"
#include <QFileDialog>
#include <QTextCodec>
@@ -46,6 +49,87 @@
namespace DiffEditor {
namespace Internal {
class SimpleDiffEditorReloader : public DiffEditorReloader
{
Q_OBJECT
public:
SimpleDiffEditorReloader(QObject *parent,
const QString &leftFileName,
const QString &rightFileName);
protected:
void reload();
private:
QString getFileContents(const QString &fileName) const;
QString m_leftFileName;
QString m_rightFileName;
};
SimpleDiffEditorReloader::SimpleDiffEditorReloader(QObject *parent,
const QString &leftFileName,
const QString &rightFileName)
: DiffEditorReloader(parent),
m_leftFileName(leftFileName),
m_rightFileName(rightFileName)
{
}
void SimpleDiffEditorReloader::reload()
{
const QString leftText = getFileContents(m_leftFileName);
const QString rightText = getFileContents(m_rightFileName);
Differ differ;
QList<Diff> diffList = differ.cleanupSemantics(
differ.diff(leftText, rightText));
QList<Diff> leftDiffList;
QList<Diff> rightDiffList;
Differ::splitDiffList(diffList, &leftDiffList, &rightDiffList);
QList<Diff> outputLeftDiffList;
QList<Diff> outputRightDiffList;
if (diffEditorController()->isIgnoreWhitespace()) {
const QList<Diff> leftIntermediate =
Differ::moveWhitespaceIntoEqualities(leftDiffList);
const QList<Diff> rightIntermediate =
Differ::moveWhitespaceIntoEqualities(rightDiffList);
Differ::ignoreWhitespaceBetweenEqualities(leftIntermediate,
rightIntermediate,
&outputLeftDiffList,
&outputRightDiffList);
} else {
outputLeftDiffList = leftDiffList;
outputRightDiffList = rightDiffList;
}
const ChunkData chunkData = DiffUtils::calculateOriginalData(
outputLeftDiffList, outputRightDiffList);
FileData fileData = DiffUtils::calculateContextData(
chunkData, diffEditorController()->contextLinesNumber(), 0);
fileData.leftFileInfo.fileName = m_leftFileName;
fileData.rightFileInfo.fileName = m_rightFileName;
QList<FileData> fileDataList;
fileDataList << fileData;
diffEditorController()->setDiffFiles(fileDataList);
reloadFinished();
}
QString SimpleDiffEditorReloader::getFileContents(const QString &fileName) const
{
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
return Core::EditorManager::defaultTextCodec()->toUnicode(file.readAll());
return QString();
}
/////////////////
DiffEditorPlugin::DiffEditorPlugin()
{
}
@@ -62,7 +146,8 @@ bool DiffEditorPlugin::initialize(const QStringList &arguments, QString *errorMe
//register actions
Core::ActionContainer *toolsContainer =
Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
toolsContainer->insertGroup(Core::Constants::G_TOOLS_OPTIONS, Constants::G_TOOLS_DIFF);
toolsContainer->insertGroup(Core::Constants::G_TOOLS_OPTIONS,
Constants::G_TOOLS_DIFF);
Core::Context globalcontext(Core::Constants::C_GLOBAL);
@@ -98,39 +183,396 @@ void DiffEditorPlugin::diff()
return;
const Core::Id editorId = Constants::DIFF_EDITOR_ID;
//: Editor title
QString title = tr("Diff \"%1\", \"%2\"").arg(fileName1).arg(fileName2);
DiffEditor *editor = qobject_cast<DiffEditor *>
(Core::EditorManager::openEditorWithContents(editorId, &title, QByteArray(),
(Core::EditorManager::OpenInOtherSplit
| Core::EditorManager::NoNewSplits)));
if (!editor)
return;
const QString documentId = QLatin1String("Diff ") + fileName1
+ QLatin1String(", ") + fileName2;
DiffEditorDocument *document = DiffEditorManager::find(documentId);
if (!document) {
QString title = tr("Diff \"%1\", \"%2\"").arg(fileName1).arg(fileName2);
document = DiffEditorManager::findOrCreate(documentId, title);
if (!document)
return;
const QString text1 = getFileContents(fileName1);
const QString text2 = getFileContents(fileName2);
DiffEditorController *controller = document->controller();
SimpleDiffEditorReloader *reloader =
new SimpleDiffEditorReloader(controller, fileName1, fileName2);
reloader->setDiffEditorController(controller);
}
DiffEditorController::DiffFilesContents dfc;
dfc.leftFileInfo = fileName1;
dfc.leftText = text1;
dfc.rightFileInfo = fileName2;
dfc.rightText = text2;
QList<DiffEditorController::DiffFilesContents> list;
list.append(dfc);
Core::EditorManager::activateEditorForDocument(document);
editor->controller()->setDiffContents(list);
}
QString DiffEditorPlugin::getFileContents(const QString &fileName) const
{
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
return Core::EditorManager::defaultTextCodec()->toUnicode(file.readAll());
return QString();
document->controller()->requestReload();
}
} // namespace Internal
} // namespace DiffEditor
#ifdef WITH_TESTS
#include <QTest>
#include "diffutils.h"
Q_DECLARE_METATYPE(DiffEditor::ChunkData)
Q_DECLARE_METATYPE(QList<DiffEditor::FileData>)
void DiffEditor::Internal::DiffEditorPlugin::testMakePatch_data()
{
QTest::addColumn<ChunkData>("sourceChunk");
QTest::addColumn<QString>("leftFileName");
QTest::addColumn<QString>("rightFileName");
QTest::addColumn<bool>("lastChunk");
QTest::addColumn<QString>("patchText");
const QString fileName = QLatin1String("a.txt");
const QString header = QLatin1String("--- ") + fileName
+ QLatin1String("\n+++ ") + fileName + QLatin1String("\n");
QList<RowData> rows;
rows << RowData(TextLineData(QLatin1String("ABCD")),
TextLineData(TextLineData::Separator));
rows << RowData(TextLineData(QLatin1String("EFGH")));
rows << RowData(TextLineData(QLatin1String("")));
ChunkData chunk;
chunk.rows = rows;
QString patchText = header + QLatin1String("@@ -1,2 +1,1 @@\n"
"-ABCD\n"
" EFGH\n");
QTest::newRow("Simple") << chunk
<< fileName
<< fileName
<< true
<< patchText;
///////////
rows.clear();
rows << RowData(TextLineData(QLatin1String("ABCD")),
TextLineData(QLatin1String("ABCD")));
rows << RowData(TextLineData(QLatin1String("")),
TextLineData(TextLineData::Separator));
chunk.rows = rows;
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
"-ABCD\n"
"+ABCD\n"
"\\ No newline at end of file\n");
QTest::newRow("Last newline removed") << chunk
<< fileName
<< fileName
<< true
<< patchText;
///////////
// chunk the same here
patchText = header + QLatin1String("@@ -1,2 +1,1 @@\n"
"-ABCD\n"
"-\n"
"+ABCD\n");
QTest::newRow("Not a last newline removed") << chunk
<< fileName
<< fileName
<< false
<< patchText;
///////////
rows.clear();
rows << RowData(TextLineData(QLatin1String("ABCD")),
TextLineData(QLatin1String("ABCD")));
rows << RowData(TextLineData(TextLineData::Separator),
TextLineData(QLatin1String("")));
chunk.rows = rows;
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
"-ABCD\n"
"\\ No newline at end of file\n"
"+ABCD\n");
QTest::newRow("Last newline added") << chunk
<< fileName
<< fileName
<< true
<< patchText;
///////////
// chunk the same here
patchText = header + QLatin1String("@@ -1,1 +1,2 @@\n"
"-ABCD\n"
"+ABCD\n"
"+\n");
QTest::newRow("Not a last newline added") << chunk
<< fileName
<< fileName
<< false
<< patchText;
///////////
rows.clear();
rows << RowData(TextLineData(QLatin1String("ABCD")),
TextLineData(QLatin1String("EFGH")));
rows << RowData(TextLineData(QLatin1String("")));
chunk.rows = rows;
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
"-ABCD\n"
"+EFGH\n");
QTest::newRow("Last line with a newline modified") << chunk
<< fileName
<< fileName
<< true
<< patchText;
///////////
// chunk the same here
patchText = header + QLatin1String("@@ -1,2 +1,2 @@\n"
"-ABCD\n"
"+EFGH\n"
" \n");
QTest::newRow("Not a last line with a newline modified") << chunk
<< fileName
<< fileName
<< false
<< patchText;
///////////
rows.clear();
rows << RowData(TextLineData(QLatin1String("ABCD")),
TextLineData(QLatin1String("EFGH")));
chunk.rows = rows;
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
"-ABCD\n"
"\\ No newline at end of file\n"
"+EFGH\n"
"\\ No newline at end of file\n");
QTest::newRow("Last line without a newline modified") << chunk
<< fileName
<< fileName
<< true
<< patchText;
///////////
// chunk the same here
patchText = header + QLatin1String("@@ -1,1 +1,1 @@\n"
"-ABCD\n"
"+EFGH\n");
QTest::newRow("Not a last line without a newline modified") << chunk
<< fileName
<< fileName
<< false
<< patchText;
///////////
rows.clear();
rows << RowData(TextLineData(QLatin1String("ABCD")),
TextLineData(QLatin1String("EFGH")));
rows << RowData(TextLineData(QLatin1String("IJKL")));
chunk.rows = rows;
patchText = header + QLatin1String("@@ -1,2 +1,2 @@\n"
"-ABCD\n"
"+EFGH\n"
" IJKL\n"
"\\ No newline at end of file\n");
QTest::newRow("Last but one line modified, last line without a newline")
<< chunk
<< fileName
<< fileName
<< true
<< patchText;
///////////
// chunk the same here
patchText = header + QLatin1String("@@ -1,2 +1,2 @@\n"
"-ABCD\n"
"+EFGH\n"
" IJKL\n");
QTest::newRow("Last but one line modified, last line with a newline")
<< chunk
<< fileName
<< fileName
<< false
<< patchText;
}
void DiffEditor::Internal::DiffEditorPlugin::testMakePatch()
{
QFETCH(ChunkData, sourceChunk);
QFETCH(QString, leftFileName);
QFETCH(QString, rightFileName);
QFETCH(bool, lastChunk);
QFETCH(QString, patchText);
QString result = DiffUtils::makePatch(sourceChunk, leftFileName, rightFileName, lastChunk);
QCOMPARE(patchText, result);
}
void DiffEditor::Internal::DiffEditorPlugin::testReadPatch_data()
{
QTest::addColumn<QString>("sourcePatch");
QTest::addColumn<QList<FileData> >("fileDataList");
QString patch = QLatin1String("diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp\n"
"index eab9e9b..082c135 100644\n"
"--- a/src/plugins/diffeditor/diffeditor.cpp\n"
"+++ b/src/plugins/diffeditor/diffeditor.cpp\n"
"@@ -187,9 +187,6 @@ void DiffEditor::ctor()\n"
" m_controller = m_document->controller();\n"
" m_guiController = new DiffEditorGuiController(m_controller, this);\n"
" \n"
"-// m_sideBySideEditor->setDiffEditorGuiController(m_guiController);\n"
"-// m_unifiedEditor->setDiffEditorGuiController(m_guiController);\n"
"-\n"
" connect(m_controller, SIGNAL(cleared(QString)),\n"
" this, SLOT(slotCleared(QString)));\n"
" connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),\n"
"diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp\n"
"index 2f641c9..f8ff795 100644\n"
"--- a/src/plugins/diffeditor/diffutils.cpp\n"
"+++ b/src/plugins/diffeditor/diffutils.cpp\n"
"@@ -464,5 +464,12 @@ QString DiffUtils::makePatch(const ChunkData &chunkData,\n"
" return diffText;\n"
" }\n"
" \n"
"+FileData DiffUtils::makeFileData(const QString &patch)\n"
"+{\n"
"+ FileData fileData;\n"
"+\n"
"+ return fileData;\n"
"+}\n"
"+\n"
" } // namespace Internal\n"
" } // namespace DiffEditor");
FileData fileData1;
fileData1.leftFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffeditor.cpp"),
QLatin1String("eab9e9b"));
fileData1.rightFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffeditor.cpp"),
QLatin1String("082c135"));
ChunkData chunkData1;
chunkData1.leftStartingLineNumber = 187;
chunkData1.rightStartingLineNumber = 187;
QList<RowData> rows1;
rows1.append(RowData(TextLineData(QLatin1String(" m_controller = m_document->controller();"))));
rows1.append(RowData(TextLineData(QLatin1String(" m_guiController = new DiffEditorGuiController(m_controller, this);"))));
rows1.append(RowData(TextLineData(QLatin1String(""))));
rows1.append(RowData(TextLineData(QLatin1String("// m_sideBySideEditor->setDiffEditorGuiController(m_guiController);")),
TextLineData(TextLineData::Separator)));
rows1.append(RowData(TextLineData(QLatin1String("// m_unifiedEditor->setDiffEditorGuiController(m_guiController);")),
TextLineData(TextLineData::Separator)));
rows1.append(RowData(TextLineData(QLatin1String("")),
TextLineData(TextLineData::Separator)));
rows1.append(RowData(TextLineData(QLatin1String(" connect(m_controller, SIGNAL(cleared(QString)),"))));
rows1.append(RowData(TextLineData(QLatin1String(" this, SLOT(slotCleared(QString)));"))));
rows1.append(RowData(TextLineData(QLatin1String(" connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),"))));
chunkData1.rows = rows1;
fileData1.chunks.append(chunkData1);
FileData fileData2;
fileData2.leftFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffutils.cpp"),
QLatin1String("2f641c9"));
fileData2.rightFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffutils.cpp"),
QLatin1String("f8ff795"));
ChunkData chunkData2;
chunkData2.leftStartingLineNumber = 464;
chunkData2.rightStartingLineNumber = 464;
QList<RowData> rows2;
rows2.append(RowData(TextLineData(QLatin1String(" return diffText;"))));
rows2.append(RowData(TextLineData(QLatin1String("}"))));
rows2.append(RowData(TextLineData(QLatin1String(""))));
rows2.append(RowData(TextLineData(TextLineData::Separator),
TextLineData(QLatin1String("FileData DiffUtils::makeFileData(const QString &patch)"))));
rows2.append(RowData(TextLineData(TextLineData::Separator),
TextLineData(QLatin1String("{"))));
rows2.append(RowData(TextLineData(TextLineData::Separator),
TextLineData(QLatin1String(" FileData fileData;"))));
rows2.append(RowData(TextLineData(TextLineData::Separator),
TextLineData(QLatin1String(""))));
rows2.append(RowData(TextLineData(TextLineData::Separator),
TextLineData(QLatin1String(" return fileData;"))));
rows2.append(RowData(TextLineData(TextLineData::Separator),
TextLineData(QLatin1String("}"))));
rows2.append(RowData(TextLineData(TextLineData::Separator),
TextLineData(QLatin1String(""))));
rows2.append(RowData(TextLineData(QLatin1String("} // namespace Internal"))));
rows2.append(RowData(TextLineData(QLatin1String("} // namespace DiffEditor"))));
chunkData2.rows = rows2;
fileData2.chunks.append(chunkData2);
QList<FileData> fileDataList;
fileDataList.append(fileData1);
fileDataList.append(fileData2);
QTest::newRow("Git patch") << patch
<< fileDataList;
}
void DiffEditor::Internal::DiffEditorPlugin::testReadPatch()
{
QFETCH(QString, sourcePatch);
QFETCH(QList<FileData>, fileDataList);
bool ok;
QList<FileData> result = DiffUtils::readPatch(sourcePatch, false, &ok);
QVERIFY(ok);
QCOMPARE(fileDataList.count(), result.count());
for (int i = 0; i < fileDataList.count(); i++) {
const FileData &origFileData = fileDataList.at(i);
const FileData &resultFileData = result.at(i);
QCOMPARE(origFileData.leftFileInfo.fileName,
resultFileData.leftFileInfo.fileName);
QCOMPARE(origFileData.leftFileInfo.typeInfo,
resultFileData.leftFileInfo.typeInfo);
QCOMPARE(origFileData.rightFileInfo.fileName,
resultFileData.rightFileInfo.fileName);
QCOMPARE(origFileData.rightFileInfo.typeInfo,
resultFileData.rightFileInfo.typeInfo);
QCOMPARE(origFileData.chunks.count(),
resultFileData.chunks.count());
for (int j = 0; j < origFileData.chunks.count(); j++) {
const ChunkData &origChunkData = origFileData.chunks.at(j);
const ChunkData &resultChunkData = resultFileData.chunks.at(j);
QCOMPARE(origChunkData.leftStartingLineNumber,
resultChunkData.leftStartingLineNumber);
QCOMPARE(origChunkData.rightStartingLineNumber,
resultChunkData.rightStartingLineNumber);
QCOMPARE(origChunkData.contextChunk,
resultChunkData.contextChunk);
QCOMPARE(origChunkData.rows.count(),
resultChunkData.rows.count());
for (int k = 0; k < origChunkData.rows.count(); k++) {
const RowData &origRowData = origChunkData.rows.at(k);
const RowData &resultRowData = resultChunkData.rows.at(k);
QCOMPARE(origRowData.equal,
resultRowData.equal);
QCOMPARE(origRowData.leftLine.text,
resultRowData.leftLine.text);
QCOMPARE(origRowData.leftLine.textLineType,
resultRowData.leftLine.textLineType);
QCOMPARE(origRowData.rightLine.text,
resultRowData.rightLine.text);
QCOMPARE(origRowData.rightLine.textLineType,
resultRowData.rightLine.textLineType);
}
}
}
}
#endif // WITH_TESTS
Q_EXPORT_PLUGIN(DiffEditor::Internal::DiffEditorPlugin)
#include "diffeditorplugin.moc"