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 @@ -704,14 +704,20 @@ private boolean openUnifiedDiff(UnifiedDiffSource source, CompareEditorInput inp
// no active workbench page; fall back to the classic compare editor path
return false;
}
// Only an editor opened here may be closed again when the diff cannot be
// applied after all; one the user already had open stays untouched.
IEditorPart editorBefore = wpage.findEditor(source.editorInput());
IEditorPart openedHere = null;
try {
IDocumentMergerInput mergerInput = findDocumentMergerInput(input, source.compareInput());
IEditorPart editorPart = wpage.openEditor(source.editorInput(),
getEditorId(source.editorInput(), source.element()));
openedHere = editorPart == editorBefore ? null : editorPart;
if (editorPart instanceof MultiPageEditorPart mpe && mpe.getSelectedPage() instanceof IEditorPart selected) {
editorPart = selected;
}
if (!(editorPart instanceof ITextEditor textEditor)) {
closeIfOpenedHere(wpage, openedHere);
return false;
}
Action openTwoWayCompare = createOpenTwoWayCompareAction(input, page, editor, activate, textEditor);
Expand All @@ -726,13 +732,25 @@ private boolean openUnifiedDiff(UnifiedDiffSource source, CompareEditorInput inp
.open();
// The user canceled the diff, not the open: leave the text editor alone
// instead of falling back to the classic compare editor.
return status.isOK() || status == UnifiedDiffManager.CANCELED_BY_USER;
if (status.isOK() || status == UnifiedDiffManager.CANCELED_BY_USER) {
return true;
}
} catch (PartInitException e) {
CompareUIPlugin.log(e);
}
// The classic compare editor takes over, so the editor opened for the unified
// diff would only be a second editor on the same file without a comparison.
closeIfOpenedHere(wpage, openedHere);
return false;
}

private static void closeIfOpenedHere(IWorkbenchPage page, IEditorPart editor) {
if (editor != null) {
// Nothing here writes to the document, so there is nothing to save.
page.closeEditor(editor, false);
}
}

/**
* Returns the token comparator and whitespace factories of a registered custom
* merge viewer, or <code>null</code> when the platform's own text merge viewer
Expand Down
1 change: 1 addition & 0 deletions team/tests/org.eclipse.compare.tests/build.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# Mickael Istria (Red Hat Inc.) - 419531 Get rid of nested jars
###############################################################################
bin.includes = plugin.properties,\
plugin.xml,\
test.xml,\
about.html,\
.,\
Expand Down
24 changes: 24 additions & 0 deletions team/tests/org.eclipse.compare.tests/plugin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<!--
Copyright (c) 2026 Lars Vogel and others.

This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/

SPDX-License-Identifier: EPL-2.0
-->
<plugin>
<extension
point="org.eclipse.ui.editors">
<editor
class="org.eclipse.compare.tests.NonTextTestEditor"
default="true"
extensions="nontexteditortest"
id="org.eclipse.compare.tests.nonTextEditor"
name="Non-Text Test Editor">
</editor>
</extension>
</plugin>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (c) 2026 Lars Vogel and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eclipse contributors - initial API and implementation
*******************************************************************************/
package org.eclipse.compare.tests;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.part.EditorPart;

