Bug 577332 - In Compare editor, Generic Editor doesn't configure viewer

...for baseline documents because the document isn't backed by a buffer
so its content-types cannot be resolved.
For the compare viewer, we store the content-types that could be
resolved for other panels and use them as fallback when no content-type
can be resolved.

Change-Id: I2425d8b826ca31c79a68cf719d7bb2903e54750f
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.text/+/187892
Tested-by: Platform Bot <platform-bot@eclipse.org>
Reviewed-by: Mickael Istria <mistria@redhat.com>
diff --git a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
index 0d8bbe1..7f881ef 100644
--- a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
+++ b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.ui.genericeditor;singleton:=true
-Bundle-Version: 1.2.100.qualifier
+Bundle-Version: 1.2.200.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Require-Bundle: org.eclipse.ui.workbench.texteditor;bundle-version="3.10.0",
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextEditor.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextEditor.java
index cbb910c..ad14b1c 100644
--- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextEditor.java
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextEditor.java
@@ -37,7 +37,8 @@
 import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
 
 /**
- * A generic code editor that is aimed at being extended by contributions. Behavior is supposed to be added via extensions, not by inheritance.
+ * A generic code editor that is aimed at being extended by contributions.
+ * Behavior is supposed to be added via extensions, not by inheritance.
  *
  * @since 1.0
  */
@@ -66,26 +67,31 @@
 	/**
 	 * Initializes the key binding scopes of this generic code editor.
 	 */
-	@Override protected void initializeKeyBindingScopes() {
+	@Override
+	protected void initializeKeyBindingScopes() {
 		setKeyBindingScopes(new String[] { CONTEXT_ID });
 	}
 
-	@Override protected void doSetInput(IEditorInput input) throws CoreException {
+	@Override
+	protected void doSetInput(IEditorInput input) throws CoreException {
 		super.doSetInput(input);
 		configuration.watchDocument(getDocumentProvider().getDocument(input));
 	}
 
-	@Override protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
+	@Override
+	protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
 		fAnnotationAccess = getAnnotationAccess();
 		fOverviewRuler = createOverviewRuler(getSharedColors());
 
-		ProjectionViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles);
+		ProjectionViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(),
+				styles);
 		SourceViewerDecorationSupport support = getSourceViewerDecorationSupport(viewer);
 		configureCharacterPairMatcher(viewer, support);
 		return viewer;
 	}
 
-	@Override public void createPartControl(Composite parent) {
+	@Override
+	public void createPartControl(Composite parent) {
 		super.createPartControl(parent);
 		ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
 
@@ -94,26 +100,29 @@
 		computeImage();
 	}
 
-	@Override protected void initializeEditor() {
+	@Override
+	protected void initializeEditor() {
 		super.initializeEditor();
-		setPreferenceStore(new ChainedPreferenceStore(new IPreferenceStore[] { GenericEditorPreferenceConstants.getPreferenceStore(), EditorsUI.getPreferenceStore() }));
+		setPreferenceStore(new ChainedPreferenceStore(new IPreferenceStore[] {
+				GenericEditorPreferenceConstants.getPreferenceStore(), EditorsUI.getPreferenceStore() }));
 	}
 
 	/**
-	 * Configure the {@link ICharacterPairMatcher} from the "org.eclipse.ui.genericeditor.characterPairMatchers" extension point.
+	 * Configure the {@link ICharacterPairMatcher} from the
+	 * "org.eclipse.ui.genericeditor.characterPairMatchers" extension point.
 	 *
-	 * @param viewer
-	 *            the source viewer.
+	 * @param viewer  the source viewer.
 	 *
-	 * @param support
-	 *            the source viewer decoration support.
+	 * @param support the source viewer decoration support.
 	 */
 	private void configureCharacterPairMatcher(ISourceViewer viewer, SourceViewerDecorationSupport support) {
-		List<ICharacterPairMatcher> matchers = GenericEditorPlugin.getDefault().getCharacterPairMatcherRegistry().getCharacterPairMatchers(viewer, this, configuration.getContentTypes(viewer));
+		List<ICharacterPairMatcher> matchers = GenericEditorPlugin.getDefault().getCharacterPairMatcherRegistry()
+				.getCharacterPairMatchers(viewer, this, configuration.getContentTypes(viewer.getDocument()));
 		if (!matchers.isEmpty()) {
 			ICharacterPairMatcher matcher = matchers.get(0);
 			support.setCharacterPairMatcher(matcher);
-			support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR, HIGHLIGHT_BRACKET_AT_CARET_LOCATION, ENCLOSING_BRACKETS);
+			support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR,
+					HIGHLIGHT_BRACKET_AT_CARET_LOCATION, ENCLOSING_BRACKETS);
 		}
 	}
 
