From 34bbf1bbf19611cac2060f4719ac4c1e731ad851 Mon Sep 17 00:00:00 2001 From: azerr Date: Thu, 27 Oct 2022 19:39:43 +0200 Subject: [PATCH] Introduce ITextViewerLifecycle API Signed-off-by: azerr --- .../jface/text/ITextViewerLifecycle.java | 35 ++++++++++ .../text/contentassist/IContentAssistant.java | 5 +- .../text/hyperlink/IHyperlinkPresenter.java | 5 +- .../information/IInformationPresenter.java | 5 +- .../presentation/IPresentationReconciler.java | 5 +- .../jface/text/reconciler/IReconciler.java | 5 +- .../schema/reconcilers.exsd | 28 ++++++++ .../AutoEditStrategyRegistry.java | 15 ++-- .../CharacterPairMatcherRegistry.java | 19 +++--- .../ContentAssistProcessorRegistry.java | 58 +++++++++------- .../ExtensionBasedTextEditor.java | 37 +++++++++- ...ExtensionBasedTextViewerConfiguration.java | 27 +++++--- .../GenericContentTypeRelatedExtension.java | 25 ++++++- .../PresentationReconcilerRegistry.java | 31 +++++---- .../QuickAssistProcessorRegistry.java | 18 +++-- .../genericeditor/ReconcilerRegistry.java | 68 ++++++++++++++----- .../hover/TextHoverRegistry.java | 6 +- 17 files changed, 299 insertions(+), 93 deletions(-) create mode 100644 org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerLifecycle.java diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerLifecycle.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerLifecycle.java new file mode 100644 index 000000000..bcd57030c --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerLifecycle.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.jface.text; + +/** + * {@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.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.java index f6954cda6..a6f76494a 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.java @@ -14,6 +14,7 @@ package org.eclipse.jface.text.contentassist; import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerLifecycle; /** @@ -76,7 +77,7 @@ * @see org.eclipse.jface.text.ITextViewer * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor */ - public interface IContentAssistant { +public interface IContentAssistant extends ITextViewerLifecycle { //------ proposal popup orientation styles ------------ /** The context info list will overlay the list of completion proposals. */ @@ -98,12 +99,14 @@ public interface IContentAssistant { * * @param textViewer the text viewer on which content assist will work */ + @Override void install(ITextViewer textViewer); /** * Uninstalls content assist support from the text viewer it has * previously be installed on. */ + @Override void uninstall(); /** diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkPresenter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkPresenter.java index f926faccd..5ea7d1bfb 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkPresenter.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkPresenter.java @@ -14,6 +14,7 @@ package org.eclipse.jface.text.hyperlink; import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerLifecycle; /** @@ -39,7 +40,7 @@ * @see IHyperlinkPresenterExtension2 * @since 3.1 */ -public interface IHyperlinkPresenter { +public interface IHyperlinkPresenter extends ITextViewerLifecycle { /** * Tells whether this presenter is able to handle @@ -73,10 +74,12 @@ public interface IHyperlinkPresenter { * * @param textViewer the text viewer */ + @Override void install(ITextViewer textViewer); /** * Uninstalls this hyperlink presenter. */ + @Override void uninstall(); } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.java index 2874e9ae1..263aae15e 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.java @@ -14,6 +14,7 @@ package org.eclipse.jface.text.information; import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerLifecycle; /** @@ -46,7 +47,7 @@ * @see org.eclipse.jface.text.information.IInformationProvider * @since 2.0 */ -public interface IInformationPresenter { +public interface IInformationPresenter extends ITextViewerLifecycle { /** * Installs the information presenter on the given text viewer. After this method has been @@ -55,12 +56,14 @@ public interface IInformationPresenter { * * @param textViewer the viewer on which the presenter is installed */ + @Override void install(ITextViewer textViewer); /** * Removes the information presenter from the text viewer it has previously been * installed on. */ + @Override void uninstall(); /** diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.java index 222233ed5..03338a60d 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.java @@ -15,6 +15,7 @@ import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerLifecycle; /** @@ -55,7 +56,7 @@ * @see org.eclipse.jface.text.presentation.IPresentationRepairer * @see org.eclipse.jface.text.TextPresentation */ -public interface IPresentationReconciler { +public interface IPresentationReconciler extends ITextViewerLifecycle { /** * Installs this presentation reconciler on the given text viewer. After @@ -71,12 +72,14 @@ public interface IPresentationReconciler { * @param viewer the viewer on which this presentation reconciler is * installed */ + @Override void install(ITextViewer viewer); /** * Removes the reconciler from the text viewer it has previously been * installed on. */ + @Override void uninstall(); /** diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.java index fc3fa2c32..06419f15c 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.java @@ -14,6 +14,7 @@ package org.eclipse.jface.text.reconciler; import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerLifecycle; /** @@ -47,7 +48,7 @@ * @see ITextViewer * @see IReconcilingStrategy */ -public interface IReconciler { +public interface IReconciler extends ITextViewerLifecycle { /** * Installs the reconciler on the given text viewer. After this method has been @@ -56,12 +57,14 @@ public interface IReconciler { * * @param textViewer the viewer on which the reconciler is installed */ + @Override void install(ITextViewer textViewer); /** * Removes the reconciler from the text viewer it has * previously been installed on. */ + @Override void uninstall(); /** 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/internal/genericeditor/AutoEditStrategyRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AutoEditStrategyRegistry.java index 702aae2d8..b8454159b 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 @@ -26,6 +26,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.text.IAutoEditStrategy; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.source.ISourceViewer; 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..99f56b71f 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 @@ -22,6 +22,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.source.ICharacterPairMatcher; import org.eclipse.jface.text.source.ISourceViewer; 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..bd5146d5b 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 @@ -29,6 +29,7 @@ import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContextInformation; @@ -38,8 +39,9 @@ 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..6e8e2596f 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; @@ -26,6 +27,7 @@ import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.hyperlink.HyperlinkManager; import org.eclipse.jface.text.hyperlink.IHyperlink; @@ -34,6 +36,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; @@ -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..a1f6ae1b9 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; @@ -52,6 +53,7 @@ import org.eclipse.jface.text.ITextHoverExtension; import org.eclipse.jface.text.ITextHoverExtension2; import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContentAssistant; @@ -93,6 +95,8 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewe private GenericEditorContentAssistant contentAssistant; + private final List textViewerLifecycles; + /** * * @param editor the editor we're creating. @@ -101,6 +105,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 +169,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 +187,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 +199,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 +237,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,11 +253,11 @@ public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) @Override public IReconciler getReconciler(ISourceViewer sourceViewer) { ReconcilerRegistry registry = GenericEditorPlugin.getDefault().getReconcilerRegistry(); - List reconcilers = registry.getReconcilers(sourceViewer, editor, + List reconcilers = registry.getReconcilers(sourceViewer, editor, textViewerLifecycles, getContentTypes(sourceViewer.getDocument())); // Fill with highlight reconcilers List highlightReconcilers = registry.getHighlightReconcilers(sourceViewer, editor, - getContentTypes(sourceViewer.getDocument())); + textViewerLifecycles, getContentTypes(sourceViewer.getDocument())); if (!highlightReconcilers.isEmpty()) { reconcilers.addAll(highlightReconcilers); } else { @@ -258,7 +265,7 @@ public IReconciler getReconciler(ISourceViewer sourceViewer) { } // Fill with folding reconcilers List foldingReconcilers = registry.getFoldingReconcilers(sourceViewer, editor, - getContentTypes(sourceViewer.getDocument())); + textViewerLifecycles, getContentTypes(sourceViewer.getDocument())); if (!foldingReconcilers.isEmpty()) { reconcilers.addAll(foldingReconcilers); } else { @@ -275,7 +282,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 +300,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..fa5e2e010 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; @@ -25,6 +27,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.source.ISourceViewer; 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..d9d0855e8 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 @@ -25,6 +25,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.source.ISourceViewer; 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..6bfe9a6af 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 @@ -25,6 +25,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.ui.texteditor.ITextEditor; @@ -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..0e8c89f5f 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; @@ -25,7 +26,11 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewerLifecycle; 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.ui.texteditor.ITextEditor; @@ -40,6 +45,7 @@ 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 FOLDING_EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".foldingReconcilers"; //$NON-NLS-1$ @@ -65,38 +71,67 @@ 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, - Set contentTypes) { + List textViewerLifecycles, Set contentTypes) { if (this.outOfSync) { sync(); } - return this.extensions.values().stream() // + + List reconcilers = new ArrayList<>(); + List reconcilingStrategies = null; + List> extensions = 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()); + for (GenericContentTypeRelatedExtension ext : extensions) { + if (RECONCILING_STRATEGY_ELT_NAME.equals(ext.getContributionName())) { + IReconcilingStrategy reconcilingStrategy = ext.createDelegateWithoutTypeCheck(textViewerLifecycles); + if (reconcilingStrategy != null) { + if (reconcilingStrategies == null) { + reconcilingStrategies = new ArrayList<>(); + } + reconcilingStrategies.add(reconcilingStrategy); + } + } else { + IReconciler reconciler = ext.createDelegate(textViewerLifecycles); + if (reconciler != null) { + reconcilers.add(reconciler); + } + } + } + + if (reconcilingStrategies != null) { + Reconciler reconciler = new Reconciler(); + reconciler.setReconcilingStrategy(new CompositeReconcilerStrategy(reconcilingStrategies), + IDocument.DEFAULT_CONTENT_TYPE); + reconcilers.add(0, reconciler); + } + + return reconcilers; } /** * 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, - Set contentTypes) { + List textViewerLifecycles, Set contentTypes) { if (this.highlightOutOfSync) { syncHighlight(); } @@ -104,7 +139,7 @@ public List getHighlightReconcilers(ISourceViewer sourceViewer, ITe .filter(ext -> contentTypes.contains(ext.targetContentType)) // .filter(ext -> ext.matches(sourceViewer, editor)) // .sorted(new ContentTypeSpecializationComparator().reversed()) // - .map(GenericContentTypeRelatedExtension::createDelegate) // + .map(ext -> ext.createDelegate(textViewerLifecycles)) // .collect(Collectors.toList()); } @@ -112,15 +147,16 @@ public List getHighlightReconcilers(ISourceViewer sourceViewer, ITe * 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, - Set contentTypes) { + List textViewerLifecycles, Set contentTypes) { if (this.foldingOutOfSync) { syncFolding(); } @@ -128,7 +164,7 @@ public List getFoldingReconcilers(ISourceViewer sourceViewer, IText .filter(ext -> contentTypes.contains(ext.targetContentType)) // .filter(ext -> ext.matches(sourceViewer, editor)) // .sorted(new ContentTypeSpecializationComparator().reversed()) // - .map(GenericContentTypeRelatedExtension::createDelegate) // + .map(ext -> ext.createDelegate(textViewerLifecycles)) // .collect(Collectors.toList()); } 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..7d37a5400 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 @@ -30,6 +30,7 @@ import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.ITextViewerLifecycle; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.ui.internal.genericeditor.GenericContentTypeRelatedExtension; import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; @@ -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()); }