Move diff calculation to the separate thread

It doesn't freeze creator anymore.

Task-number: QTCREATORBUG-14255
Change-Id: I3d06ea7a848e321c6072e00a73b96e9ea9770b6a
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Jarek Kobus
2016-11-18 16:06:27 +01:00
parent 6d1d1a4416
commit 2144fb5949
3 changed files with 176 additions and 82 deletions

View File

@@ -33,6 +33,7 @@
#include <QAction> #include <QAction>
#include <QFileDialog> #include <QFileDialog>
#include <QFutureWatcher>
#include <QMenu> #include <QMenu>
#include <QTextCodec> #include <QTextCodec>
#include <QtPlugin> #include <QtPlugin>
@@ -43,11 +44,13 @@
#include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/mapreduce.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
using namespace Core; using namespace Core;
@@ -55,48 +58,116 @@ using namespace Core;
namespace DiffEditor { namespace DiffEditor {
namespace Internal { namespace Internal {
class ReloadInput {
public:
QString leftText;
QString rightText;
DiffFileInfo leftFileInfo;
DiffFileInfo rightFileInfo;
FileData::FileOperation fileOperation = FileData::ChangeFile;
};
class DiffFile
{
public:
DiffFile(bool ignoreWhitespace, int contextLineCount)
: m_ignoreWhitespace(ignoreWhitespace),
m_contextLineCount(contextLineCount)
{}
void operator()(QFutureInterface<FileData> &futureInterface,
const ReloadInput &reloadInfo) const
{
Differ differ(&futureInterface);
const QList<Diff> diffList = differ.cleanupSemantics(
differ.diff(reloadInfo.leftText, reloadInfo.rightText));
QList<Diff> leftDiffList;
QList<Diff> rightDiffList;
Differ::splitDiffList(diffList, &leftDiffList, &rightDiffList);
QList<Diff> outputLeftDiffList;
QList<Diff> outputRightDiffList;
if (m_ignoreWhitespace) {
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, m_contextLineCount, 0);
fileData.leftFileInfo = reloadInfo.leftFileInfo;
fileData.rightFileInfo = reloadInfo.rightFileInfo;
fileData.fileOperation = reloadInfo.fileOperation;
futureInterface.reportResult(fileData);
}
private:
const bool m_ignoreWhitespace;
const int m_contextLineCount;
};
class DiffFilesController : public DiffEditorController class DiffFilesController : public DiffEditorController
{ {
Q_OBJECT Q_OBJECT
public: public:
DiffFilesController(IDocument *document); DiffFilesController(IDocument *document);
~DiffFilesController();
protected: protected:
FileData diffFiles(const QString &leftContents, const QString &rightContents); void reload() final;
virtual QList<ReloadInput> reloadInputList() const = 0;
private:
void reloaded();
void cancelReload();
QFutureWatcher<FileData> m_futureWatcher;
}; };
DiffFilesController::DiffFilesController(IDocument *document) DiffFilesController::DiffFilesController(IDocument *document)
: DiffEditorController(document) : DiffEditorController(document)
{}
FileData DiffFilesController::diffFiles(const QString &leftContents, const QString &rightContents)
{ {
Differ differ; connect(&m_futureWatcher, &QFutureWatcher<FileData>::finished,
const QList<Diff> diffList = differ.cleanupSemantics(differ.diff(leftContents, rightContents)); this, &DiffFilesController::reloaded);
}
QList<Diff> leftDiffList; DiffFilesController::~DiffFilesController()
QList<Diff> rightDiffList; {
Differ::splitDiffList(diffList, &leftDiffList, &rightDiffList); cancelReload();
QList<Diff> outputLeftDiffList; }
QList<Diff> outputRightDiffList;
if (ignoreWhitespace()) { void DiffFilesController::reload()
const QList<Diff> leftIntermediate {
= Differ::moveWhitespaceIntoEqualities(leftDiffList); cancelReload();
const QList<Diff> rightIntermediate m_futureWatcher.setFuture(Utils::map(reloadInputList(),
= Differ::moveWhitespaceIntoEqualities(rightDiffList); DiffFile(ignoreWhitespace(),
Differ::ignoreWhitespaceBetweenEqualities(leftIntermediate, rightIntermediate, contextLineCount())));
&outputLeftDiffList, &outputRightDiffList);
} else { Core::ProgressManager::addTask(m_futureWatcher.future(),
outputLeftDiffList = leftDiffList; tr("Calculating diff"), "DiffEditor");
outputRightDiffList = rightDiffList; }
void DiffFilesController::reloaded()
{
const QList<FileData> fileDataList = m_futureWatcher.future().results();
setDiffFiles(fileDataList);
reloadFinished(true);
}
void DiffFilesController::cancelReload()
{
if (m_futureWatcher.future().isRunning()) {
m_futureWatcher.future().cancel();
m_futureWatcher.setFuture(QFuture<FileData>());
} }
const ChunkData chunkData = DiffUtils::calculateOriginalData(
outputLeftDiffList, outputRightDiffList);
const FileData fileData = DiffUtils::calculateContextData(chunkData, contextLineCount(), 0);
return fileData;
} }
class DiffCurrentFileController : public DiffFilesController class DiffCurrentFileController : public DiffFilesController
@@ -106,7 +177,7 @@ public:
DiffCurrentFileController(IDocument *document, const QString &fileName); DiffCurrentFileController(IDocument *document, const QString &fileName);
protected: protected:
void reload() override; QList<ReloadInput> reloadInputList() const final;
private: private:
const QString m_fileName; const QString m_fileName;
@@ -116,9 +187,9 @@ DiffCurrentFileController::DiffCurrentFileController(IDocument *document, const
DiffFilesController(document), m_fileName(fileName) DiffFilesController(document), m_fileName(fileName)
{ } { }
void DiffCurrentFileController::reload() QList<ReloadInput> DiffCurrentFileController::reloadInputList() const
{ {
QList<FileData> fileDataList; QList<ReloadInput> result;
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>( TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(
DocumentModel::documentForFilePath(m_fileName)); DocumentModel::documentForFilePath(m_fileName));
@@ -138,21 +209,22 @@ void DiffCurrentFileController::reload()
const QString rightText = textDocument->plainText(); const QString rightText = textDocument->plainText();
FileData fileData = diffFiles(leftText, rightText); ReloadInput reloadInput;
fileData.leftFileInfo.fileName = m_fileName; reloadInput.leftText = leftText;
fileData.rightFileInfo.fileName = m_fileName; reloadInput.rightText = rightText;
fileData.leftFileInfo.typeInfo = tr("Saved"); reloadInput.leftFileInfo.fileName = m_fileName;
fileData.rightFileInfo.typeInfo = tr("Modified"); reloadInput.rightFileInfo.fileName = m_fileName;
fileData.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor; reloadInput.leftFileInfo.typeInfo = tr("Saved");
reloadInput.rightFileInfo.typeInfo = tr("Modified");
reloadInput.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor;
if (!leftFileExists) if (!leftFileExists)
fileData.fileOperation = FileData::NewFile; reloadInput.fileOperation = FileData::NewFile;
fileDataList << fileData; result << reloadInput;
} }
setDiffFiles(fileDataList); return result;
reloadFinished(true);
} }
///////////////// /////////////////
@@ -164,19 +236,18 @@ public:
DiffOpenFilesController(IDocument *document); DiffOpenFilesController(IDocument *document);
protected: protected:
void reload() override; QList<ReloadInput> reloadInputList() const final;
}; };
DiffOpenFilesController::DiffOpenFilesController(IDocument *document) : DiffOpenFilesController::DiffOpenFilesController(IDocument *document) :
DiffFilesController(document) DiffFilesController(document)
{ } { }
void DiffOpenFilesController::reload() QList<ReloadInput> DiffOpenFilesController::reloadInputList() const
{ {
const QList<IDocument *> openedDocuments = QList<ReloadInput> result;
DocumentModel::openedDocuments();
QList<FileData> fileDataList; const QList<IDocument *> openedDocuments = DocumentModel::openedDocuments();
foreach (IDocument *doc, openedDocuments) { foreach (IDocument *doc, openedDocuments) {
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(doc); TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(doc);
@@ -197,22 +268,23 @@ void DiffOpenFilesController::reload()
const QString rightText = textDocument->plainText(); const QString rightText = textDocument->plainText();
FileData fileData = diffFiles(leftText, rightText); ReloadInput reloadInput;
fileData.leftFileInfo.fileName = fileName; reloadInput.leftText = leftText;
fileData.rightFileInfo.fileName = fileName; reloadInput.rightText = rightText;
fileData.leftFileInfo.typeInfo = tr("Saved"); reloadInput.leftFileInfo.fileName = fileName;
fileData.rightFileInfo.typeInfo = tr("Modified"); reloadInput.rightFileInfo.fileName = fileName;
fileData.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor; reloadInput.leftFileInfo.typeInfo = tr("Saved");
reloadInput.rightFileInfo.typeInfo = tr("Modified");
reloadInput.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor;
if (!leftFileExists) if (!leftFileExists)
fileData.fileOperation = FileData::NewFile; reloadInput.fileOperation = FileData::NewFile;
fileDataList << fileData; result << reloadInput;
} }
} }
setDiffFiles(fileDataList); return result;
reloadFinished(true);
} }
///////////////// /////////////////
@@ -224,7 +296,7 @@ public:
DiffModifiedFilesController(IDocument *document, const QStringList &fileNames); DiffModifiedFilesController(IDocument *document, const QStringList &fileNames);
protected: protected:
void reload() override; QList<ReloadInput> reloadInputList() const final;
private: private:
const QStringList m_fileNames; const QStringList m_fileNames;
@@ -234,9 +306,9 @@ DiffModifiedFilesController::DiffModifiedFilesController(IDocument *document, co
DiffFilesController(document), m_fileNames(fileNames) DiffFilesController(document), m_fileNames(fileNames)
{ } { }
void DiffModifiedFilesController::reload() QList<ReloadInput> DiffModifiedFilesController::reloadInputList() const
{ {
QList<FileData> fileDataList; QList<ReloadInput> result;
foreach (const QString fileName, m_fileNames) { foreach (const QString fileName, m_fileNames) {
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>( TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(
@@ -258,22 +330,23 @@ void DiffModifiedFilesController::reload()
const QString rightText = textDocument->plainText(); const QString rightText = textDocument->plainText();
FileData fileData = diffFiles(leftText, rightText); ReloadInput reloadInput;
fileData.leftFileInfo.fileName = fileName; reloadInput.leftText = leftText;
fileData.rightFileInfo.fileName = fileName; reloadInput.rightText = rightText;
fileData.leftFileInfo.typeInfo = tr("Saved"); reloadInput.leftFileInfo.fileName = fileName;
fileData.rightFileInfo.typeInfo = tr("Modified"); reloadInput.rightFileInfo.fileName = fileName;
fileData.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor; reloadInput.leftFileInfo.typeInfo = tr("Saved");
reloadInput.rightFileInfo.typeInfo = tr("Modified");
reloadInput.rightFileInfo.patchBehaviour = DiffFileInfo::PatchEditor;
if (!leftFileExists) if (!leftFileExists)
fileData.fileOperation = FileData::NewFile; reloadInput.fileOperation = FileData::NewFile;
fileDataList << fileData; result << reloadInput;
} }
} }
setDiffFiles(fileDataList); return result;
reloadFinished(true);
} }
///////////////// /////////////////
@@ -286,7 +359,7 @@ public:
const QString &rightFileName); const QString &rightFileName);
protected: protected:
void reload() override; QList<ReloadInput> reloadInputList() const final;
private: private:
const QString m_leftFileName; const QString m_leftFileName;
@@ -298,7 +371,7 @@ DiffExternalFilesController::DiffExternalFilesController(IDocument *document, co
DiffFilesController(document), m_leftFileName(leftFileName), m_rightFileName(rightFileName) DiffFilesController(document), m_leftFileName(leftFileName), m_rightFileName(rightFileName)
{ } { }
void DiffExternalFilesController::reload() QList<ReloadInput> DiffExternalFilesController::reloadInputList() const
{ {
QString errorString; QString errorString;
Utils::TextFileFormat format; Utils::TextFileFormat format;
@@ -322,20 +395,21 @@ void DiffExternalFilesController::reload()
rightFileExists = false; rightFileExists = false;
} }
FileData fileData = diffFiles(leftText, rightText); ReloadInput reloadInput;
fileData.leftFileInfo.fileName = m_leftFileName; reloadInput.leftText = leftText;
fileData.rightFileInfo.fileName = m_rightFileName; reloadInput.rightText = rightText;
reloadInput.leftFileInfo.fileName = m_leftFileName;
reloadInput.rightFileInfo.fileName = m_rightFileName;
if (!leftFileExists && rightFileExists) if (!leftFileExists && rightFileExists)
fileData.fileOperation = FileData::NewFile; reloadInput.fileOperation = FileData::NewFile;
else if (leftFileExists && !rightFileExists) else if (leftFileExists && !rightFileExists)
fileData.fileOperation = FileData::DeleteFile; reloadInput.fileOperation = FileData::DeleteFile;
QList<FileData> fileDataList; QList<ReloadInput> result;
if (leftFileExists || rightFileExists) if (leftFileExists || rightFileExists)
fileDataList << fileData; result << reloadInput;
setDiffFiles(fileDataList); return result;
reloadFinished(true);
} }
///////////////// /////////////////

