diff options
Diffstat (limited to 'org.eclipse.ui.genericeditor/src/org/eclipse/ui')
7 files changed, 765 insertions, 0 deletions
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/CompositeContentAssistProcessor.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/CompositeContentAssistProcessor.java new file mode 100644 index 00000000000..d4d64acee51 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/CompositeContentAssistProcessor.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * - Mickael Istria (Red Hat Inc.) + *******************************************************************************/ +package org.eclipse.ui.internal.genericeditor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformationValidator; + +/** + * A content assist processor that delegates all content assist + * operations to children provided in constructor and aggregates + * the results. + * + * @since 1.0 + */ +public class CompositeContentAssistProcessor implements IContentAssistProcessor { + + private List<IContentAssistProcessor> fContentAssistProcessors; + + /** + * Constructor + * @param contentAssistProcessors the children that will actually populate the output + * of this content assist processor. + */ + public CompositeContentAssistProcessor(List<IContentAssistProcessor> contentAssistProcessors) { + fContentAssistProcessors= contentAssistProcessors; + } + + @Override + public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { + List<ICompletionProposal> res = new ArrayList<>(); + for (IContentAssistProcessor processor : this.fContentAssistProcessors) { + res.addAll(Arrays.asList(processor.computeCompletionProposals(viewer, offset))); + } + return res.toArray(new ICompletionProposal[res.size()]); + } + + @Override + public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { + List<IContextInformation> res = new ArrayList<>(); + for (IContentAssistProcessor processor : this.fContentAssistProcessors) { + res.addAll(Arrays.asList(processor.computeContextInformation(viewer, offset))); + } + return res.toArray(new IContextInformation[res.size()]); + } + + @Override + public char[] getCompletionProposalAutoActivationCharacters() { + return null; + } + + @Override + public char[] getContextInformationAutoActivationCharacters() { + return null; + } + + @Override + public String getErrorMessage() { + StringBuilder res = new StringBuilder(); + for (IContentAssistProcessor processor : this.fContentAssistProcessors) { + String errorMessage = processor.getErrorMessage(); + if (errorMessage != null) { + res.append(errorMessage); + res.append('\n'); + } + } + if (res.length() == 0) { + return null; + } else { + return res.toString(); + } + } + + @Override + public IContextInformationValidator getContextInformationValidator() { + return null; + } + +}
\ No newline at end of file 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 new file mode 100644 index 00000000000..a81cecbf8f0 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ContentAssistProcessorRegistry.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * - Mickael Istria (Red Hat Inc.) + *******************************************************************************/ +package org.eclipse.ui.internal.genericeditor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IRegistryChangeEvent; +import org.eclipse.core.runtime.IRegistryChangeListener; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +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.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformationValidator; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.PlatformUI; + +/** + * A registry of content assist processors provided by extension <code>org.eclipse.ui.genericeditor.contentAssistProcessors</code>. + * Those extensions are specific to a given {@link IContentType}. + * + * @since 1.0 + */ +public class ContentAssistProcessorRegistry { + + private static final String EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".contentAssistProcessors"; //$NON-NLS-1$ + + /** + * This class wraps and proxies an {@link IContentAssistProcessor} provided through extensions + * and loads it lazily when it can contribute to the editor, then delegates all operations to + * actual processor. + * When the contribution cannot contribute to the editor, this wrapper will return neutral values + * that don't affect editor behavior. + */ + private static class ContentAssistProcessorExtension implements IContentAssistProcessor { + private static final String CONTENT_TYPE_ATTRIBUTE = "contentType"; //$NON-NLS-1$ + private static final String CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$ + + private IConfigurationElement extension; + private IContentType targetContentType; + + private IContentAssistProcessor delegate; + + private ContentAssistProcessorExtension(IConfigurationElement element) throws Exception { + this.extension = element; + this.targetContentType = Platform.getContentTypeManager().getContentType(element.getAttribute(CONTENT_TYPE_ATTRIBUTE)); + } + + private IContentAssistProcessor getDelegate() { + if (this.delegate == null) { + try { + this.delegate = (IContentAssistProcessor) extension.createExecutableExtension(CLASS_ATTRIBUTE); + } catch (CoreException e) { + e.printStackTrace(); + } + } + return delegate; + } + + /** + * @return whether the referenced contribution should contribute to the current editor. + */ + public boolean isActive() { + IEditorInput input = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getEditorInput(); + IContentTypeManager contentTypeManager= Platform.getContentTypeManager(); + for (IContentType currentContentType : contentTypeManager.findContentTypesFor(input.getName())) { + if (currentContentType.isKindOf(targetContentType)) { + return true; + } + } + return false; + } + + @Override + public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { + if (isActive()) { + return getDelegate().computeCompletionProposals(viewer, offset); + } + return new ICompletionProposal[0]; + } + + @Override + public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { + if (isActive()) { + return getDelegate().computeContextInformation(viewer, offset); + } + return new IContextInformation[0]; + } + + @Override + public char[] getCompletionProposalAutoActivationCharacters() { + if (isActive()) { + return getDelegate().getCompletionProposalAutoActivationCharacters(); + } + return null; + } + + @Override + public char[] getContextInformationAutoActivationCharacters() { + if (isActive()) { + return getDelegate().getContextInformationAutoActivationCharacters(); + } + return null; + } + + @Override + public String getErrorMessage() { + if (isActive()) { + return getDelegate().getErrorMessage(); + } + return null; + } + + @Override + public IContextInformationValidator getContextInformationValidator() { + if (isActive()) { + return getDelegate().getContextInformationValidator(); + } + return null; + } + } + + private Map<IConfigurationElement, ContentAssistProcessorExtension> extensions = new HashMap<>(); + private boolean outOfSync = true; + + /** + * Creates the registry and binds it to the extension point. + */ + public ContentAssistProcessorRegistry() { + Platform.getExtensionRegistry().addRegistryChangeListener(new IRegistryChangeListener() { + @Override + public void registryChanged(IRegistryChangeEvent event) { + outOfSync = true; + } + }, EXTENSION_POINT_ID); + } + + /** + * 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 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<IContentAssistProcessor> getContentAssistProcessors(ISourceViewer sourceViewer, Set<IContentType> contentTypes) { + if (this.outOfSync) { + sync(); + } + List<IContentAssistProcessor> res = new ArrayList<>(); + for (ContentAssistProcessorExtension ext : this.extensions.values()) { + if (contentTypes.contains(ext.targetContentType)) { + res.add(ext); + } + } + return res; + } + + private void sync() { + Set<IConfigurationElement> toRemoveExtensions = new HashSet<>(this.extensions.keySet()); + for (IConfigurationElement extension : Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID)) { + toRemoveExtensions.remove(extension); + if (!this.extensions.containsKey(extension)) { + try { + this.extensions.put(extension, new ContentAssistProcessorExtension(extension)); + } catch (Exception ex) { + GenericEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); + } + } + } + for (IConfigurationElement toRemove : toRemoveExtensions) { + this.extensions.remove(toRemove); + } + this.outOfSync = false; + } +} 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 new file mode 100644 index 00000000000..467be5318d4 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextEditor.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2016 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sopot Cela, Mickael Istria (Red Hat Inc.) - initial implementation + *******************************************************************************/ +package org.eclipse.ui.internal.genericeditor; + +import org.eclipse.ui.editors.text.TextEditor; + +/** + * 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 + */ +public class ExtensionBasedTextEditor extends TextEditor { + + private static final String CONTEXT_ID = "org.eclipse.ui.genericeditor.genericEditorContext"; //$NON-NLS-1$ + + /** + * + */ + public ExtensionBasedTextEditor() { + setSourceViewerConfiguration(new ExtensionBasedTextViewerConfiguration(this, getPreferenceStore())); + } + + @Override + protected void setKeyBindingScopes(String[] scopes) { + super.setKeyBindingScopes(new String[] { CONTEXT_ID }); + } + +} 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 new file mode 100644 index 00000000000..d8d2b21c423 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/ExtensionBasedTextViewerConfiguration.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sopot Cela, Mickael Istria (Red Hat Inc.) - initial implementation + *******************************************************************************/ +package org.eclipse.ui.internal.genericeditor; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.AbstractReusableInformationControlCreator; +import org.eclipse.jface.text.DefaultInformationControl; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.contentassist.ContentAssistant; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.contentassist.IContentAssistant; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; + +/** + * The configuration of the {@link ExtensionBasedTextEditor}. It registers the proxy composite + * for hover, completion, syntax highlighting, and then those proxy take care of resolving to + * the right extensions on-demand. + * + * @since 1.0 + */ +public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewerConfiguration { + + private IEditorPart editor; + private Set<IContentType> contentTypes; + + /** + * + * @param editor the editor we're creating. + * @param preferenceStore the preference store. + */ + public ExtensionBasedTextViewerConfiguration(IEditorPart editor, IPreferenceStore preferenceStore) { + super(preferenceStore); + this.editor = editor; + } + + private Set<IContentType> getContentTypes() { + if (this.contentTypes == null) { + this.contentTypes = new HashSet<>(Arrays.asList(Platform.getContentTypeManager().findContentTypesFor(editor.getEditorInput().getName()))); + } + return this.contentTypes; + } + + @Override + public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { + TextHoverRegistry registry= GenericEditorPlugin.getDefault().getHoverRegistry(); + return registry.getAvailableHover(sourceViewer, getContentTypes()); + } + + @Override + public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { + ContentAssistProcessorRegistry registry= GenericEditorPlugin.getDefault().getContentAssistProcessorRegistry(); + IContentAssistProcessor processor = new CompositeContentAssistProcessor(registry.getContentAssistProcessors(sourceViewer, getContentTypes())); + ContentAssistant res= new ContentAssistant(); + res.setContextInformationPopupOrientation(ContentAssistant.CONTEXT_INFO_BELOW); + res.setProposalPopupOrientation(ContentAssistant.PROPOSAL_REMOVE); + res.enableColoredLabels(true); + res.setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE); + res.setInformationControlCreator(new AbstractReusableInformationControlCreator() { + @Override + protected IInformationControl doCreateInformationControl(Shell parent) { + return new DefaultInformationControl(parent); + } + }); + return res; + } + + @Override + public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { + PresentationReconcilerRegistry registry = GenericEditorPlugin.getDefault().getPresentationReconcilerRegistry(); + List<IPresentationReconciler> reconciliers = registry.getPresentationReconcilers(sourceViewer, getContentTypes()); + if (!reconciliers.isEmpty()) { + return reconciliers.get(0); + } + return null; + } + +} diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorPlugin.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorPlugin.java new file mode 100644 index 00000000000..e5f33d470cc --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorPlugin.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sopot Cela, Mickael Istria (Red Hat Inc.) - initial implementation + *******************************************************************************/ +package org.eclipse.ui.internal.genericeditor; + +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * Generic editor plugin activator and singletons. + * + * @since 1.0 + */ +public class GenericEditorPlugin extends AbstractUIPlugin { + + public static final String BUNDLE_ID = "org.eclipse.ui.genericeditor"; //$NON-NLS-1$ + + private static GenericEditorPlugin INSTANCE; + + private TextHoverRegistry textHoversRegistry; + private ContentAssistProcessorRegistry contentAssistProcessorsRegistry; + private PresentationReconcilerRegistry presentationReconcilierRegistry; + + @Override + public void start(BundleContext context) throws Exception{ + INSTANCE = this; + super.start(context); + } + + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + INSTANCE = null; + } + + public static GenericEditorPlugin getDefault() { + return INSTANCE; + } + + /** + * @return the registry allowing to access contributed {@link ITextHover}s. + * @since 1.0 + */ + public synchronized TextHoverRegistry getHoverRegistry() { + if (this.textHoversRegistry == null) { + this.textHoversRegistry = new TextHoverRegistry(getPreferenceStore()); + } + return this.textHoversRegistry; + } + + /** + * @return the registry allowing to access contributed {@link IContentAssistProcessor}s. + * @since 1.0 + */ + public synchronized ContentAssistProcessorRegistry getContentAssistProcessorRegistry() { + if (this.contentAssistProcessorsRegistry == null) { + this.contentAssistProcessorsRegistry = new ContentAssistProcessorRegistry(); + } + return this.contentAssistProcessorsRegistry; + } + + /** + * @return the registry allowing to access contributed {@link IPresentationReconciler}s. + * @since 1.0 + */ + public synchronized PresentationReconcilerRegistry getPresentationReconcilerRegistry() { + if (this.presentationReconcilierRegistry == null) { + this.presentationReconcilierRegistry = new PresentationReconcilerRegistry(); + } + return this.presentationReconcilierRegistry; + } +} 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 new file mode 100644 index 00000000000..b973b816cf2 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/PresentationReconcilerRegistry.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sopot Cela, Mickael Istria (Red Hat Inc.) - initial implementation + *******************************************************************************/ +package org.eclipse.ui.internal.genericeditor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IRegistryChangeEvent; +import org.eclipse.core.runtime.IRegistryChangeListener; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.presentation.IPresentationDamager; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.jface.text.presentation.IPresentationRepairer; +import org.eclipse.jface.text.source.ISourceViewer; + +/** + * A registry of presentation reconciliers provided by extension <code>org.eclipse.ui.genericeditor.presentationReconcilers</code>. + * Those extensions are specific to a given {@link IContentType}. + * + * @since 1.0 + */ +public class PresentationReconcilerRegistry { + + private static final String EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".presentationReconcilers"; //$NON-NLS-1$ + + /** + * This class wraps and proxies an {@link IPresentationReconcilier} provided through extensions + * and loads it lazily when it can contribute to the editor, then delegates all operations to + * actual reconcilier. + */ + private static class PresentationReconcilerExtension implements IPresentationReconciler { + private static final String CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$ + private static final String CONTENT_TYPE_ATTRIBUTE = "contentType"; //$NON-NLS-1$ + + private IConfigurationElement extension; + private IContentType targetContentType; + + private IPresentationReconciler delegate; + + private PresentationReconcilerExtension(IConfigurationElement element) throws Exception { + this.extension = element; + this.targetContentType = Platform.getContentTypeManager().getContentType(element.getAttribute(CONTENT_TYPE_ATTRIBUTE)); + } + + private IPresentationReconciler getDelegate() { + if (this.delegate == null) { + try { + this.delegate = (IPresentationReconciler) extension.createExecutableExtension(CLASS_ATTRIBUTE); + } catch (CoreException e) { + e.printStackTrace(); + } + } + return delegate; + } + + @Override + public void install(ITextViewer viewer) { + getDelegate().install(viewer); + } + + @Override + public void uninstall() { + getDelegate().uninstall(); + } + + @Override + public IPresentationDamager getDamager(String contentType) { + return getDelegate().getDamager(contentType); + + } + + @Override + public IPresentationRepairer getRepairer(String contentType) { + return getDelegate().getRepairer(contentType); + } + + } + private Map<IConfigurationElement, PresentationReconcilerExtension> extensions = new HashMap<>(); + private boolean outOfSync = true; + + /** + * Creates the registry and binds it to the extension point. + */ + public PresentationReconcilerRegistry() { + Platform.getExtensionRegistry().addRegistryChangeListener(new IRegistryChangeListener() { + @Override + public void registryChanged(IRegistryChangeEvent event) { + outOfSync = true; + } + }, EXTENSION_POINT_ID); + } + + /** + * 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 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<IPresentationReconciler> getPresentationReconcilers(ISourceViewer sourceViewer, Set<IContentType> contentTypes) { + if (this.outOfSync) { + sync(); + } + List<IPresentationReconciler> res = new ArrayList<>(); + for (PresentationReconcilerExtension ext : this.extensions.values()) { + if (contentTypes.contains(ext.targetContentType)) { + res.add(ext); + } + } + return res; + } + + private void sync() { + Set<IConfigurationElement> toRemoveExtensions = new HashSet<>(this.extensions.keySet()); + for (IConfigurationElement extension : Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID)) { + toRemoveExtensions.remove(extension); + if (!this.extensions.containsKey(extension)) { + try { + this.extensions.put(extension, new PresentationReconcilerExtension(extension)); + } catch (Exception ex) { + GenericEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); + } + } + } + for (IConfigurationElement toRemove : toRemoveExtensions) { + this.extensions.remove(toRemove); + } + this.outOfSync = false; + } + +} diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/TextHoverRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/TextHoverRegistry.java new file mode 100644 index 00000000000..88e4825f66c --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/TextHoverRegistry.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * - Mickael Istria (Red Hat Inc.) + *******************************************************************************/ +package org.eclipse.ui.internal.genericeditor; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IRegistryChangeEvent; +import org.eclipse.core.runtime.IRegistryChangeListener; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.source.ISourceViewer; + +/** + * Text hover registry that manages the detectors + * contributed by the <code>org.eclipse.ui.workbench.texteditor.hoverProvider</code> extension point for + * targets contributed by the <code>org.eclipse.ui.workbench.texteditor.hyperlinkDetectorTargets</code> extension point. + * + * @since 1.0 + */ +public final class TextHoverRegistry { + + private static final String EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".hoverProviders"; //$NON-NLS-1$ + + private Map<IConfigurationElement, TextHoverExtension> extensions = new HashMap<>(); + private boolean outOfSync = true; + + private static class TextHoverExtension { + private static final String CONTENT_TYPE_ATTRIBUTE = "contentType"; //$NON-NLS-1$ + private static final String CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$ + + private IConfigurationElement extension; + private IContentType targetContentType; + private ITextHover delegate; + + public TextHoverExtension(IConfigurationElement extension) throws Exception { + this.extension = extension; + this.targetContentType = Platform.getContentTypeManager().getContentType(extension.getAttribute(CONTENT_TYPE_ATTRIBUTE)); + } + + private ITextHover getDelegate() { + if (this.delegate == null) { + try { + this.delegate = (ITextHover) extension.createExecutableExtension(CLASS_ATTRIBUTE); + } catch (CoreException e) { + e.printStackTrace(); + } + } + return delegate; + } + + } + + public TextHoverRegistry(IPreferenceStore preferenceStore) { + Platform.getExtensionRegistry().addRegistryChangeListener(new IRegistryChangeListener() { + @Override + public void registryChanged(IRegistryChangeEvent event) { + outOfSync = true; + } + }, EXTENSION_POINT_ID); + } + + public ITextHover getAvailableHover(ISourceViewer sourceViewer, Set<IContentType> contentTypes) { + if (this.outOfSync) { + sync(); + } + // TODO rather that returning the 1st active hover, consider + // supporting compound/aggregated hovers. + for (TextHoverExtension ext : this.extensions.values()) { + if (contentTypes.contains(ext.targetContentType)) { + return ext.getDelegate(); + } + } + return null; + } + + private void sync() { + Set<IConfigurationElement> toRemoveExtensions = new HashSet<>(this.extensions.keySet()); + for (IConfigurationElement extension : Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID)) { + toRemoveExtensions.remove(extension); + if (!this.extensions.containsKey(extension)) { + try { + this.extensions.put(extension, new TextHoverExtension(extension)); + } catch (Exception ex) { + GenericEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); + } + } + } + for (IConfigurationElement toRemove : toRemoveExtensions) { + this.extensions.remove(toRemove); + } + this.outOfSync = false; + } + +} |