From bd18dd475d00da587a3ac7ca9fead1f386e21dc9 Mon Sep 17 00:00:00 2001 From: azerr Date: Thu, 27 Oct 2022 19:39:43 +0200 Subject: [PATCH] API to Track TextViewer install/uninstall in generic editor contribution Signed-off-by: azerr --- .../META-INF/MANIFEST.MF | 7 +- .../schema/foldingReconcilers.exsd | 28 ++++++++ .../schema/highlightReconcilers.exsd | 28 ++++++++ .../schema/reconcilers.exsd | 28 ++++++++ .../genericeditor/ITextViewerLifecycle.java | 38 ++++++++++ .../AutoEditStrategyRegistry.java | 15 ++-- .../CharacterPairMatcherRegistry.java | 19 ++--- .../ContentAssistProcessorRegistry.java | 58 +++++++++------ .../ExtensionBasedTextEditor.java | 37 +++++++++- ...ExtensionBasedTextViewerConfiguration.java | 50 +++++++++---- .../GenericContentTypeRelatedExtension.java | 25 ++++++- .../PresentationReconcilerRegistry.java | 31 ++++---- .../QuickAssistProcessorRegistry.java | 18 +++-- .../genericeditor/ReconcilerRegistry.java | 70 +++++++++++++------ .../hover/TextHoverRegistry.java | 6 +- 15 files changed, 357 insertions(+), 101 deletions(-) create mode 100644 org.eclipse.ui.genericeditor/src/org/eclipse/ui/genericeditor/ITextViewerLifecycle.java diff --git a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF index aeaf84dd6..660fbbcf9 100644 --- a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF +++ b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF @@ -16,11 +16,12 @@ Require-Bundle: org.eclipse.ui.workbench.texteditor;bundle-version="3.10.0", org.eclipse.core.resources;bundle-version="3.11.0", org.eclipse.core.expressions;bundle-version="3.6.0", org.eclipse.compare;resolution:=optional -Export-Package: org.eclipse.ui.internal.genericeditor;x-internal:=true, +Export-Package: org.eclipse.ui.genericeditor, + org.eclipse.ui.internal.genericeditor;x-internal:=true, + org.eclipse.ui.internal.genericeditor.compare;x-internal:=true, org.eclipse.ui.internal.genericeditor.hover;x-internal:=true, org.eclipse.ui.internal.genericeditor.markers;x-internal:=true, - org.eclipse.ui.internal.genericeditor.preferences;x-internal:=true, - org.eclipse.ui.internal.genericeditor.compare;x-internal:=true + org.eclipse.ui.internal.genericeditor.preferences;x-internal:=true Bundle-Activator: org.eclipse.ui.internal.genericeditor.GenericEditorPlugin Bundle-Localization: plugin Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.ui.genericeditor/schema/foldingReconcilers.exsd b/org.eclipse.ui.genericeditor/schema/foldingReconcilers.exsd index 70eb49da5..fd6a922a1 100644 --- a/org.eclipse.ui.genericeditor/schema/foldingReconcilers.exsd +++ b/org.eclipse.ui.genericeditor/schema/foldingReconcilers.exsd @@ -79,6 +79,34 @@ + + + + + + + + + The fully qualified class name implementing the interface <code>org.eclipse.jface.text.reconciler.IReconcilingStrategy</code> + + + + + + + + + + The target content-type for this extension. Content-types are defined as extension to the org.eclipse.core.contenttype.contentTypes extension point. + + + + + + + + + diff --git a/org.eclipse.ui.genericeditor/schema/highlightReconcilers.exsd b/org.eclipse.ui.genericeditor/schema/highlightReconcilers.exsd index a457bcf9a..a0c95bc28 100644 --- a/org.eclipse.ui.genericeditor/schema/highlightReconcilers.exsd +++ b/org.eclipse.ui.genericeditor/schema/highlightReconcilers.exsd @@ -77,6 +77,34 @@ + + + + + + + + + The fully qualified class name implementing the interface <code>org.eclipse.jface.text.reconciler.IReconcilingStrategy</code> + + + + + + + + + + The target content-type for this extension. Content-types are defined as extension to the org.eclipse.core.contenttype.contentTypes extension point. + + + + + + + + + diff --git a/org.eclipse.ui.genericeditor/schema/reconcilers.exsd b/org.eclipse.ui.genericeditor/schema/reconcilers.exsd index 508cf6c6d..3535398ea 100644 --- a/org.eclipse.ui.genericeditor/schema/reconcilers.exsd +++ b/org.eclipse.ui.genericeditor/schema/reconcilers.exsd @@ -77,6 +77,34 @@ + + + + + + + + + The fully qualified class name implementing the interface <code>org.eclipse.jface.text.reconciler.IReconcilingStrategy</code> + + + + + + + + + + The target content-type for this extension. Content-types are defined as extension to the org.eclipse.core.contenttype.contentTypes extension point. + + + + + + + + + diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/genericeditor/ITextViewerLifecycle.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/genericeditor/ITextViewerLifecycle.java new file mode 100644 index 000000000..7cab9a07e --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/genericeditor/ITextViewerLifecycle.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat Inc. 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: + * - Angelo ZERR (Red Hat Inc.) - initial implementation + *******************************************************************************/ +package org.eclipse.ui.genericeditor; + +import org.eclipse.jface.text.ITextViewer; + +/** + * {@link ITextViewer} lifecycle API to track install / uninstall of a given + * {@link ITextViewer}. + * + * @since 3.21 + * + */ +public interface ITextViewerLifecycle { + + /** + * Installs a text viewer. + * + * @param textViewer the text viewer + */ + void install(ITextViewer textViewer); + + /** + * Uninstalls the registered text viewer. + */ + void uninstall(); +} diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AutoEditStrategyRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AutoEditStrategyRegistry.java index 702aae2d8..49f111069 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AutoEditStrategyRegistry.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AutoEditStrategyRegistry.java @@ -27,6 +27,7 @@ import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.texteditor.ITextEditor; /** @@ -57,20 +58,22 @@ public AutoEditStrategyRegistry() { * @param sourceViewer * the source viewer we're hooking completion to. * @param editor the text editor + * @param textViewerLifecycles the list of text viewer lifecycle to fill * @param contentTypes * the content types of the document we're editing. * @return the list of {@link IAutoEditStrategy} contributed for at least * one of the content types. */ - public List getAutoEditStrategies(ISourceViewer sourceViewer, ITextEditor editor, Set contentTypes) { + public List getAutoEditStrategies(ISourceViewer sourceViewer, ITextEditor editor, + List textViewerLifecycles, Set contentTypes) { if (this.outOfSync) { sync(); } - return this.extensions.values().stream() - .filter(ext -> contentTypes.contains(ext.targetContentType)) - .filter(ext -> ext.matches(sourceViewer, editor)) - .sorted(new ContentTypeSpecializationComparator()) - .map(GenericContentTypeRelatedExtension::createDelegate) + return this.extensions.values().stream() // + .filter(ext -> contentTypes.contains(ext.targetContentType)) // + .filter(ext -> ext.matches(sourceViewer, editor)) // + .sorted(new ContentTypeSpecializationComparator()) // + .map(ext -> ext.createDelegate(textViewerLifecycles)) // .collect(Collectors.toList()); } diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/CharacterPairMatcherRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/CharacterPairMatcherRegistry.java index aa812826b..d3d65cc98 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/CharacterPairMatcherRegistry.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/CharacterPairMatcherRegistry.java @@ -24,6 +24,7 @@ import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.text.source.ICharacterPairMatcher; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.texteditor.ITextEditor; /** @@ -51,21 +52,23 @@ public CharacterPairMatcherRegistry() { * Get the contributed {@link IPresentationReconciliers}s that are relevant to * hook on source viewer according to document content types. * - * @param sourceViewer the source viewer we're hooking completion to. - * @param editor the text editor - * @param contentTypes the content types of the document we're editing. + * @param sourceViewer the source viewer we're hooking completion to. + * @param editor the text editor + * @param textViewerLifecycles the list of text viewer lifecycle to fill + * @param contentTypes the content types of the document we're editing. * @return the list of {@link ICharacterPairMatcher} contributed for at least * one of the content types. */ public List getCharacterPairMatchers(ISourceViewer sourceViewer, ITextEditor editor, - Set contentTypes) { + List textViewerLifecycles, Set contentTypes) { if (this.outOfSync) { sync(); } - return this.extensions.values().stream().filter(ext -> contentTypes.contains(ext.targetContentType)) - .filter(ext -> ext.matches(sourceViewer, editor)) - .sorted(new ContentTypeSpecializationComparator()) - .map(GenericContentTypeRelatedExtension::createDelegate) + return this.extensions.values().stream() // + .filter(ext -> contentTypes.contains(ext.targetContentType)) // + .filter(ext -> ext.matches(sourceViewer, editor)) // + .sorted(new ContentTypeSpecializationComparator()) // + .map(ext -> ext.createDelegate(textViewerLifecycles)) // .collect(Collectors.toList()); } diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java index 3592fb820..a3213032f 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java @@ -35,11 +35,13 @@ import org.eclipse.jface.text.contentassist.IContextInformationValidator; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.texteditor.ITextEditor; /** - * A registry of content assist processors provided by extension org.eclipse.ui.genericeditor.contentAssistProcessors. - * Those extensions are specific to a given {@link IContentType}. + * A registry of content assist processors provided by extension + * org.eclipse.ui.genericeditor.contentAssistProcessors. Those + * extensions are specific to a given {@link IContentType}. * * @since 1.0 */ @@ -57,19 +59,22 @@ public ContentAssistProcessorDelegate(IContentAssistProcessor delegate, IContent } /** - * @return whether the referenced contribution should contribute to the current editor. + * @return whether the referenced contribution should contribute to the current + * editor. */ public boolean isActive(ITextViewer viewer) { String fileName = null; if (viewer != null && viewer.getDocument() != null) { - IPath location = FileBuffers.getTextFileBufferManager().getTextFileBuffer(viewer.getDocument()).getLocation(); - fileName = location.segment(location.segmentCount() - 1); + IPath location = FileBuffers.getTextFileBufferManager().getTextFileBuffer(viewer.getDocument()) + .getLocation(); + fileName = location.segment(location.segmentCount() - 1); } if (fileName == null) { - fileName = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getEditorInput().getName(); + fileName = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor() + .getEditorInput().getName(); } if (fileName != null) { - IContentTypeManager contentTypeManager= Platform.getContentTypeManager(); + IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); for (IContentType currentContentType : contentTypeManager.findContentTypesFor(fileName)) { if (currentContentType.isKindOf(targetContentType)) { return true; @@ -139,34 +144,41 @@ public ContentAssistProcessorRegistry() { } /** - * Get the contributed {@link IContentAssistProcessor}s that are relevant to hook on source viewer according - * to document content types. - * @param sourceViewer the source viewer we're hooking completion to. - * @param editor the text editor - * @param contentTypes the content types of the document we're editing. - * @return the list of {@link IContentAssistProcessor} contributed for at least one of the content types. + * Get the contributed {@link IContentAssistProcessor}s that are relevant to + * hook on source viewer according to document content types. + * + * @param sourceViewer the source viewer we're hooking completion to. + * @param editor the text editor + * @param textViewerLifecycles the list of text viewer lifecycle to fill + * @param contentTypes the content types of the document we're editing. + * @return the list of {@link IContentAssistProcessor} contributed for at least + * one of the content types. */ - public List getContentAssistProcessors(ISourceViewer sourceViewer, ITextEditor editor, Set contentTypes) { + public List getContentAssistProcessors(ISourceViewer sourceViewer, ITextEditor editor, + List textViewerLifecycles, Set contentTypes) { if (this.outOfSync) { sync(); } - return this.extensions.values().stream() - .filter(ext -> contentTypes.contains(ext.targetContentType)) - .filter(ext -> ext.matches(sourceViewer, editor)) - .sorted(new ContentTypeSpecializationComparator()) - .map(GenericContentTypeRelatedExtension::createDelegate) - .collect(Collectors.toList()); + return this.extensions.values().stream() // + .filter(ext -> contentTypes.contains(ext.targetContentType)) // + .filter(ext -> ext.matches(sourceViewer, editor)) // + .sorted(new ContentTypeSpecializationComparator()) // + .map(ext -> ext.createDelegate(textViewerLifecycles)) // + .collect(Collectors.toList()); } private void sync() { Set toRemoveExtensions = new HashSet<>(this.extensions.keySet()); - for (IConfigurationElement extension : Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID)) { + for (IConfigurationElement extension : Platform.getExtensionRegistry() + .getConfigurationElementsFor(EXTENSION_POINT_ID)) { toRemoveExtensions.remove(extension); if (!this.extensions.containsKey(extension)) { try { - this.extensions.put(extension, new GenericContentTypeRelatedExtension(extension)); + this.extensions.put(extension, + new GenericContentTypeRelatedExtension(extension)); } catch (Exception ex) { - GenericEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); + GenericEditorPlugin.getDefault().getLog() + .log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); } } } 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 5cea4e45a..5fecdee55 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 @@ -16,6 +16,7 @@ *******************************************************************************/ package org.eclipse.ui.internal.genericeditor; +import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.Assert; @@ -34,6 +35,7 @@ import org.eclipse.jface.text.source.ICharacterPairMatcher; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.jface.text.source.projection.ProjectionSupport; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.swt.graphics.Image; @@ -41,6 +43,7 @@ import org.eclipse.ui.IEditorInput; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.TextEditor; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.internal.genericeditor.preferences.GenericEditorPreferenceConstants; import org.eclipse.ui.texteditor.ChainedPreferenceStore; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; @@ -64,11 +67,13 @@ public class ExtensionBasedTextEditor extends TextEditor { private ExtensionBasedTextViewerConfiguration configuration; private Image contentTypeImage; private ImageDescriptor contentTypeImageDescripter; + private final List textViewerLifecycles; /** * */ public ExtensionBasedTextEditor() { + this.textViewerLifecycles = new ArrayList<>(); configuration = new ExtensionBasedTextViewerConfiguration(this, getPreferenceStore()); setSourceViewerConfiguration(configuration); } @@ -95,6 +100,18 @@ protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler rule ProjectionViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles) { + @Override + public void configure(SourceViewerConfiguration configuration) { + super.configure(configuration); + installContributionViewer(this); + } + + @Override + public void unconfigure() { + super.unconfigure(); + uninstallContributionViewer(); + } + @Override public void doOperation(int operation) { if (HyperlinkManager.OPEN_HYPERLINK == operation) { @@ -158,9 +175,22 @@ else if (stateMask == -1 && activeHyperlinkStateMask != fHyperlinkStateMask) SourceViewerDecorationSupport support = getSourceViewerDecorationSupport(viewer); configureCharacterPairMatcher(viewer, support); + return viewer; } + private void installContributionViewer(ProjectionViewer viewer) { + for (ITextViewerLifecycle contribution : textViewerLifecycles) { + contribution.install(viewer); + } + } + + private void uninstallContributionViewer() { + for (ITextViewerLifecycle contribution : textViewerLifecycles) { + contribution.uninstall(); + } + } + @Override public void createPartControl(Composite parent) { super.createPartControl(parent); @@ -188,7 +218,8 @@ protected void initializeEditor() { */ private void configureCharacterPairMatcher(ISourceViewer viewer, SourceViewerDecorationSupport support) { List matchers = GenericEditorPlugin.getDefault().getCharacterPairMatcherRegistry() - .getCharacterPairMatchers(viewer, this, configuration.getContentTypes(viewer.getDocument())); + .getCharacterPairMatchers(viewer, this, textViewerLifecycles, + configuration.getContentTypes(viewer.getDocument())); if (!matchers.isEmpty()) { ICharacterPairMatcher matcher = matchers.get(0); support.setCharacterPairMatcher(matcher); @@ -226,4 +257,8 @@ public void dispose() { } super.dispose(); } + + List getTextViewerLifecycles() { + return textViewerLifecycles; + } } 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 2c14aa78c..145e4e0a8 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 @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -65,9 +66,12 @@ import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; import org.eclipse.jface.text.quickassist.QuickAssistAssistant; import org.eclipse.jface.text.reconciler.IReconciler; +import org.eclipse.jface.text.reconciler.IReconcilingStrategy; +import org.eclipse.jface.text.reconciler.Reconciler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.internal.editors.text.EditorsPlugin; import org.eclipse.ui.internal.genericeditor.folding.DefaultFoldingReconciler; import org.eclipse.ui.internal.genericeditor.hover.CompositeInformationControlCreator; @@ -93,6 +97,8 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewe private GenericEditorContentAssistant contentAssistant; + private final List textViewerLifecycles; + /** * * @param editor the editor we're creating. @@ -101,6 +107,8 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewe public ExtensionBasedTextViewerConfiguration(ITextEditor editor, IPreferenceStore preferenceStore) { super(preferenceStore); this.editor = editor; + this.textViewerLifecycles = editor != null ? ((ExtensionBasedTextEditor) editor).getTextViewerLifecycles() + : Collections.emptyList(); } public Set getContentTypes(IDocument document) { @@ -163,7 +171,7 @@ private String getCurrentFileName(IDocument document) { @Override public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { List hovers = GenericEditorPlugin.getDefault().getHoverRegistry().getAvailableHovers(sourceViewer, - editor, getContentTypes(sourceViewer.getDocument())); + editor, textViewerLifecycles, getContentTypes(sourceViewer.getDocument())); if (hovers == null || hovers.isEmpty()) { return null; } else if (hovers.size() == 1) { @@ -181,7 +189,7 @@ public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { sourceViewer.getTextWidget().getDisplay()); Set types = getContentTypes(sourceViewer.getDocument()); contentAssistant = new GenericEditorContentAssistant(contentAssistProcessorTracker, - registry.getContentAssistProcessors(sourceViewer, editor, types), types); + registry.getContentAssistProcessors(sourceViewer, editor, textViewerLifecycles, types), types); if (this.document != null) { associateTokenContentTypes(this.document); } @@ -193,7 +201,7 @@ public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { PresentationReconcilerRegistry registry = GenericEditorPlugin.getDefault().getPresentationReconcilerRegistry(); List reconciliers = registry.getPresentationReconcilers(sourceViewer, editor, - getContentTypes(sourceViewer.getDocument())); + textViewerLifecycles, getContentTypes(sourceViewer.getDocument())); if (!reconciliers.isEmpty()) { return reconciliers.get(0); } @@ -231,8 +239,9 @@ public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) QuickAssistAssistant quickAssistAssistant = new QuickAssistAssistant(); List quickAssistProcessors = new ArrayList<>(); quickAssistProcessors.add(new MarkerResoltionQuickAssistProcessor()); - quickAssistProcessors.addAll(GenericEditorPlugin.getDefault().getQuickAssistProcessorRegistry() - .getQuickAssistProcessors(sourceViewer, editor, getContentTypes(sourceViewer.getDocument()))); + quickAssistProcessors + .addAll(GenericEditorPlugin.getDefault().getQuickAssistProcessorRegistry().getQuickAssistProcessors( + sourceViewer, editor, textViewerLifecycles, getContentTypes(sourceViewer.getDocument()))); CompositeQuickAssistProcessor compQuickAssistProcessor = new CompositeQuickAssistProcessor( quickAssistProcessors); quickAssistAssistant.setQuickAssistProcessor(compQuickAssistProcessor); @@ -246,24 +255,39 @@ public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) @Override public IReconciler getReconciler(ISourceViewer sourceViewer) { ReconcilerRegistry registry = GenericEditorPlugin.getDefault().getReconcilerRegistry(); - List reconcilers = registry.getReconcilers(sourceViewer, editor, - getContentTypes(sourceViewer.getDocument())); + List reconcilingStrategies = new ArrayList<>(); + List reconcilers = registry.getReconcilers(sourceViewer, editor, textViewerLifecycles, + reconcilingStrategies, getContentTypes(sourceViewer.getDocument())); + // Fill with highlight reconcilers + List highlightReconcilingStrategies = new ArrayList<>(); List highlightReconcilers = registry.getHighlightReconcilers(sourceViewer, editor, - getContentTypes(sourceViewer.getDocument())); + textViewerLifecycles, highlightReconcilingStrategies, getContentTypes(sourceViewer.getDocument())); if (!highlightReconcilers.isEmpty()) { reconcilers.addAll(highlightReconcilers); - } else { + } else if (highlightReconcilingStrategies.isEmpty()) { reconcilers.add(new DefaultWordHighlightReconciler()); } + reconcilingStrategies.addAll(highlightReconcilingStrategies); + // Fill with folding reconcilers + List foldingReconcilingStrategies = new ArrayList<>(); List foldingReconcilers = registry.getFoldingReconcilers(sourceViewer, editor, - getContentTypes(sourceViewer.getDocument())); + textViewerLifecycles, foldingReconcilingStrategies, getContentTypes(sourceViewer.getDocument())); if (!foldingReconcilers.isEmpty()) { reconcilers.addAll(foldingReconcilers); - } else { + } else if (foldingReconcilingStrategies.isEmpty()) { reconcilers.add(new DefaultFoldingReconciler()); } + reconcilingStrategies.addAll(foldingReconcilingStrategies); + + if (!reconcilingStrategies.isEmpty()) { + // Create the main Reconciler of the generic editor + Reconciler reconciler = new Reconciler(); + reconciler.setReconcilingStrategy(new CompositeReconcilerStrategy(reconcilingStrategies), + IDocument.DEFAULT_CONTENT_TYPE); + reconcilers.add(0, reconciler); + } if (!reconcilers.isEmpty()) { return new CompositeReconciler(reconcilers); @@ -275,7 +299,7 @@ public IReconciler getReconciler(ISourceViewer sourceViewer) { public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) { AutoEditStrategyRegistry registry = GenericEditorPlugin.getDefault().getAutoEditStrategyRegistry(); List editStrategies = registry.getAutoEditStrategies(sourceViewer, editor, - getContentTypes(sourceViewer.getDocument())); + textViewerLifecycles, getContentTypes(sourceViewer.getDocument())); if (!editStrategies.isEmpty()) { return editStrategies.toArray(new IAutoEditStrategy[editStrategies.size()]); } @@ -293,7 +317,7 @@ protected Map getHyperlinkDetectorTargets(ISourceViewer sour public IInformationPresenter getInformationPresenter(ISourceViewer sourceViewer) { // Register information provider List hovers = GenericEditorPlugin.getDefault().getHoverRegistry().getAvailableHovers(sourceViewer, - editor, getContentTypes(sourceViewer.getDocument())); + editor, textViewerLifecycles, getContentTypes(sourceViewer.getDocument())); InformationPresenter presenter = new InformationPresenter(new CompositeInformationControlCreator(hovers)); // By default the InformationPresented is set to take the focus when visible, diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericContentTypeRelatedExtension.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericContentTypeRelatedExtension.java index 558655a91..11aa57ceb 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericContentTypeRelatedExtension.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericContentTypeRelatedExtension.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.ui.internal.genericeditor; +import java.util.List; + import org.eclipse.core.expressions.ElementHandler; import org.eclipse.core.expressions.EvaluationContext; import org.eclipse.core.expressions.EvaluationResult; @@ -26,6 +28,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.texteditor.ITextEditor; /** @@ -53,10 +56,19 @@ public GenericContentTypeRelatedExtension(IConfigurationElement element) throws this.enabledWhen = buildEnabledWhen(element); } + public T createDelegate(List textViewerLifecycles) { + T delegateInstance = createDelegateWithoutTypeCheck(textViewerLifecycles); + return delegateInstance; + } + @SuppressWarnings("unchecked") - public T createDelegate() { + public E createDelegateWithoutTypeCheck(List textViewerLifecycles) { try { - return (T) extension.createExecutableExtension(CLASS_ATTRIBUTE); + E delegateInstance = (E) extension.createExecutableExtension(CLASS_ATTRIBUTE); + if (delegateInstance instanceof ITextViewerLifecycle) { + textViewerLifecycles.add((ITextViewerLifecycle) delegateInstance); + } + return delegateInstance; } catch (CoreException e) { GenericEditorPlugin.getDefault().getLog() .log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, e.getMessage(), e)); @@ -121,4 +133,13 @@ public boolean matches(ISourceViewer viewer, ITextEditor editor) { return false; } } + + /** + * Returns the name of the contribution. + * + * @return the name of the contribution. + */ + public String getContributionName() { + return extension.getName(); + } } diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/PresentationReconcilerRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/PresentationReconcilerRegistry.java index 317d354e6..428a988e7 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/PresentationReconcilerRegistry.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/PresentationReconcilerRegistry.java @@ -27,6 +27,7 @@ import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.texteditor.ITextEditor; /** @@ -50,23 +51,27 @@ public PresentationReconcilerRegistry() { } /** - * Get the contributed {@link IPresentationReconciliers}s that are relevant to hook on source viewer according - * to document content types. - * @param sourceViewer the source viewer we're hooking completion to. - * @param editor the text editor - * @param contentTypes the content types of the document we're editing. - * @return the list of {@link IPresentationReconciler} contributed for at least one of the content types. + * Get the contributed {@link IPresentationReconciliers}s that are relevant to + * hook on source viewer according to document content types. + * + * @param sourceViewer the source viewer we're hooking completion to. + * @param editor the text editor + * @param textViewerLifecycles the list of text viewer lifecycle to fill + * @param contentTypes the content types of the document we're editing. + * @return the list of {@link IPresentationReconciler} contributed for at least + * one of the content types. */ - public List getPresentationReconcilers(ISourceViewer sourceViewer, ITextEditor editor, Set contentTypes) { + public List getPresentationReconcilers(ISourceViewer sourceViewer, ITextEditor editor, + List textViewerLifecycles, Set contentTypes) { if (this.outOfSync) { sync(); } - return this.extensions.values().stream() - .filter(ext -> contentTypes.contains(ext.targetContentType)) - .filter(ext -> ext.matches(sourceViewer, editor)) - .sorted(new ContentTypeSpecializationComparator()) - .map(GenericContentTypeRelatedExtension::createDelegate) - .collect(Collectors.toList()); + return this.extensions.values().stream() // + .filter(ext -> contentTypes.contains(ext.targetContentType)) // + .filter(ext -> ext.matches(sourceViewer, editor)) // + .sorted(new ContentTypeSpecializationComparator()) // + .map(ext -> ext.createDelegate(textViewerLifecycles)) // + .collect(Collectors.toList()); } private void sync() { diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/QuickAssistProcessorRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/QuickAssistProcessorRegistry.java index 66b7ded8b..f458174f5 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/QuickAssistProcessorRegistry.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/QuickAssistProcessorRegistry.java @@ -27,6 +27,7 @@ import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.texteditor.ITextEditor; public class QuickAssistProcessorRegistry { @@ -45,19 +46,22 @@ public QuickAssistProcessorRegistry() { /** * Get the contributed {@link IQuickAssistProcessor}s * + * @param sourceViewer the source viewer we're hooking completion to. + * @param editor the text editor + * @param textViewerLifecycles the list of text viewer lifecycle to fill + * * @return the list of {@link IQuickAssistProcessor} contributed */ - public List getQuickAssistProcessors(ISourceViewer sourceViewer, ITextEditor editor, - Set contentTypes) { + List textViewerLifecycles, Set contentTypes) { if (this.outOfSync) { sync(); } - return this.extensions.values().stream() - .filter(ext -> contentTypes.contains(ext.targetContentType)) - .filter(ext -> ext.matches(sourceViewer, editor)) - .sorted(new ContentTypeSpecializationComparator()) - .map(GenericContentTypeRelatedExtension::createDelegate) + return this.extensions.values().stream() // + .filter(ext -> contentTypes.contains(ext.targetContentType)) // + .filter(ext -> ext.matches(sourceViewer, editor)) // + .sorted(new ContentTypeSpecializationComparator()) // + .map(ext -> ext.createDelegate(textViewerLifecycles)) // .collect(Collectors.toList()); } diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ReconcilerRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ReconcilerRegistry.java index a8e11b99f..4291b7c96 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ReconcilerRegistry.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ReconcilerRegistry.java @@ -13,6 +13,7 @@ *******************************************************************************/ package org.eclipse.ui.internal.genericeditor; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -26,7 +27,9 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.text.reconciler.IReconciler; +import org.eclipse.jface.text.reconciler.IReconcilingStrategy; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.texteditor.ITextEditor; /** @@ -40,8 +43,11 @@ public class ReconcilerRegistry { private static final String EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".reconcilers"; //$NON-NLS-1$ + private static final String RECONCILING_STRATEGY_ELT_NAME = "reconcilingStrategy"; //$NON-NLS-1$ private static final String HIGHLIGHT_EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".highlightReconcilers"; //$NON-NLS-1$ + private static final String HIGHLIGHT_RECONCILING_STRATEGY_ELT_NAME = "highlightReconcilingStrategy"; //$NON-NLS-1$ private static final String FOLDING_EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".foldingReconcilers"; //$NON-NLS-1$ + private static final String FOLDING_RECONCILING_STRATEGY_ELT_NAME = "foldingReconcilingStrategy"; //$NON-NLS-1$ private Map> extensions = new HashMap<>(); private Map> highlightExtensions = new HashMap<>(); @@ -65,71 +71,89 @@ public ReconcilerRegistry() { * Get the contributed {@link IReconciliers}s that are relevant to hook on * source viewer according to document content types. * - * @param sourceViewer the source viewer we're hooking completion to. - * @param editor the text editor - * @param contentTypes the content types of the document we're editing. + * @param sourceViewer the source viewer we're hooking completion to. + * @param editor the text editor + * @param textViewerLifecycles the list of text viewer lifecycle to fill + * @param contentTypes the content types of the document we're editing. * @return the list of {@link IReconciler} contributed for at least one of the * content types, sorted by most generic content type to most specific. */ public List getReconcilers(ISourceViewer sourceViewer, ITextEditor editor, + List textViewerLifecycles, List reconcilingStrategies, Set contentTypes) { if (this.outOfSync) { sync(); } - return this.extensions.values().stream() // - .filter(ext -> contentTypes.contains(ext.targetContentType)) // - .filter(ext -> ext.matches(sourceViewer, editor)) // - .sorted(new ContentTypeSpecializationComparator().reversed()) // - .map(GenericContentTypeRelatedExtension::createDelegate) // - .collect(Collectors.toList()); + return getReconcilers(sourceViewer, editor, textViewerLifecycles, reconcilingStrategies, contentTypes, + RECONCILING_STRATEGY_ELT_NAME, this.extensions); } /** * Get the contributed highlight {@link IReconciliers}s that are relevant to * hook on source viewer according to document content types. * - * @param sourceViewer the source viewer we're hooking completion to. - * @param editor the text editor - * @param contentTypes the content types of the document we're editing. + * @param sourceViewer the source viewer we're hooking completion to. + * @param editor the text editor + * @param textViewerLifecycles the list of text viewer lifecycle to fill + * @param contentTypes the content types of the document we're editing. * @return the list of highlight {@link IReconciler}s contributed for at least * one of the content types, sorted by most generic content type to most * specific. */ public List getHighlightReconcilers(ISourceViewer sourceViewer, ITextEditor editor, + List textViewerLifecycles, List reconcilingStrategies, Set contentTypes) { if (this.highlightOutOfSync) { syncHighlight(); } - return this.highlightExtensions.values().stream() // - .filter(ext -> contentTypes.contains(ext.targetContentType)) // - .filter(ext -> ext.matches(sourceViewer, editor)) // - .sorted(new ContentTypeSpecializationComparator().reversed()) // - .map(GenericContentTypeRelatedExtension::createDelegate) // - .collect(Collectors.toList()); + return getReconcilers(sourceViewer, editor, textViewerLifecycles, reconcilingStrategies, contentTypes, + HIGHLIGHT_RECONCILING_STRATEGY_ELT_NAME, this.highlightExtensions); } /** * Get the contributed folding {@link IReconciliers}s that are relevant to hook * on source viewer according to document content types. * - * @param sourceViewer the source viewer we're hooking completion to. - * @param editor the text editor - * @param contentTypes the content types of the document we're editing. + * @param sourceViewer the source viewer we're hooking completion to. + * @param editor the text editor + * @param textViewerLifecycles the list of text viewer lifecycle to fill + * @param contentTypes the content types of the document we're editing. * @return the list of folding {@link IReconciler}s contributed for at least one * of the content types, sorted by most generic content type to most * specific. */ public List getFoldingReconcilers(ISourceViewer sourceViewer, ITextEditor editor, + List textViewerLifecycles, List reconcilingStrategies, Set contentTypes) { if (this.foldingOutOfSync) { syncFolding(); } - return this.foldingExtensions.values().stream() // + return getReconcilers(sourceViewer, editor, textViewerLifecycles, reconcilingStrategies, contentTypes, + FOLDING_RECONCILING_STRATEGY_ELT_NAME, this.foldingExtensions); + } + + private static List getReconcilers(ISourceViewer sourceViewer, ITextEditor editor, + List textViewerLifecycles, List reconcilingStrategies, + Set contentTypes, String contributionName, + Map> extensionsMap) { + List reconcilers = new ArrayList<>(); + List> extensions = extensionsMap.values().stream() // .filter(ext -> contentTypes.contains(ext.targetContentType)) // .filter(ext -> ext.matches(sourceViewer, editor)) // .sorted(new ContentTypeSpecializationComparator().reversed()) // - .map(GenericContentTypeRelatedExtension::createDelegate) // .collect(Collectors.toList()); + for (GenericContentTypeRelatedExtension ext : extensions) { + if (contributionName.equals(ext.getContributionName())) { + IReconcilingStrategy reconcilingStrategy = ext.createDelegateWithoutTypeCheck(textViewerLifecycles); + reconcilingStrategies.add(reconcilingStrategy); + } else { + IReconciler reconciler = ext.createDelegate(textViewerLifecycles); + if (reconciler != null) { + reconcilers.add(reconciler); + } + } + } + return reconcilers; } private void sync() { diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/hover/TextHoverRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/hover/TextHoverRegistry.java index 1d30371d8..7c8654bf4 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/hover/TextHoverRegistry.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/hover/TextHoverRegistry.java @@ -31,6 +31,7 @@ import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.genericeditor.ITextViewerLifecycle; import org.eclipse.ui.internal.genericeditor.GenericContentTypeRelatedExtension; import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; import org.eclipse.ui.texteditor.ITextEditor; @@ -89,7 +90,8 @@ public TextHoverRegistry(IPreferenceStore preferenceStore) { Platform.getExtensionRegistry().addRegistryChangeListener(event -> outOfSync = true, EXTENSION_POINT_ID); } - public List getAvailableHovers(ISourceViewer sourceViewer, ITextEditor editor, Set contentTypes) { + public List getAvailableHovers(ISourceViewer sourceViewer, ITextEditor editor, + List textViewerLifecycles, Set contentTypes) { if (this.outOfSync) { sync(); } @@ -97,7 +99,7 @@ public List getAvailableHovers(ISourceViewer sourceViewer, ITextEdit .filter(ext -> contentTypes.contains(ext.targetContentType)) .filter(ext -> ext.matches(sourceViewer, editor)) // don't sort in the stream as the initial structure is already sorted by isAfter/isBefore - .map(GenericContentTypeRelatedExtension::createDelegate) + .map(ext -> ext.createDelegate(textViewerLifecycles)) .collect(Collectors.toList()); }