View File

@@ -38,6 +38,7 @@ publication by Neil Fraser: http://neil.fraser.name/writing/diff/
#include <QMap> #include <QMap>
#include <QPair> #include <QPair>
#include <QCoreApplication> #include <QCoreApplication>
#include <QFutureInterfaceBase>
namespace DiffEditor { namespace DiffEditor {
@@ -985,9 +986,10 @@ QString Diff::toString() const
/////////////// ///////////////
Differ::Differ() Differ::Differ(QFutureInterfaceBase *jobController)
: m_diffMode(Differ::LineMode), : m_diffMode(Differ::LineMode),
m_currentDiffMode(Differ::LineMode) m_currentDiffMode(Differ::LineMode),
m_jobController(jobController)
{ {
} }
@@ -1124,6 +1126,11 @@ QList<Diff> Differ::diffMyers(const QString &text1, const QString &text2)
int kMinReverse = -D; int kMinReverse = -D;
int kMaxReverse = D; int kMaxReverse = D;
for (int d = 0; d <= D; d++) { for (int d = 0; d <= D; d++) {
if (m_jobController && m_jobController->isCanceled()) {
delete [] forwardV;
delete [] reverseV;
return QList<Diff>();
}
// going forward // going forward
for (int k = qMax(-d, kMinForward + qAbs(d + kMinForward) % 2); for (int k = qMax(-d, kMinForward + qAbs(d + kMinForward) % 2);
k <= qMin(d, kMaxForward - qAbs(d + kMaxForward) % 2); k <= qMin(d, kMaxForward - qAbs(d + kMaxForward) % 2);
@@ -1237,7 +1244,18 @@ QList<Diff> Differ::diffNonCharMode(const QString &text1, const QString &text2)
QString lastDelete; QString lastDelete;
QString lastInsert; QString lastInsert;
QList<Diff> newDiffList; QList<Diff> newDiffList;
if (m_jobController) {
m_jobController->setProgressRange(0, diffList.count());
m_jobController->setProgressValue(0);
}
for (int i = 0; i <= diffList.count(); i++) { for (int i = 0; i <= diffList.count(); i++) {
if (m_jobController) {
if (m_jobController->isCanceled()) {
m_currentDiffMode = diffMode;
return QList<Diff>();
}
m_jobController->setProgressValue(i + 1);
}
const Diff diffItem = i < diffList.count() const Diff diffItem = i < diffList.count()
? diffList.at(i) ? diffList.at(i)
: Diff(Diff::Equal); // dummy, ensure we process to the end : Diff(Diff::Equal); // dummy, ensure we process to the end

View File

@@ -31,6 +31,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
template <class K, class T> template <class K, class T>
class QMap; class QMap;
class QFutureInterfaceBase;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace DiffEditor { namespace DiffEditor {
@@ -62,7 +63,7 @@ public:
WordMode, WordMode,
LineMode LineMode
}; };
Differ(); Differ(QFutureInterfaceBase *jobController = nullptr);
QList<Diff> diff(const QString &text1, const QString &text2); QList<Diff> diff(const QString &text1, const QString &text2);
QList<Diff> unifiedDiff(const QString &text1, const QString &text2); QList<Diff> unifiedDiff(const QString &text1, const QString &text2);
void setDiffMode(DiffMode mode); void setDiffMode(DiffMode mode);
@@ -110,6 +111,7 @@ private:
int subTextStart); int subTextStart);
DiffMode m_diffMode; DiffMode m_diffMode;
DiffMode m_currentDiffMode; DiffMode m_currentDiffMode;
QFutureInterfaceBase *m_jobController = nullptr;
}; };
} // namespace DiffEditor } // namespace DiffEditor