diff options
author | Sopot Cela | 2016-09-26 10:16:25 +0000 |
---|---|---|
committer | Dani Megert | 2016-09-26 10:30:36 +0000 |
commit | 5bcb7ac5045743f9ec17895a9a12318dd90ea4cb (patch) | |
tree | ea2d98cee208a040d2ee43fef458d3775a5dc90d /org.eclipse.ui.genericeditor | |
parent | 8871738e5b595e7715499161b04980d9f16df820 (diff) | |
download | eclipse.platform.text-5bcb7ac5045743f9ec17895a9a12318dd90ea4cb.tar.gz eclipse.platform.text-5bcb7ac5045743f9ec17895a9a12318dd90ea4cb.tar.xz eclipse.platform.text-5bcb7ac5045743f9ec17895a9a12318dd90ea4cb.zip |
Bug 497871 - Generic and extensible text editor
This change creates a new extensible text editor, with extension points
for:
* contentAssist
* hover
* syntax highlighting
Some unit tests show examples of extensions. You can load an IDE with
the suggested change (including org.eclipse.ui.editors.tests) and get a
syntax highlighter, a hover support and some content assist on the plain
text editor.
Bug: 497871
Bug: 496117
Bug: 496115
Bug: 496300
Signed-off-by: Mickael Istria <mistria@redhat.com>
Signed-off-by: Sopot Cela <scela@redhat.com>
Change-Id: I2eec71e4620364aa11c500ec07e54c693863cf44
Diffstat (limited to 'org.eclipse.ui.genericeditor')
19 files changed, 1305 insertions, 0 deletions
diff --git a/org.eclipse.ui.genericeditor/.classpath b/org.eclipse.ui.genericeditor/.classpath new file mode 100644 index 00000000000..eca7bdba8f0 --- /dev/null +++ b/org.eclipse.ui.genericeditor/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.ui.genericeditor/.project b/org.eclipse.ui.genericeditor/.project new file mode 100644 index 00000000000..e1f2994e5af --- /dev/null +++ b/org.eclipse.ui.genericeditor/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.ui.genericeditor</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.ui.genericeditor/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.ui.genericeditor/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..0c68a61dca8 --- /dev/null +++ b/org.eclipse.ui.genericeditor/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..ca7ab11688a --- /dev/null +++ b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF @@ -0,0 +1,17 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Generic and Extensible Text Editor +Bundle-SymbolicName: org.eclipse.ui.genericeditor;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: Eclipse.org +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.eclipse.ui.workbench.texteditor;bundle-version="3.10.0", + org.eclipse.ui.editors;bundle-version="3.10.0", + org.eclipse.ui;bundle-version="3.108.0", + org.eclipse.text;bundle-version="3.6.0", + org.eclipse.jface.text;bundle-version="3.11.0", + org.eclipse.core.runtime;bundle-version="3.12.0" +Export-Package: org.eclipse.ui.internal.genericeditor;x-internal:=true +Bundle-Activator: org.eclipse.ui.internal.genericeditor.GenericEditorPlugin +Bundle-Localization: plugin +Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.ui.genericeditor/build.properties b/org.eclipse.ui.genericeditor/build.properties new file mode 100644 index 00000000000..bef8e620327 --- /dev/null +++ b/org.eclipse.ui.genericeditor/build.properties @@ -0,0 +1,7 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + plugin.properties,\ + icons/ diff --git a/org.eclipse.ui.genericeditor/icons/full/obj16/geneditor.png b/org.eclipse.ui.genericeditor/icons/full/obj16/geneditor.png Binary files differnew file mode 100644 index 00000000000..25b7217874f --- /dev/null +++ b/org.eclipse.ui.genericeditor/icons/full/obj16/geneditor.png diff --git a/org.eclipse.ui.genericeditor/plugin.properties b/org.eclipse.ui.genericeditor/plugin.properties new file mode 100644 index 00000000000..b6a2e26f60c --- /dev/null +++ b/org.eclipse.ui.genericeditor/plugin.properties @@ -0,0 +1,10 @@ +genericEditor_name=Generic Text Editor +ExtPoint.presentationReconciliers=Presentation Reconciliers +ExtPoint.hoverProvider= Hover Providers +ExtPoint.contentAssistProcessors=Content Assist Providers +openDeclarationCommand_name=Open Declaration +openDeclarationCommand_name=Open Declaration for selected element +context_name=in Generic Code Editor +context_description=When editing in the Generic Code Editor +findReferencesCommand_name=Find References +findReferencesCommand_description=Find other code items referencing the current selected item.
\ No newline at end of file diff --git a/org.eclipse.ui.genericeditor/plugin.xml b/org.eclipse.ui.genericeditor/plugin.xml new file mode 100644 index 00000000000..dba3b9ab914 --- /dev/null +++ b/org.eclipse.ui.genericeditor/plugin.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.4"?> +<plugin> + <extension-point id="presentationReconcilers" name="%ExtPoint.presentationReconciliers" schema="schema/presentationReconcilers.exsd"/> + <extension-point id="contentAssistProcessors" name="%ExtPoint.contentAssistProcessors" schema="schema/contentAssistProcessors.exsd"/> + <extension-point id="hoverProviders" name="%ExtPoint.hoverProviders" schema="schema/hoverProviders.exsd"/> + <extension + point="org.eclipse.ui.editors"> + <editor + class="org.eclipse.ui.internal.genericeditor.ExtensionBasedTextEditor" + default="false" + icon="icons/full/obj16/geneditor.png" + id="org.eclipse.ui.genericeditor.GenericEditor" + name="%genericEditor_name"> + <contentTypeBinding + contentTypeId="org.eclipse.core.runtime.text"> + </contentTypeBinding> + </editor> + </extension> + <extension + point="org.eclipse.ui.commands"> + <command + description="%findReferencesCommand_description" + id="org.eclipse.ui.genericeditor.findReferences" + name="%findReferencesCommand_name"> + </command> + </extension> + <extension + point="org.eclipse.ui.contexts"> + <context + description="%context_description" + id="org.eclipse.ui.genericeditor.genericEditorContext" + name="%context_name" + parentId="org.eclipse.ui.textEditorScope"> + </context> + </extension> + <extension + point="org.eclipse.ui.bindings"> + <key + commandId="org.eclipse.ui.edit.text.open.hyperlink" + contextId="org.eclipse.ui.genericeditor.genericEditorContext" + schemeId="org.eclipse.ui.defaultAcceleratorConfiguration" + sequence="F3"> + </key> + <key + commandId="org.eclipse.ui.genericeditor.findReferences" + contextId="org.eclipse.ui.genericeditor.genericEditorContext" + schemeId="org.eclipse.ui.defaultAcceleratorConfiguration" + sequence="M1+M2+G"> + </key> + </extension> + <extension + point="org.eclipse.ui.menus"> + <menuContribution + allPopups="true" + locationURI="popup:#TextEditorContext?after=additions"> + <command + commandId="org.eclipse.ui.genericeditor.findReferences" + style="push"> + </command> + </menuContribution> + </extension> + +</plugin> diff --git a/org.eclipse.ui.genericeditor/pom.xml b/org.eclipse.ui.genericeditor/pom.xml new file mode 100644 index 00000000000..df83a530d13 --- /dev/null +++ b/org.eclipse.ui.genericeditor/pom.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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 Distribution License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/org/documents/edl-v10.php + + Contributors: + Mickael Istria (Red Hat Inc.) - initial implementation +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>eclipse.platform.text</artifactId> + <groupId>eclipse.platform.text</groupId> + <version>4.7.0-SNAPSHOT</version> + </parent> + <groupId>org.eclipse.ui</groupId> + <artifactId>org.eclipse.ui.genericeditor</artifactId> + <version>1.0.0-SNAPSHOT</version> + <packaging>eclipse-plugin</packaging> +</project> diff --git a/org.eclipse.ui.genericeditor/schema/contentAssistProcessors.exsd b/org.eclipse.ui.genericeditor/schema/contentAssistProcessors.exsd new file mode 100644 index 00000000000..441b85ab13f --- /dev/null +++ b/org.eclipse.ui.genericeditor/schema/contentAssistProcessors.exsd @@ -0,0 +1,116 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.ui.genericeditor" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appinfo> + <meta.schema plugin="org.eclipse.ui.genericeditor" id="contentAssistProcessors" name="Content assist processors"/> + </appinfo> + <documentation> + This extension point is used to contribute content assist processors for adding content assist support to a given content type. + </documentation> + </annotation> + + <include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/> + + <element name="extension"> + <annotation> + <appinfo> + <meta.element /> + </appinfo> + </annotation> + <complexType> + <sequence minOccurs="1" maxOccurs="unbounded"> + <element ref="contentAssistProcessor"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + a fully qualified identifier of the target extension point + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + an optional identifier of the extension instance + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + an optional name of the extension instance + </documentation> + <appinfo> + <meta.attribute translatable="true"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="contentAssistProcessor"> + <complexType> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + The fully qualified class name implementing the interface <code>org.eclipse.jface.text.contentassist.IContentAssistProcessor</code> + </documentation> + <appinfo> + <meta.attribute kind="java" basedOn=":org.eclipse.jface.text.contentassist.IContentAssistProcessor"/> + </appinfo> + </annotation> + </attribute> + <attribute name="contentType" type="string" use="required"> + <annotation> + <documentation> + The target content-type for this extension. Content-types are defined as extension to the org.eclipse.core.contenttype.contentTypes extension point. + </documentation> + <appinfo> + <meta.attribute kind="identifier" basedOn="org.eclipse.core.contenttype.contentTypes/content-type/@id"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appinfo> + <meta.section type="since"/> + </appinfo> + <documentation> + 1.0 + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="examples"/> + </appinfo> + <documentation> + This is an example of a processor being registered for a target definition file type: + +<pre> +<extension point="org.eclipse.ui.genericeditor.contentAssistProcessors"> + <contentAssistProcessor + class="org.eclipse.ui.genericeditor.examples.TargedDefinitionContentAssist" + contentType="org.eclipse.pde.targetFile"> + </contentAssistProcessor> +</extension> +</pre> + </documentation> + </annotation> + + + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + 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 <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema> diff --git a/org.eclipse.ui.genericeditor/schema/hoverProviders.exsd b/org.eclipse.ui.genericeditor/schema/hoverProviders.exsd new file mode 100644 index 00000000000..ac475e82af3 --- /dev/null +++ b/org.eclipse.ui.genericeditor/schema/hoverProviders.exsd @@ -0,0 +1,145 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.ui.genericeditor" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appinfo> + <meta.schema plugin="org.eclipse.ui.genericeditor" id="hoverProviders" name="Hover providers"/> + </appinfo> + <documentation> + This extension point is used to contribute hover providers for showing text hovers in a file with a given content type. + </documentation> + </annotation> + + <include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/> + + <element name="extension"> + <annotation> + <appinfo> + <meta.element /> + </appinfo> + </annotation> + <complexType> + <sequence> + <element ref="hoverProvider" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + a fully qualified identifier of the target extension point + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + an optional identifier of the extension instance + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + an optional name of the extension instance + </documentation> + <appinfo> + <meta.attribute translatable="true"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="hoverProvider"> + <complexType> + <attribute name="id" type="string"> + <annotation> + <documentation> + A string uniquely identifying this reference provider. + </documentation> + </annotation> + </attribute> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + The fully qualified class name implementing the interface <code>org.eclipse.jface.text.ITextHover</code> + </documentation> + <appinfo> + <meta.attribute kind="java" basedOn=":org.eclipse.jface.text.ITextHover"/> + </appinfo> + </annotation> + </attribute> + <attribute name="contentType" type="string" use="required"> + <annotation> + <documentation> + The target content-type for this extension. Content-types are defined as extension to the org.eclipse.core.contenttype.contentTypes extension point. + </documentation> + <appinfo> + <meta.attribute kind="identifier" basedOn="org.eclipse.core.contenttype.contentTypes/content-type/@id"/> + </appinfo> + </annotation> + </attribute> + <attribute name="before" type="string"> + <annotation> + <documentation> + The id of a hoverProvider before which to place this contribution. +Plan is to have contributions are sorted according to that value and to have only the first one shown (or the the first "compoundable" ones) + </documentation> + <appinfo> + <meta.attribute kind="identifier" basedOn="org.eclipse.ui.genericeditor.hoverProviders/hoverProvider/@id"/> + </appinfo> + </annotation> + </attribute> + <attribute name="after" type="string"> + <annotation> + <documentation> + The id of a hoverProvider after which to place this contribution. +Plan is to have contributions are sorted according to that value and to have only the first one shown (or the the first "compoundable" ones) + </documentation> + <appinfo> + <meta.attribute kind="identifier" basedOn="org.eclipse.ui.genericeditor.hoverProviders/hoverProvider/@id"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appinfo> + <meta.section type="since"/> + </appinfo> + <documentation> + 1.0 + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="examples"/> + </appinfo> + <documentation> + Below is an example using the hover provider extension point: + +<pre> +<extension point="org.eclipse.ui.genericeditor.hoverProviders"> + <hoverProvider + class="org.eclipse.ui.genericeditor.tests.contributions.MagicHoverProvider" + contentType="org.eclipse.core.runtime.text"> + </hoverProvider> +</extension> +</pre> + </documentation> + </annotation> + + + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + 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 <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema> diff --git a/org.eclipse.ui.genericeditor/schema/presentationReconcilers.exsd b/org.eclipse.ui.genericeditor/schema/presentationReconcilers.exsd new file mode 100644 index 00000000000..17aca3a3506 --- /dev/null +++ b/org.eclipse.ui.genericeditor/schema/presentationReconcilers.exsd @@ -0,0 +1,116 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.ui.genericeditor" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appinfo> + <meta.schema plugin="org.eclipse.ui.genericeditor" id="presentationReconcilers" name="Presentation reconcilers"/> + </appinfo> + <documentation> + This extension point is used to contribute presentation reconcilers for controlling the presentation on a file with a given content type. + </documentation> + </annotation> + + <include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/> + + <element name="extension"> + <annotation> + <appinfo> + <meta.element /> + </appinfo> + </annotation> + <complexType> + <sequence minOccurs="1" maxOccurs="unbounded"> + <element ref="presentationReconciler"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + a fully qualified identifier of the target extension point + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + an optional identifier of the extension instance + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + an optional name of the extension instance + </documentation> + <appinfo> + <meta.attribute translatable="true"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="presentationReconciler"> + <complexType> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + The fully qualified class name implementing the interface <code>org.eclipse.jface.text.presentation.IPresentationReconciler</code> + </documentation> + <appinfo> + <meta.attribute kind="java" basedOn=":org.eclipse.jface.text.presentation.IPresentationReconciler"/> + </appinfo> + </annotation> + </attribute> + <attribute name="contentType" type="string" use="required"> + <annotation> + <documentation> + The target content-type for this extension. Content-types are defined as extension to the org.eclipse.core.contenttype.contentTypes extension point. + </documentation> + <appinfo> + <meta.attribute kind="identifier" basedOn="org.eclipse.core.contenttype.contentTypes/content-type/@id"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appinfo> + <meta.section type="since"/> + </appinfo> + <documentation> + 1.0 + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="examples"/> + </appinfo> + <documentation> + Below is an example of how to use the Presentation Reconciler extension point: + +<pre> +<extension point="org.eclipse.ui.genericeditor.presentationReconcilers"> + <presentationReconciler + class="org.eclipse.ui.genericeditor.examples.TPPresentationReconciler" + contentType="org.eclipse.pde.targetFile"> + </presentationReconciler> +</extension> +</pre> + </documentation> + </annotation> + + + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + 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 <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema> 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; + } + +} |