Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,18 @@ private static RGB interpolate(RGB fg, RGB bg, double scale) {
private boolean isConfigured = false;
private boolean fRedoDiff = false;

/**
* Combined line count of both sides from which on the first comparison is
* deferred, so that the documents are painted before they are compared. Smaller
* inputs are compared right away: the diff costs next to nothing there, and
* showing the text and jumping to the first change in two steps would only
* flicker.
*/
private static final int DEFER_DIFF_LINE_COUNT = 2000;

/** Pending first diff of the current input, scheduled after the documents are set. */
private UIJob fInitialDiffJob;

private double fCurrMagni = 0;

private int fCurrentHeight;
Expand Down Expand Up @@ -2103,6 +2115,11 @@ protected String getDocumentPartitioning() {
protected void handleDispose(DisposeEvent event) {
OperationHistoryFactory.getOperationHistory().removeOperationHistoryListener(operationHistoryListener);

if (fInitialDiffJob != null) {
fInitialDiffJob.cancel();
fInitialDiffJob = null;
}

if (fHandlerService != null) {
fHandlerService.dispose();
}
Expand Down Expand Up @@ -3211,41 +3228,73 @@ protected void updateContent(Object ancestor, Object left, Object right) {

setSyncScrolling(fPreferenceStore.getBoolean(ComparePreferencePage.SYNCHRONIZE_SCROLLING));

update(false);

if (!fHasErrors && !emptyInput && !fComposite.isDisposed()) {
if (isRefreshing()) {
fLeftContributor.updateSelection(fLeft, !fSynchronizedScrolling);
fRightContributor.updateSelection(fRight, !fSynchronizedScrolling);
fAncestorContributor.updateSelection(fAncestor, !fSynchronizedScrolling);
if (fSynchronizedScrolling && fSynchronziedScrollPosition != -1) {
synchronizedScrollVertical(fSynchronziedScrollPosition);
}
} else {
if (isPatchHunk()) {
if (right != null && Adapters.adapt(right, IHunk.class) != null) {
fLeft.getSourceViewer().setTopIndex(getHunkStart());
} else {
fRight.getSourceViewer().setTopIndex(getHunkStart());
// A refresh restores the cached selection and scroll position, and that cache is
// dropped as soon as the refresh returns, so a refresh is never deferred.
if (isRefreshing() || fLeftLineCount + fRightLineCount <= DEFER_DIFF_LINE_COUNT) {
update(false);
if (!fHasErrors && !emptyInput && !fComposite.isDisposed()) {
if (isRefreshing()) {
fLeftContributor.updateSelection(fLeft, !fSynchronizedScrolling);
fRightContributor.updateSelection(fRight, !fSynchronizedScrolling);
fAncestorContributor.updateSelection(fAncestor, !fSynchronizedScrolling);
if (fSynchronizedScrolling && fSynchronziedScrollPosition != -1) {
synchronizedScrollVertical(fSynchronziedScrollPosition);
}
} else {
Diff selectDiff= null;
if (FIX_47640) {
if (leftRange != null) {
selectDiff= fMerger.findDiff(LEFT_CONTRIBUTOR, leftRange);
} else if (rightRange != null) {
selectDiff= fMerger.findDiff(RIGHT_CONTRIBUTOR, rightRange);
}
}
if (selectDiff != null) {
setCurrentDiff(selectDiff, true);
} else {
selectFirstDiff(true);
}
revealInitialDiff(right, leftRange, rightRange);
}
}
return;
}

// The documents are set, so let them be painted before comparing them. The diff
// and everything derived from it follows in a separate UI event.
final boolean isEmptyInput = emptyInput;
final Object rightElement = right;
final Position leftSelectRange = leftRange;
final Position rightSelectRange = rightRange;
if (fInitialDiffJob != null) {
fInitialDiffJob.cancel();
}
fInitialDiffJob = new UIJob(CompareMessages.DocumentMerger_0) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
fInitialDiffJob = null;
if (fComposite == null || fComposite.isDisposed()) {
return Status.OK_STATUS;
}
update(false);
if (!fHasErrors && !isEmptyInput && !fComposite.isDisposed()) {
revealInitialDiff(rightElement, leftSelectRange, rightSelectRange);
}
return Status.OK_STATUS;
}
};
fInitialDiffJob.schedule();
}

private void revealInitialDiff(Object right, Position leftRange, Position rightRange) {
if (isPatchHunk()) {
if (right != null && Adapters.adapt(right, IHunk.class) != null) {
fLeft.getSourceViewer().setTopIndex(getHunkStart());
} else {
fRight.getSourceViewer().setTopIndex(getHunkStart());
}
return;
}
Diff selectDiff= null;
if (FIX_47640) {
if (leftRange != null) {
selectDiff= fMerger.findDiff(LEFT_CONTRIBUTOR, leftRange);
} else if (rightRange != null) {
selectDiff= fMerger.findDiff(RIGHT_CONTRIBUTOR, rightRange);
}
}
if (selectDiff != null) {
setCurrentDiff(selectDiff, true);
} else {
selectFirstDiff(true);
}
}

private void configureSourceViewer(SourceViewer sourceViewer, boolean editable, ContributorInfo contributor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,55 @@ public void testCopyEmptyLeftToRightAndModify() throws Exception {
}
}

/**
* A large input must be shown before it is compared, so the differences are only
* available once the deferred comparison ran.
*/
@Test
public void testLargeInputIsShownBeforeItIsCompared() throws Exception {
DiffNode parentNode = new DiffNode(new ParentTestElement(), new ParentTestElement());
DiffNode testNode = new DiffNode(parentNode, Differencer.CHANGE, null,
new EditableTestElement(manyLines("line", 3000).getBytes()),
new EditableTestElement(manyLines("LINE", 3000).getBytes()));

runInDialog(testNode, () -> {
IMergeViewerTestAdapter ta = viewer.getAdapter(IMergeViewerTestAdapter.class);
assertEquals(0, ta.getChangesCount(), "a large input must not be compared before it is shown");
waitForChanges(ta);
assertTrue(ta.getChangesCount() > 0, "the deferred comparison did not produce differences");
});
}

private static String manyLines(String prefix, int count) {
StringBuilder content = new StringBuilder();
for (int i = 0; i < count; i++) {
content.append(prefix).append(' ').append(i).append('\n');
}
return content.toString();
}

private static void waitForChanges(IMergeViewerTestAdapter ta) {
Display display = Display.getCurrent();
long deadline = System.currentTimeMillis() + 30_000;
// A self-rescheduling timer keeps the loop waking so the deadline is enforced
// even while blocked in Display.sleep().
Runnable[] wake = new Runnable[1];
wake[0] = () -> display.timerExec(50, wake[0]);
display.timerExec(50, wake[0]);
try {
while (ta.getChangesCount() == 0) {
if (System.currentTimeMillis() > deadline) {
fail("no differences within 30000ms");
}
if (!display.readAndDispatch()) {
display.sleep();
}
}
} finally {
display.timerExec(-1, wake[0]);
}
}

@Test
public void testCompareFilter() throws Exception {
DiffNode parentNode = new DiffNode(new ParentTestElement(),
Expand Down
Loading