/**
* An editor that is deliberately not an {@link org.eclipse.ui.texteditor.ITextEditor}.
* Registered for the {@code nontexteditortest} extension so tests can open a file
* whose default editor cannot display a unified diff.
*/
public class NonTextTestEditor extends EditorPart {

public static final String ID = "org.eclipse.compare.tests.nonTextEditor"; //$NON-NLS-1$

public static final String EXTENSION = "nontexteditortest"; //$NON-NLS-1$

@Override
public void doSave(IProgressMonitor monitor) {
// nothing to save
}

@Override
public void doSaveAs() {
// saving as is not allowed
}

@Override
public void init(IEditorSite site, IEditorInput input) {
setSite(site);
setInput(input);
}

@Override
public boolean isDirty() {
return false;
}

@Override
public boolean isSaveAsAllowed() {
return false;
}

@Override
public void createPartControl(Composite parent) {
new Label(parent, SWT.NONE).setText("not a text editor"); //$NON-NLS-1$
}

@Override
public void setFocus() {
// no focusable content
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

Expand Down Expand Up @@ -56,11 +58,15 @@
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -286,6 +292,68 @@ public void testClassicEditorOpensWhenInputDoesNotQualify() {
"an input without shared document adapters must fall back to the compare editor"); //$NON-NLS-1$
}

/**
* The unified diff opens the editor before it knows whether the diff can be
* applied. When the registered editor turns out not to be a text editor, the
* classic compare editor takes over, and the editor opened a moment earlier
* must not stay behind as a second editor on the same file.
*/
@Test
public void testNoEditorIsLeftBehindWhenTheUnifiedDiffFallsBack() throws Exception {
IFile left = createFile("left." + NonTextTestEditor.EXTENSION, "alpha\nbravo\ncharlie\n"); //$NON-NLS-1$ //$NON-NLS-2$
IFile right = createFile("right." + NonTextTestEditor.EXTENSION, "alpha\nBRAVO\ncharlie\n"); //$NON-NLS-1$ //$NON-NLS-2$
assertEquals(NonTextTestEditor.ID, defaultEditorIdFor(left),
"the test needs a file whose default editor is not a text editor"); //$NON-NLS-1$

RecordingCompareEditorInput input = new RecordingCompareEditorInput(new WorkspaceFileElement(left),
new WorkspaceFileElement(right));
CompareUI.openCompareEditor(input);
pumpUntil(UnifiedDiffOpenTest::hasCompareEditor, "the classic compare editor did not take over"); //$NON-NLS-1$
processQueuedEvents();

assertNull(activePage().findEditor(new FileEditorInput(left)),
"the editor opened for the unified diff must be closed when the compare editor takes over"); //$NON-NLS-1$
assertEquals(1, activePage().getEditorReferences().length,
"the fallback must leave exactly one editor open"); //$NON-NLS-1$
}

/**
* An editor the user already had open is not the unified diff's to close, so it
* survives the fallback to the compare editor.
*/
@Test
public void testAlreadyOpenEditorSurvivesTheFallback() throws Exception {
IFile left = createFile("left." + NonTextTestEditor.EXTENSION, "alpha\nbravo\ncharlie\n"); //$NON-NLS-1$ //$NON-NLS-2$
IFile right = createFile("right." + NonTextTestEditor.EXTENSION, "alpha\nBRAVO\ncharlie\n"); //$NON-NLS-1$ //$NON-NLS-2$
IEditorPart preOpened = IDE.openEditor(activePage(), left, NonTextTestEditor.ID);
assertNotNull(preOpened, "the editor to be preserved did not open"); //$NON-NLS-1$

RecordingCompareEditorInput input = new RecordingCompareEditorInput(new WorkspaceFileElement(left),
new WorkspaceFileElement(right));
CompareUI.openCompareEditor(input);
pumpUntil(UnifiedDiffOpenTest::hasCompareEditor, "the classic compare editor did not take over"); //$NON-NLS-1$
processQueuedEvents();

assertSame(preOpened, activePage().findEditor(new FileEditorInput(left)),
"an editor the unified diff did not open must stay open"); //$NON-NLS-1$
assertEquals(2, activePage().getEditorReferences().length,
"the pre-opened editor and the compare editor must both be open"); //$NON-NLS-1$
}

private static String defaultEditorIdFor(IFile file) {
IEditorDescriptor descriptor = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(file.getName());
return descriptor == null ? null : descriptor.getId();
}

private static boolean hasCompareEditor() {
for (IEditorReference reference : activePage().getEditorReferences()) {
if (reference.getEditor(false) instanceof CompareEditor) {
return true;
}
}
return false;
}

private RecordingCompareEditorInput openQualifyingInput() throws CoreException {
IFile left = createFile("left.txt", "alpha\nbravo\ncharlie\ndelta\n"); //$NON-NLS-1$ //$NON-NLS-2$
IFile right = createFile("right.txt", "alpha\nBRAVO\ncharlie\ndelta\n"); //$NON-NLS-1$ //$NON-NLS-2$
Expand Down
Loading