forked from qt-creator/qt-creator
SideBySideDiffEditor: Move showing diff into separate thread
Change-Id: I8b0a4835cf6f51e4acfd483dcfc7b94585c64bf5 Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include "selectabletexteditorwidget.h"
|
||||
#include "diffeditorconstants.h"
|
||||
#include "diffeditordocument.h"
|
||||
#include "diffeditorplugin.h"
|
||||
#include "diffutils.h"
|
||||
|
||||
#include <QMenu>
|
||||
@@ -22,7 +23,9 @@
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/find/highlightscrollbarcontroller.h>
|
||||
#include <coreplugin/minisplitter.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/tooltip/tooltip.h>
|
||||
|
||||
using namespace Core;
|
||||
@@ -876,6 +879,14 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
|
||||
Core::ICore::addContextObject(rightContext);
|
||||
}
|
||||
|
||||
SideBySideDiffEditorWidget::~SideBySideDiffEditorWidget()
|
||||
{
|
||||
if (m_watcher) {
|
||||
m_watcher->cancel();
|
||||
DiffEditorPlugin::addFuture(m_watcher->future());
|
||||
}
|
||||
}
|
||||
|
||||
TextEditorWidget *SideBySideDiffEditorWidget::leftEditorWidget() const
|
||||
{
|
||||
return m_leftEditor;
|
||||
@@ -907,13 +918,19 @@ void SideBySideDiffEditorWidget::clear(const QString &message)
|
||||
setDiff({});
|
||||
m_leftEditor->clearAll(message);
|
||||
m_rightEditor->clearAll(message);
|
||||
if (m_watcher) {
|
||||
m_watcher->cancel();
|
||||
DiffEditorPlugin::addFuture(m_watcher->future());
|
||||
m_watcher.reset();
|
||||
m_controller.setBusyShowing(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SideBySideDiffEditorWidget::setDiff(const QList<FileData> &diffFileList)
|
||||
{
|
||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
||||
m_leftEditor->clear();
|
||||
m_rightEditor->clear();
|
||||
m_leftEditor->clearAll(tr("Waiting for data..."));
|
||||
m_rightEditor->clearAll(tr("Waiting for data..."));
|
||||
|
||||
m_controller.m_contextFileData = diffFileList;
|
||||
if (m_controller.m_contextFileData.isEmpty()) {
|
||||
@@ -966,29 +983,104 @@ void SideBySideDiffEditorWidget::restoreState()
|
||||
|
||||
void SideBySideDiffEditorWidget::showDiff()
|
||||
{
|
||||
QFutureInterface<void> fi;
|
||||
const SideBySideDiffOutput output = SideDiffData::diffOutput(fi, 0, 100, {&m_controller});
|
||||
m_watcher.reset(new QFutureWatcher<ShowResults>());
|
||||
m_controller.setBusyShowing(true);
|
||||
|
||||
m_leftEditor->setDiffData(output.side[LeftSide].diffData);
|
||||
m_rightEditor->setDiffData(output.side[RightSide].diffData);
|
||||
connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] {
|
||||
if (m_watcher->isCanceled()) {
|
||||
m_leftEditor->clearAll(tr("Retrieving data failed."));
|
||||
m_rightEditor->clearAll(tr("Retrieving data failed."));
|
||||
} else {
|
||||
const ShowResults results = m_watcher->result();
|
||||
m_leftEditor->setDiffData(results[LeftSide].diffData);
|
||||
m_rightEditor->setDiffData(results[RightSide].diffData);
|
||||
TextDocumentPtr leftDoc(results[LeftSide].textDocument);
|
||||
TextDocumentPtr rightDoc(results[RightSide].textDocument);
|
||||
{
|
||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
||||
// TextDocument was living in no thread, so it's safe to pull it
|
||||
leftDoc->moveToThread(thread());
|
||||
rightDoc->moveToThread(thread());
|
||||
m_leftEditor->setTextDocument(leftDoc);
|
||||
m_rightEditor->setTextDocument(rightDoc);
|
||||
|
||||
{
|
||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
||||
m_leftEditor->clear();
|
||||
m_leftEditor->setPlainText(output.side[LeftSide].diffText);
|
||||
m_rightEditor->clear();
|
||||
m_rightEditor->setPlainText(output.side[RightSide].diffText);
|
||||
}
|
||||
m_leftEditor->setReadOnly(true);
|
||||
m_rightEditor->setReadOnly(true);
|
||||
}
|
||||
m_leftEditor->setSelections(results[LeftSide].selections);
|
||||
m_rightEditor->setSelections(results[RightSide].selections);
|
||||
}
|
||||
m_watcher.release()->deleteLater();
|
||||
m_controller.setBusyShowing(false);
|
||||
});
|
||||
|
||||
QTextBlock block = m_leftEditor->document()->firstBlock();
|
||||
for (int b = 0; block.isValid(); block = block.next(), ++b)
|
||||
SelectableTextEditorWidget::setFoldingIndent(block, output.foldingIndent.value(b, 3));
|
||||
block = m_rightEditor->document()->firstBlock();
|
||||
for (int b = 0; block.isValid(); block = block.next(), ++b)
|
||||
SelectableTextEditorWidget::setFoldingIndent(block, output.foldingIndent.value(b, 3));
|
||||
const DiffEditorInput input(&m_controller);
|
||||
|
||||
m_leftEditor->setSelections(output.side[LeftSide].selections);
|
||||
m_rightEditor->setSelections(output.side[RightSide].selections);
|
||||
auto getDocument = [input](QFutureInterface<ShowResults> &futureInterface) {
|
||||
auto cleanup = qScopeGuard([&futureInterface] {
|
||||
if (futureInterface.isCanceled())
|
||||
futureInterface.reportCanceled();
|
||||
});
|
||||
const int firstPartMax = 20; // showDiff is about 4 times quicker than filling document
|
||||
const int leftPartMax = 60;
|
||||
const int rightPartMax = 100;
|
||||
futureInterface.setProgressRange(0, rightPartMax);
|
||||
futureInterface.setProgressValue(0);
|
||||
QFutureInterface<void> fi = futureInterface;
|
||||
const SideBySideDiffOutput output = SideDiffData::diffOutput(fi, 0, firstPartMax, input);
|
||||
if (futureInterface.isCanceled())
|
||||
return;
|
||||
|
||||
const ShowResult leftResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
|
||||
output.side[LeftSide].diffData, output.side[LeftSide].selections};
|
||||
const ShowResult rightResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
|
||||
output.side[RightSide].diffData, output.side[RightSide].selections};
|
||||
const ShowResults result{leftResult, rightResult};
|
||||
|
||||
auto propagateDocument = [&output, &fi](DiffSide side, const ShowResult &result,
|
||||
int progressMin, int progressMax) {
|
||||
// No need to store the change history
|
||||
result.textDocument->document()->setUndoRedoEnabled(false);
|
||||
|
||||
// We could do just:
|
||||
// result.textDocument->setPlainText(output.diffText);
|
||||
// but this would freeze the thread for couple of seconds without progress reporting
|
||||
// and without checking for canceled.
|
||||
const int diffSize = output.side[side].diffText.size();
|
||||
const int packageSize = 10000;
|
||||
int currentPos = 0;
|
||||
QTextCursor cursor(result.textDocument->document());
|
||||
while (currentPos < diffSize) {
|
||||
const QString package = output.side[side].diffText.mid(currentPos, packageSize);
|
||||
cursor.insertText(package);
|
||||
currentPos += package.size();
|
||||
fi.setProgressValue(DiffUtils::interpolate(currentPos, 0, diffSize, progressMin, progressMax));
|
||||
if (fi.isCanceled())
|
||||
return;
|
||||
}
|
||||
|
||||
QTextBlock block = result.textDocument->document()->firstBlock();
|
||||
for (int b = 0; block.isValid(); block = block.next(), ++b)
|
||||
SelectableTextEditorWidget::setFoldingIndent(block, output.foldingIndent.value(b, 3));
|
||||
|
||||
// If future was canceled, the destructor runs in this thread, so we can't move it
|
||||
// to caller's thread. We push it to no thread (make object to have no thread affinity),
|
||||
// and later, in the caller's thread, we pull it back to the caller's thread.
|
||||
result.textDocument->moveToThread(nullptr);
|
||||
};
|
||||
|
||||
propagateDocument(LeftSide, leftResult, firstPartMax, leftPartMax);
|
||||
if (fi.isCanceled())
|
||||
return;
|
||||
propagateDocument(RightSide, rightResult, leftPartMax, rightPartMax);
|
||||
if (fi.isCanceled())
|
||||
return;
|
||||
|
||||
futureInterface.reportResult(result);
|
||||
};
|
||||
|
||||
m_watcher->setFuture(runAsync(getDocument));
|
||||
ProgressManager::addTask(m_watcher->future(), tr("Rendering diff"), "DiffEditor");
|
||||
}
|
||||
|
||||
void SideBySideDiffEditorWidget::setFontSettings(const FontSettings &fontSettings)
|
||||
@@ -1143,10 +1235,13 @@ void SideBySideDiffEditorWidget::handlePositionChange(SideDiffEditorWidget *sour
|
||||
if (m_controller.m_ignoreChanges.isLocked())
|
||||
return;
|
||||
|
||||
const int fileIndex = source->diffData().fileIndexForBlockNumber(source->textCursor().blockNumber());
|
||||
if (fileIndex < 0)
|
||||
return;
|
||||
|
||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
||||
syncCursor(source, dest);
|
||||
emit currentDiffFileIndexChanged(
|
||||
source->diffData().fileIndexForBlockNumber(source->textCursor().blockNumber()));
|
||||
emit currentDiffFileIndexChanged(fileIndex);
|
||||
}
|
||||
|
||||
void SideBySideDiffEditorWidget::syncCursor(SideDiffEditorWidget *source, SideDiffEditorWidget *dest)
|
||||
|
||||
@@ -94,6 +94,7 @@ class SideBySideDiffEditorWidget : public QWidget
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SideBySideDiffEditorWidget(QWidget *parent = nullptr);
|
||||
~SideBySideDiffEditorWidget();
|
||||
|
||||
TextEditor::TextEditorWidget *leftEditorWidget() const;
|
||||
TextEditor::TextEditorWidget *rightEditorWidget() const;
|
||||
@@ -143,6 +144,16 @@ private:
|
||||
DiffEditorWidgetController m_controller;
|
||||
|
||||
bool m_horizontalSync = false;
|
||||
|
||||
struct ShowResult
|
||||
{
|
||||
QSharedPointer<TextEditor::TextDocument> textDocument{};
|
||||
SideDiffData diffData;
|
||||
DiffSelections selections;
|
||||
};
|
||||
using ShowResults = std::array<ShowResult, SideCount>;
|
||||
|
||||
std::unique_ptr<QFutureWatcher<ShowResults>> m_watcher;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -124,11 +124,11 @@ void UnifiedDiffEditorWidget::setFontSettings(const FontSettings &fontSettings)
|
||||
|
||||
void UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor()
|
||||
{
|
||||
const int fileIndex = fileIndexForBlockNumber(textCursor().blockNumber());
|
||||
if (fileIndex < 0)
|
||||
if (m_controller.m_ignoreChanges.isLocked())
|
||||
return;
|
||||
|
||||
if (m_controller.m_ignoreChanges.isLocked())
|
||||
const int fileIndex = fileIndexForBlockNumber(textCursor().blockNumber());
|
||||
if (fileIndex < 0)
|
||||
return;
|
||||
|
||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
||||
@@ -515,7 +515,7 @@ void UnifiedDiffEditorWidget::showDiff()
|
||||
|
||||
const DiffEditorInput input(&m_controller);
|
||||
|
||||
auto getDocument = [=](QFutureInterface<ShowResult> &futureInterface) {
|
||||
auto getDocument = [input](QFutureInterface<ShowResult> &futureInterface) {
|
||||
auto cleanup = qScopeGuard([&futureInterface] {
|
||||
if (futureInterface.isCanceled())
|
||||
futureInterface.reportCanceled();
|
||||
@@ -540,7 +540,7 @@ void UnifiedDiffEditorWidget::showDiff()
|
||||
// but this would freeze the thread for couple of seconds without progress reporting
|
||||
// and without checking for canceled.
|
||||
const int diffSize = output.diffText.size();
|
||||
const int packageSize = 100000;
|
||||
const int packageSize = 10000;
|
||||
int currentPos = 0;
|
||||
QTextCursor cursor(result.textDocument->document());
|
||||
while (currentPos < diffSize) {
|
||||
|
||||
Reference in New Issue
Block a user