@@ -133,7 +142,7 @@
 	private IContentType[] getContentTypes() {
 		ISourceViewer sourceViewer = getSourceViewer();
 		if (sourceViewer != null) {
-			return configuration.getContentTypes(sourceViewer).toArray(new IContentType[] {});
+			return configuration.getContentTypes(sourceViewer.getDocument()).toArray(new IContentType[] {});
 		}
 		return new IContentType[] {};
 	}
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java
index 0f1f212..3ac1029 100644
--- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java
@@ -42,7 +42,6 @@
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IDocumentPartitioningListener;
 import org.eclipse.jface.text.ITextHover;
-import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
 import org.eclipse.jface.text.contentassist.IContentAssistant;
 import org.eclipse.jface.text.presentation.IPresentationReconciler;
@@ -70,7 +69,8 @@
 		implements IDocumentPartitioningListener {
 
 	private ITextEditor editor;
-	private Set<IContentType> contentTypes;
+	private Set<IContentType> resolvedContentTypes;
+	private Set<IContentType> fallbackContentTypes = Set.of();
 	private IDocument document;
 
 	private GenericEditorContentAssistant contentAssistant;
@@ -85,53 +85,53 @@
 		this.editor = editor;
 	}
 
-	Set<IContentType> getContentTypes(ITextViewer viewer) {
-		if (this.contentTypes == null) {
-			this.contentTypes = new LinkedHashSet<>();
-			ITextFileBuffer buffer = getCurrentBuffer(viewer);
-			if (buffer != null) {
-				try {
-					IContentType contentType = buffer.getContentType();
-					if (contentType != null) {
-						this.contentTypes.add(contentType);
-					}
-				} catch (CoreException ex) {
-					GenericEditorPlugin.getDefault().getLog()
-							.log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex));
+	public Set<IContentType> getContentTypes(IDocument document) {
+		if (this.resolvedContentTypes != null) {
+			return this.resolvedContentTypes;
+		}
+		this.resolvedContentTypes = new LinkedHashSet<>();
+		ITextFileBuffer buffer = getCurrentBuffer(document);
+		if (buffer != null) {
+			try {
+				IContentType contentType = buffer.getContentType();
+				if (contentType != null) {
+					this.resolvedContentTypes.add(contentType);
 				}
+			} catch (CoreException ex) {
+				GenericEditorPlugin.getDefault().getLog()
+						.log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex));
 			}
-			String fileName = getCurrentFileName(viewer);
-			if (fileName != null) {
-				Queue<IContentType> types = new LinkedList<>(
-						Arrays.asList(Platform.getContentTypeManager().findContentTypesFor(fileName)));
-				while (!types.isEmpty()) {
-					IContentType type = types.poll();
-					this.contentTypes.add(type);
-					IContentType parent = type.getBaseType();
-					if (parent != null) {
-						types.add(parent);
-					}
+		}
+		String fileName = getCurrentFileName(document);
+		if (fileName != null) {
+			Queue<IContentType> types = new LinkedList<>(
+					Arrays.asList(Platform.getContentTypeManager().findContentTypesFor(fileName)));
+			while (!types.isEmpty()) {
+				IContentType type = types.poll();
+				this.resolvedContentTypes.add(type);
+				IContentType parent = type.getBaseType();
+				if (parent != null) {
+					types.add(parent);
 				}
 			}
 		}
-		return this.contentTypes;
+		return this.resolvedContentTypes.isEmpty() ? fallbackContentTypes : resolvedContentTypes;
 	}
 
-	private static ITextFileBuffer getCurrentBuffer(ITextViewer viewer) {
-		IDocument viewerDocument = viewer.getDocument();
-		if (viewerDocument != null) {
-			return FileBuffers.getTextFileBufferManager().getTextFileBuffer(viewerDocument);
+	private static ITextFileBuffer getCurrentBuffer(IDocument document) {
+		if (document != null) {
+			return FileBuffers.getTextFileBufferManager().getTextFileBuffer(document);
 		}
 		return null;
 	}
 
-	private String getCurrentFileName(ITextViewer viewer) {
+	private String getCurrentFileName(IDocument document) {
 		String fileName = null;
 		if (this.editor != null) {
 			fileName = editor.getEditorInput().getName();
 		}
 		if (fileName == null) {
-			ITextFileBuffer buffer = getCurrentBuffer(viewer);
+			ITextFileBuffer buffer = getCurrentBuffer(document);
 			if (buffer != null) {
 				IPath path = buffer.getLocation();
 				if (path != null) {
@@ -145,7 +145,7 @@
 	@Override
 	public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
 		List<ITextHover> hovers = GenericEditorPlugin.getDefault().getHoverRegistry().getAvailableHovers(sourceViewer,
-				editor, getContentTypes(sourceViewer));
+				editor, getContentTypes(sourceViewer.getDocument()));
 		if (hovers == null || hovers.isEmpty()) {
 			return null;
 		} else if (hovers.size() == 1) {
@@ -161,7 +161,7 @@
 		ContentTypeRelatedExtensionTracker<IContentAssistProcessor> contentAssistProcessorTracker = new ContentTypeRelatedExtensionTracker<>(
 				GenericEditorPlugin.getDefault().getBundle().getBundleContext(), IContentAssistProcessor.class,
 				sourceViewer.getTextWidget().getDisplay());
-		Set<IContentType> types = getContentTypes(sourceViewer);
+		Set<IContentType> types = getContentTypes(sourceViewer.getDocument());
 		contentAssistant = new GenericEditorContentAssistant(contentAssistProcessorTracker,
 				registry.getContentAssistProcessors(sourceViewer, editor, types), types);
 		if (this.document != null) {
@@ -175,7 +175,7 @@
 	public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
 		PresentationReconcilerRegistry registry = GenericEditorPlugin.getDefault().getPresentationReconcilerRegistry();
 		List<IPresentationReconciler> reconciliers = registry.getPresentationReconcilers(sourceViewer, editor,
-				getContentTypes(sourceViewer));
+				getContentTypes(sourceViewer.getDocument()));
 		if (!reconciliers.isEmpty()) {
 			return reconciliers.get(0);
 		}
@@ -214,7 +214,7 @@
 		List<IQuickAssistProcessor> quickAssistProcessors = new ArrayList<>();
 		quickAssistProcessors.add(new MarkerResoltionQuickAssistProcessor());
 		quickAssistProcessors.addAll(GenericEditorPlugin.getDefault().getQuickAssistProcessorRegistry()
-				.getQuickAssistProcessors(sourceViewer, editor, getContentTypes(sourceViewer)));
+				.getQuickAssistProcessors(sourceViewer, editor, getContentTypes(sourceViewer.getDocument())));
 		CompositeQuickAssistProcessor compQuickAssistProcessor = new CompositeQuickAssistProcessor(
 				quickAssistProcessors);
 		quickAssistAssistant.setQuickAssistProcessor(compQuickAssistProcessor);
@@ -228,10 +228,11 @@
 	@Override
 	public IReconciler getReconciler(ISourceViewer sourceViewer) {
 		ReconcilerRegistry registry = GenericEditorPlugin.getDefault().getReconcilerRegistry();
-		List<IReconciler> reconcilers = registry.getReconcilers(sourceViewer, editor, getContentTypes(sourceViewer));
+		List<IReconciler> reconcilers = registry.getReconcilers(sourceViewer, editor,
+				getContentTypes(sourceViewer.getDocument()));
 		// Fill with highlight reconcilers
 		List<IReconciler> highlightReconcilers = registry.getHighlightReconcilers(sourceViewer, editor,
-				getContentTypes(sourceViewer));
+				getContentTypes(sourceViewer.getDocument()));
 		if (!highlightReconcilers.isEmpty()) {
 			reconcilers.addAll(highlightReconcilers);
 		} else {
@@ -239,7 +240,7 @@
 		}
 		// Fill with folding reconcilers
 		List<IReconciler> foldingReconcilers = registry.getFoldingReconcilers(sourceViewer, editor,
-				getContentTypes(sourceViewer));
+				getContentTypes(sourceViewer.getDocument()));
 		if (!foldingReconcilers.isEmpty()) {
 			reconcilers.addAll(foldingReconcilers);
 		} else {
@@ -256,7 +257,7 @@
 	public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
 		AutoEditStrategyRegistry registry = GenericEditorPlugin.getDefault().getAutoEditStrategyRegistry();
 		List<IAutoEditStrategy> editStrategies = registry.getAutoEditStrategies(sourceViewer, editor,
-				getContentTypes(sourceViewer));
+				getContentTypes(sourceViewer.getDocument()));
 		if (!editStrategies.isEmpty()) {
 			return editStrategies.toArray(new IAutoEditStrategy[editStrategies.size()]);
 		}
@@ -270,4 +271,13 @@
 		return targets;
 	}
 
+	/**
+	 * Set content-types that will be considered is no content-type can be deduced
+	 * from the document (eg document is not backed by a FileBuffer)
+	 * 
+	 * @param contentTypes
+	 */
+	public void setFallbackContentTypes(Set<IContentType> contentTypes) {
+		this.fallbackContentTypes = (contentTypes == null ? Set.of() : contentTypes);
+	}
 }
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/compare/GenericEditorMergeViewer.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/compare/GenericEditorMergeViewer.java
index 296876c..3eefa8b 100644
--- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/compare/GenericEditorMergeViewer.java
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/compare/GenericEditorMergeViewer.java
@@ -13,8 +13,12 @@
  *******************************************************************************/
 package org.eclipse.ui.internal.genericeditor.compare;
 
+import java.util.LinkedHashSet;
+import java.util.Set;
+
 import org.eclipse.compare.CompareConfiguration;
 import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
+import org.eclipse.core.runtime.content.IContentType;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.ITextInputListener;
 import org.eclipse.jface.text.TextViewer;
@@ -26,27 +30,38 @@
 
 public class GenericEditorMergeViewer extends TextMergeViewer {
 
+	private final Set<IContentType> fallbackContentTypes = new LinkedHashSet<>();
+
 	public GenericEditorMergeViewer(Composite parent, CompareConfiguration configuration) {
 		super(parent, configuration);
 	}
 
-	@Override protected SourceViewer createSourceViewer(Composite parent, int textOrientation) {
+	@Override
+	protected SourceViewer createSourceViewer(Composite parent, int textOrientation) {
 		SourceViewer res = super.createSourceViewer(parent, textOrientation);
 		res.addTextInputListener(new ITextInputListener() {
-			@Override public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+			@Override
+			public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+				fallbackContentTypes
+						.addAll(new ExtensionBasedTextViewerConfiguration(null, null).getContentTypes(newInput));
 				configureTextViewer(res);
 			}
 
-			@Override public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+			@Override
+			public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
 				// Nothing to do
 			}
 		});
 		return res;
 	}
 
-	@Override protected void configureTextViewer(TextViewer textViewer) {
+	@Override
+	protected void configureTextViewer(TextViewer textViewer) {
 		if (textViewer.getDocument() != null && textViewer instanceof ISourceViewer) {
-			((ISourceViewer) textViewer).configure(new ExtensionBasedTextViewerConfiguration(null, GenericEditorPlugin.getDefault().getPreferenceStore()));
+			ExtensionBasedTextViewerConfiguration configuration = new ExtensionBasedTextViewerConfiguration(null,
+					GenericEditorPlugin.getDefault().getPreferenceStore());
+			configuration.setFallbackContentTypes(fallbackContentTypes);
+			((ISourceViewer) textViewer).configure(configuration);
 		}
 	}