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 "selectabletexteditorwidget.h"
|
||||||
#include "diffeditorconstants.h"
|
#include "diffeditorconstants.h"
|
||||||
#include "diffeditordocument.h"
|
#include "diffeditordocument.h"
|
||||||
|
#include "diffeditorplugin.h"
|
||||||
#include "diffutils.h"
|
#include "diffutils.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
@@ -22,7 +23,9 @@
|
|||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/find/highlightscrollbarcontroller.h>
|
#include <coreplugin/find/highlightscrollbarcontroller.h>
|
||||||
#include <coreplugin/minisplitter.h>
|
#include <coreplugin/minisplitter.h>
|
||||||
|
#include <coreplugin/progressmanager/progressmanager.h>
|
||||||
|
|
||||||
|
#include <utils/runextensions.h>
|
||||||
#include <utils/tooltip/tooltip.h>
|
#include <utils/tooltip/tooltip.h>
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
@@ -876,6 +879,14 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
|
|||||||
Core::ICore::addContextObject(rightContext);
|
Core::ICore::addContextObject(rightContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SideBySideDiffEditorWidget::~SideBySideDiffEditorWidget()
|
||||||
|
{
|
||||||
|
if (m_watcher) {
|
||||||
|
m_watcher->cancel();
|
||||||
|
DiffEditorPlugin::addFuture(m_watcher->future());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TextEditorWidget *SideBySideDiffEditorWidget::leftEditorWidget() const
|
TextEditorWidget *SideBySideDiffEditorWidget::leftEditorWidget() const
|
||||||
{
|
{
|
||||||
return m_leftEditor;
|
return m_leftEditor;
|
||||||
@@ -907,13 +918,19 @@ void SideBySideDiffEditorWidget::clear(const QString &message)
|
|||||||
setDiff({});
|
setDiff({});
|
||||||
m_leftEditor->clearAll(message);
|
m_leftEditor->clearAll(message);
|
||||||
m_rightEditor->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)
|
void SideBySideDiffEditorWidget::setDiff(const QList<FileData> &diffFileList)
|
||||||
{
|
{
|
||||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
const GuardLocker locker(m_controller.m_ignoreChanges);
|
||||||
m_leftEditor->clear();
|
m_leftEditor->clearAll(tr("Waiting for data..."));
|
||||||
m_rightEditor->clear();
|
m_rightEditor->clearAll(tr("Waiting for data..."));
|
||||||
|
|
||||||
m_controller.m_contextFileData = diffFileList;
|
m_controller.m_contextFileData = diffFileList;
|
||||||
if (m_controller.m_contextFileData.isEmpty()) {
|
if (m_controller.m_contextFileData.isEmpty()) {
|
||||||
@@ -966,29 +983,104 @@ void SideBySideDiffEditorWidget::restoreState()
|
|||||||
|
|
||||||
void SideBySideDiffEditorWidget::showDiff()
|
void SideBySideDiffEditorWidget::showDiff()
|
||||||
{
|
{
|
||||||
QFutureInterface<void> fi;
|
m_watcher.reset(new QFutureWatcher<ShowResults>());
|
||||||
const SideBySideDiffOutput output = SideDiffData::diffOutput(fi, 0, 100, {&m_controller});
|
m_controller.setBusyShowing(true);
|
||||||
|
|
||||||
m_leftEditor->setDiffData(output.side[LeftSide].diffData);
|
connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] {
|
||||||
m_rightEditor->setDiffData(output.side[RightSide].diffData);
|
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);
|
||||||
|
|
||||||
{
|
m_leftEditor->setReadOnly(true);
|
||||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
m_rightEditor->setReadOnly(true);
|
||||||
m_leftEditor->clear();
|
}
|
||||||
m_leftEditor->setPlainText(output.side[LeftSide].diffText);
|
m_leftEditor->setSelections(results[LeftSide].selections);
|
||||||
m_rightEditor->clear();
|
m_rightEditor->setSelections(results[RightSide].selections);
|
||||||
m_rightEditor->setPlainText(output.side[RightSide].diffText);
|
}
|
||||||
}
|
m_watcher.release()->deleteLater();
|
||||||
|
m_controller.setBusyShowing(false);
|
||||||
|
});
|
||||||
|
|
||||||
QTextBlock block = m_leftEditor->document()->firstBlock();
|
const DiffEditorInput input(&m_controller);
|
||||||
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));
|
|
||||||
|
|
||||||
m_leftEditor->setSelections(output.side[LeftSide].selections);
|
auto getDocument = [input](QFutureInterface<ShowResults> &futureInterface) {
|
||||||
m_rightEditor->setSelections(output.side[RightSide].selections);
|
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)
|
void SideBySideDiffEditorWidget::setFontSettings(const FontSettings &fontSettings)
|
||||||
@@ -1143,10 +1235,13 @@ void SideBySideDiffEditorWidget::handlePositionChange(SideDiffEditorWidget *sour
|
|||||||
if (m_controller.m_ignoreChanges.isLocked())
|
if (m_controller.m_ignoreChanges.isLocked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const int fileIndex = source->diffData().fileIndexForBlockNumber(source->textCursor().blockNumber());
|
||||||
|
if (fileIndex < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
const GuardLocker locker(m_controller.m_ignoreChanges);
|
||||||
syncCursor(source, dest);
|
syncCursor(source, dest);
|
||||||
emit currentDiffFileIndexChanged(
|
emit currentDiffFileIndexChanged(fileIndex);
|
||||||
source->diffData().fileIndexForBlockNumber(source->textCursor().blockNumber()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SideBySideDiffEditorWidget::syncCursor(SideDiffEditorWidget *source, SideDiffEditorWidget *dest)
|
void SideBySideDiffEditorWidget::syncCursor(SideDiffEditorWidget *source, SideDiffEditorWidget *dest)
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ class SideBySideDiffEditorWidget : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit SideBySideDiffEditorWidget(QWidget *parent = nullptr);
|
explicit SideBySideDiffEditorWidget(QWidget *parent = nullptr);
|
||||||
|
~SideBySideDiffEditorWidget();
|
||||||
|
|
||||||
TextEditor::TextEditorWidget *leftEditorWidget() const;
|
TextEditor::TextEditorWidget *leftEditorWidget() const;
|
||||||
TextEditor::TextEditorWidget *rightEditorWidget() const;
|
TextEditor::TextEditorWidget *rightEditorWidget() const;
|
||||||
@@ -143,6 +144,16 @@ private:
|
|||||||
DiffEditorWidgetController m_controller;
|
DiffEditorWidgetController m_controller;
|
||||||
|
|
||||||
bool m_horizontalSync = false;
|
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
|
} // namespace Internal
|
||||||
|
|||||||
@@ -124,11 +124,11 @@ void UnifiedDiffEditorWidget::setFontSettings(const FontSettings &fontSettings)
|
|||||||
|
|
||||||
void UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor()
|
void UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor()
|
||||||
{
|
{
|
||||||
const int fileIndex = fileIndexForBlockNumber(textCursor().blockNumber());
|
if (m_controller.m_ignoreChanges.isLocked())
|
||||||
if (fileIndex < 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_controller.m_ignoreChanges.isLocked())
|
const int fileIndex = fileIndexForBlockNumber(textCursor().blockNumber());
|
||||||
|
if (fileIndex < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const GuardLocker locker(m_controller.m_ignoreChanges);
|
const GuardLocker locker(m_controller.m_ignoreChanges);
|
||||||
@@ -515,7 +515,7 @@ void UnifiedDiffEditorWidget::showDiff()
|
|||||||
|
|
||||||
const DiffEditorInput input(&m_controller);
|
const DiffEditorInput input(&m_controller);
|
||||||
|
|
||||||
auto getDocument = [=](QFutureInterface<ShowResult> &futureInterface) {
|
auto getDocument = [input](QFutureInterface<ShowResult> &futureInterface) {
|
||||||
auto cleanup = qScopeGuard([&futureInterface] {
|
auto cleanup = qScopeGuard([&futureInterface] {
|
||||||
if (futureInterface.isCanceled())
|
if (futureInterface.isCanceled())
|
||||||
futureInterface.reportCanceled();
|
futureInterface.reportCanceled();
|
||||||
@@ -540,7 +540,7 @@ void UnifiedDiffEditorWidget::showDiff()
|
|||||||
// but this would freeze the thread for couple of seconds without progress reporting
|
// but this would freeze the thread for couple of seconds without progress reporting
|
||||||
// and without checking for canceled.
|
// and without checking for canceled.
|
||||||
const int diffSize = output.diffText.size();
|
const int diffSize = output.diffText.size();
|
||||||
const int packageSize = 100000;
|
const int packageSize = 10000;
|
||||||
int currentPos = 0;
|
int currentPos = 0;
|
||||||
QTextCursor cursor(result.textDocument->document());
|
QTextCursor cursor(result.textDocument->document());
|
||||||
while (currentPos < diffSize) {
|
while (currentPos < diffSize) {
|
||||||
|
|||||||
Reference in New Issue
Block a user