diff --git a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF index aeaf84dd6..3d7c32cc3 100644 --- a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF +++ b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF @@ -16,11 +16,14 @@ Require-Bundle: org.eclipse.ui.workbench.texteditor;bundle-version="3.10.0", org.eclipse.core.resources;bundle-version="3.11.0", org.eclipse.core.expressions;bundle-version="3.6.0", org.eclipse.compare;resolution:=optional -Export-Package: org.eclipse.ui.internal.genericeditor;x-internal:=true, +Export-Package: org.eclipse.core.resources.proposed, + org.eclipse.jface.text.proposed, + org.eclipse.ui.internal.genericeditor;x-internal:=true, + org.eclipse.ui.internal.genericeditor.compare;x-internal:=true, org.eclipse.ui.internal.genericeditor.hover;x-internal:=true, org.eclipse.ui.internal.genericeditor.markers;x-internal:=true, org.eclipse.ui.internal.genericeditor.preferences;x-internal:=true, - org.eclipse.ui.internal.genericeditor.compare;x-internal:=true + org.eclipse.ui.texteditor.proposed Bundle-Activator: org.eclipse.ui.internal.genericeditor.GenericEditorPlugin Bundle-Localization: plugin Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.ui.genericeditor/plugin.xml b/org.eclipse.ui.genericeditor/plugin.xml index 9a8755e5f..fac1f2291 100644 --- a/org.eclipse.ui.genericeditor/plugin.xml +++ b/org.eclipse.ui.genericeditor/plugin.xml @@ -26,6 +26,7 @@ + + + + + + + + + This extension point is used to contribute reconcilers for controlling the presentation on a file with a given content type. + + + + + + + + + + + + + + + + + + + a fully qualified identifier of the target extension point + + + + + + + an optional identifier of the extension instance + + + + + + + an optional name of the extension instance + + + + + + + + + + + + + + + + + + The fully qualified class name implementing the interface <code>org.eclipse.jface.text.reconciler.IReconciler</code> + + + + + + + + + + The target content-type for this extension. Content-types are defined as extension to the org.eclipse.core.contenttype.contentTypes extension point. + + + + + + + + + + + + + A core Expression that controls the enabled of the given reconciler. The viewer, editor, and editor input are registered in the evaluation context as variable: + + * <with variable="viewer"/> : use it if your expression requires the viewer. + * <with variable="document"/> : use it if your expression requires the document. + * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). + * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.1 + + + + + + + + + Below is an example of how to use the Reconciler extension point: + +<pre> +<extension point="org.eclipse.ui.genericeditor.reconcilers"> + <reconciler + class="org.eclipse.ui.genericeditor.examples.TargetDefinitionReconciler" + contentType="org.eclipse.pde.targetFile"> + <enabledWhen> + <with variable="editor"> + <test property="org.eclipse.ui.genericeditor.examples.TargetDefinitionPropertyTester"> + </test> + </with> + </enabledWhen> + </reconciler> +</extension> +</pre> + + + + + + + + + + + Copyright (c) 2017 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 <a href="https://www.eclipse.org/legal/epl-2.0">https://www.eclipse.org/legal/epl-v20.html</a>/ + +SPDX-License-Identifier: EPL-2.0 + + + + diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/IPreferenceStoreProvider.java b/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/IPreferenceStoreProvider.java new file mode 100644 index 000000000..a09acc1f6 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/IPreferenceStoreProvider.java @@ -0,0 +1,9 @@ +package org.eclipse.core.resources.proposed; + +import org.eclipse.core.resources.IResource; +import org.eclipse.jface.preference.IPreferenceStore; + +public interface IPreferenceStoreProvider { + + IPreferenceStore getPreferenceStore(IResource resource); +} diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/PreferenceStoreMapping.java b/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/PreferenceStoreMapping.java new file mode 100644 index 000000000..a307b7ea0 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/PreferenceStoreMapping.java @@ -0,0 +1,206 @@ +package org.eclipse.core.resources.proposed; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; + +public class PreferenceStoreMapping implements IPreferenceStore { + + private final IPreferenceStore delegate; + + private final Map mappings; + private final Map mappings2; + + public PreferenceStoreMapping(IPreferenceStore delegate) { + this.delegate = delegate; + this.mappings = new HashMap<>(); + this.mappings2 = new HashMap<>(); + } + + public PreferenceStoreMapping addMapping(String name1, String name2) { + mappings.put(name1, name2); + mappings2.put(name2, name1); + return this; + } + + private String getProperName(String name) { + if (mappings.containsKey(name)) { + return mappings.get(name); + } + return name; + } + + @Override + public void addPropertyChangeListener(IPropertyChangeListener listener) { + delegate.addPropertyChangeListener(listener); + } + + @Override + public boolean contains(String name) { + if (mappings.containsKey(name) || mappings2.containsKey(name)) { + return true; + } + return delegate.contains(getProperName(name)); + } + + @Override + public void firePropertyChangeEvent(String name, Object oldValue, Object newValue) { + delegate.firePropertyChangeEvent(name, oldValue, newValue); + } + + @Override + public boolean getBoolean(String name) { + if (mappings2.containsKey(name)) { + name = mappings2.get(name); + } + return delegate.getBoolean(name); + } + + @Override + public boolean getDefaultBoolean(String name) { + return delegate.getDefaultBoolean(name); + } + + @Override + public double getDefaultDouble(String name) { + return delegate.getDefaultDouble(getProperName(name)); + } + + @Override + public float getDefaultFloat(String name) { + return delegate.getDefaultFloat(getProperName(name)); + } + + @Override + public int getDefaultInt(String name) { + return delegate.getDefaultInt(getProperName(name)); + } + + @Override + public long getDefaultLong(String name) { + return delegate.getDefaultLong(getProperName(name)); + } + + @Override + public String getDefaultString(String name) { + return delegate.getDefaultString(getProperName(name)); + } + + @Override + public double getDouble(String name) { + return delegate.getDouble(getProperName(name)); + } + + @Override + public float getFloat(String name) { + return delegate.getFloat(getProperName(name)); + } + + @Override + public int getInt(String name) { + return delegate.getInt(getProperName(name)); + } + + @Override + public long getLong(String name) { + return delegate.getLong(getProperName(name)); + } + + @Override + public String getString(String name) { + return delegate.getString(getProperName(name)); + } + + @Override + public boolean isDefault(String name) { + return delegate.isDefault(getProperName(name)); + } + + @Override + public boolean needsSaving() { + return delegate.needsSaving(); + } + + @Override + public void putValue(String name, String value) { + delegate.putValue(name, value); + } + + @Override + public void removePropertyChangeListener(IPropertyChangeListener listener) { + delegate.removePropertyChangeListener(listener); + } + + @Override + public void setDefault(String name, double value) { + delegate.setDefault(name, value); + } + + @Override + public void setDefault(String name, float value) { + delegate.setDefault(name, value); + } + + @Override + public void setDefault(String name, int value) { + delegate.setDefault(name, value); + } + + @Override + public void setDefault(String name, long value) { + delegate.setDefault(name, value); + } + + @Override + public void setDefault(String name, String defaultObject) { + delegate.setDefault(name, defaultObject); + } + + @Override + public void setDefault(String name, boolean value) { + delegate.setDefault(name, value); + } + + @Override + public void setToDefault(String name) { + delegate.setToDefault(getProperName(name)); + } + + @Override + public void setValue(String name, double value) { + delegate.setValue(name, value); + } + + @Override + public void setValue(String name, float value) { + delegate.setValue(name, value); + } + + @Override + public void setValue(String name, int value) { + delegate.setValue(name, value); + } + + @Override + public void setValue(String name, long value) { + delegate.setValue(name, value); + } + + @Override + public void setValue(String name, String value) { + delegate.setValue(getProperName(name), value); + } + + @Override + public void setValue(String name, boolean value) { + boolean oldValue = delegate.getBoolean(name); + delegate.setValue(name, value); + String mapedName = getProperName(name); + if (oldValue != value && !mapedName.equals(name)) { + firePropertyChangeEvent(mapedName, oldValue, value); + } + } + +} diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/PreferenceStoreProviderRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/PreferenceStoreProviderRegistry.java new file mode 100644 index 000000000..b2bd7e0b1 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/core/resources/proposed/PreferenceStoreProviderRegistry.java @@ -0,0 +1,86 @@ +package org.eclipse.core.resources.proposed; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.IConfigurationElement; +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.source.ISourceViewer; +import org.eclipse.ui.internal.genericeditor.ContentTypeSpecializationComparator; +import org.eclipse.ui.internal.genericeditor.GenericContentTypeRelatedExtension; +import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; +import org.eclipse.ui.texteditor.ITextEditor; + +/** + * A registry of {@link IPreferenceStore} provider provided by extension + * org.eclipse.ui.genericeditor.preferenceStoreProviders. Those + * extensions are specific to a given {@link IContentType}. + * + * @since 1.0 + */ +public class PreferenceStoreProviderRegistry { + + private static final String EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".preferenceStoreProviders"; //$NON-NLS-1$ + + private Map> extensions = new HashMap<>(); + private boolean outOfSync = true; + + /** + * Creates the registry and binds it to the extension point. + */ + public PreferenceStoreProviderRegistry() { + Platform.getExtensionRegistry().addRegistryChangeListener(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 editor the text editor + * @param contentTypes the content types of the document we're editing. + * @return the list of {@link IPreferenceStore} contributed for at least one of + * the content types. + */ + public List getPreferenceStoreProviders(ISourceViewer sourceViewer, ITextEditor editor, + 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()); + } + + private void sync() { + Set 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 GenericContentTypeRelatedExtension(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; + } + +} \ No newline at end of file diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/jface/text/proposed/ITextViewerLifecycle.java b/org.eclipse.ui.genericeditor/src/org/eclipse/jface/text/proposed/ITextViewerLifecycle.java new file mode 100644 index 000000000..634a19c6d --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/jface/text/proposed/ITextViewerLifecycle.java @@ -0,0 +1,10 @@ +package org.eclipse.jface.text.proposed; + +import org.eclipse.jface.text.ITextViewer; + +public interface ITextViewerLifecycle { + + void install(ITextViewer viewer); + + void uninstall(); +} 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..903fe6ffd 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,14 +16,27 @@ *******************************************************************************/ package org.eclipse.ui.internal.genericeditor; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; +import java.util.Set; +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +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.dialogs.MessageDialog; -import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.Region; @@ -39,10 +52,10 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.internal.genericeditor.preferences.GenericEditorPreferenceConstants; -import org.eclipse.ui.texteditor.ChainedPreferenceStore; +import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; /** @@ -64,6 +77,8 @@ public class ExtensionBasedTextEditor extends TextEditor { private ExtensionBasedTextViewerConfiguration configuration; private Image contentTypeImage; private ImageDescriptor contentTypeImageDescripter; + private Set resolvedContentTypes; + private PreferenceStoreWrapper preferenceStoreWrapper; /** * @@ -165,7 +180,9 @@ else if (stateMask == -1 && activeHyperlinkStateMask != fHyperlinkStateMask) public void createPartControl(Composite parent) { super.createPartControl(parent); ProjectionViewer viewer = (ProjectionViewer) getSourceViewer(); - + if (preferenceStoreWrapper != null) { + preferenceStoreWrapper.install(viewer); + } new ProjectionSupport(viewer, getAnnotationAccess(), getSharedColors()).install(); viewer.doOperation(ProjectionViewer.TOGGLE); computeImage(); @@ -174,8 +191,8 @@ public void createPartControl(Composite parent) { @Override protected void initializeEditor() { super.initializeEditor(); - setPreferenceStore(new ChainedPreferenceStore(new IPreferenceStore[] { - GenericEditorPreferenceConstants.getPreferenceStore(), EditorsUI.getPreferenceStore() })); + preferenceStoreWrapper = new PreferenceStoreWrapper(this); + setPreferenceStore(preferenceStoreWrapper); } /** @@ -188,7 +205,7 @@ 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, getContentTypes(viewer.getDocument())); if (!matchers.isEmpty()) { ICharacterPairMatcher matcher = matchers.get(0); support.setCharacterPairMatcher(matcher); @@ -204,18 +221,86 @@ public Image getTitleImage() { private void computeImage() { contentTypeImageDescripter = GenericEditorPlugin.getDefault().getContentTypeImagesRegistry() - .getImageDescriptor(getContentTypes()); + .getImageDescriptor(getContentTypes().toArray(new IContentType[getContentTypes().size()])); if (contentTypeImageDescripter != null) { this.contentTypeImage = contentTypeImageDescripter.createImage(); } } - private IContentType[] getContentTypes() { + Set getContentTypes() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) { - return configuration.getContentTypes(sourceViewer.getDocument()).toArray(new IContentType[] {}); + return getContentTypes(sourceViewer.getDocument()); + } + return Collections.emptySet(); + } + + public Set getContentTypes(IDocument document) { + if (this.resolvedContentTypes != null) { + return this.resolvedContentTypes; + } + return this.resolvedContentTypes = collectContentTypes(document, this); + } + + public static Set collectContentTypes(IDocument document, ITextEditor editor) { + Set collectedContentTypes = new LinkedHashSet<>(); + ITextFileBuffer buffer = getCurrentBuffer(document); + if (buffer != null) { + try { + IContentType contentType = buffer.getContentType(); + if (contentType != null) { + collectedContentTypes.add(contentType); + } + } catch (CoreException ex) { + GenericEditorPlugin.getDefault().getLog() + .log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); + } } - return new IContentType[] {}; + String fileName = getCurrentFileName(editor, buffer); + if (fileName != null) { + Queue types = new LinkedList<>( + Arrays.asList(Platform.getContentTypeManager().findContentTypesFor(fileName))); + while (!types.isEmpty()) { + IContentType type = types.poll(); + collectedContentTypes.add(type); + IContentType parent = type.getBaseType(); + if (parent != null) { + types.add(parent); + } + } + } + return collectedContentTypes; + } + + private static ITextFileBuffer getCurrentBuffer(IDocument document) { + if (document != null) { + return FileBuffers.getTextFileBufferManager().getTextFileBuffer(document); + } + return null; + } + + private static String getCurrentFileName(ITextEditor editor, ITextFileBuffer buffer) { + String fileName = editor != null ? editor.getEditorInput().getName() : null; + if (fileName == null) { + if (buffer != null) { + IPath path = buffer.getLocation(); + if (path != null) { + fileName = path.lastSegment(); + } + } + } + return fileName; + } + + ISourceViewer getViewer() { + return getSourceViewer(); + } + + IResource getResource() { + if (getEditorInput() instanceof IFileEditorInput) { + return (((IFileEditorInput) getEditorInput()).getFile()); + } + return null; } @Override @@ -224,6 +309,10 @@ public void dispose() { this.contentTypeImage.dispose(); this.contentTypeImage = null; } + if (this.preferenceStoreWrapper != null) { + this.preferenceStoreWrapper.uninstall(); + this.preferenceStoreWrapper = null; + } super.dispose(); } } 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..0d10f185f 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 @@ -19,25 +19,15 @@ package org.eclipse.ui.internal.genericeditor; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Queue; import java.util.Set; import java.util.stream.Collectors; -import org.eclipse.core.filebuffers.FileBuffers; -import org.eclipse.core.filebuffers.ITextFileBuffer; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.core.runtime.IPath; -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.AbstractReusableInformationControlCreator; @@ -73,7 +63,6 @@ import org.eclipse.ui.internal.genericeditor.hover.CompositeInformationControlCreator; import org.eclipse.ui.internal.genericeditor.hover.CompositeTextHover; import org.eclipse.ui.internal.genericeditor.markers.MarkerResoltionQuickAssistProcessor; -import org.eclipse.ui.texteditor.ITextEditor; /** * The configuration of the {@link ExtensionBasedTextEditor}. It registers the @@ -86,8 +75,7 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewerConfiguration implements IDocumentPartitioningListener { - private ITextEditor editor; - private Set resolvedContentTypes; + private ExtensionBasedTextEditor editor; private Set fallbackContentTypes = Set.of(); private IDocument document; @@ -98,66 +86,15 @@ public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewe * @param editor the editor we're creating. * @param preferenceStore the preference store. */ - public ExtensionBasedTextViewerConfiguration(ITextEditor editor, IPreferenceStore preferenceStore) { + public ExtensionBasedTextViewerConfiguration(ExtensionBasedTextEditor editor, IPreferenceStore preferenceStore) { super(preferenceStore); this.editor = editor; } - public Set getContentTypes(IDocument document) { - if (this.resolvedContentTypes != null) { - return this.resolvedContentTypes; - } - this.resolvedContentTypes = new LinkedHashSet<>(); - ITextFileBuffer buffer = getCurrentBuffer(document); - if (buffer != null) { - try { - IContentType contentType = buffer.getContentType(); - if (contentType != null) { - this.resolvedContentTypes.add(contentType); - } - } catch (CoreException ex) { - GenericEditorPlugin.getDefault().getLog() - .log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); - } - } - String fileName = getCurrentFileName(document); - if (fileName != null) { - Queue types = new LinkedList<>( - Arrays.asList(Platform.getContentTypeManager().findContentTypesFor(fileName))); - while (!types.isEmpty()) { - IContentType type = types.poll(); - this.resolvedContentTypes.add(type); - IContentType parent = type.getBaseType(); - if (parent != null) { - types.add(parent); - } - } - } - return this.resolvedContentTypes.isEmpty() ? fallbackContentTypes : resolvedContentTypes; - } - - private static ITextFileBuffer getCurrentBuffer(IDocument document) { - if (document != null) { - return FileBuffers.getTextFileBufferManager().getTextFileBuffer(document); - } - return null; - } - - private String getCurrentFileName(IDocument document) { - String fileName = null; - if (this.editor != null) { - fileName = editor.getEditorInput().getName(); - } - if (fileName == null) { - ITextFileBuffer buffer = getCurrentBuffer(document); - if (buffer != null) { - IPath path = buffer.getLocation(); - if (path != null) { - fileName = path.lastSegment(); - } - } - } - return fileName; + private Set getContentTypes(IDocument document) { + Set resolvedContentTypes = editor != null ? editor.getContentTypes(document) + : Collections.emptySet(); + return resolvedContentTypes.isEmpty() ? fallbackContentTypes : resolvedContentTypes; } @Override diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorContentAssistant.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorContentAssistant.java index fcd3797a3..9d597b8b0 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorContentAssistant.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorContentAssistant.java @@ -74,7 +74,7 @@ public GenericEditorContentAssistant( setAutoActivationDelay(0); enableColoredLabels(true); enableAutoActivation(true); - enableAutoActivateCompletionOnType(true); + // enableAutoActivateCompletionOnType(true); setInformationControlCreator(new AbstractReusableInformationControlCreator() { @Override protected IInformationControl doCreateInformationControl(Shell parent) { 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 index a3130c721..65d24c11a 100644 --- 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 @@ -14,6 +14,7 @@ *******************************************************************************/ package org.eclipse.ui.internal.genericeditor; +import org.eclipse.core.resources.proposed.PreferenceStoreProviderRegistry; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; @@ -49,6 +50,7 @@ public class GenericEditorPlugin extends AbstractUIPlugin { private AutoEditStrategyRegistry autoEditStrategyRegistry; private CharacterPairMatcherRegistry characterPairMatcherRegistry; private IconsRegistry editorImagesRegistry; + private PreferenceStoreProviderRegistry preferenceStoreRegistry; private IPropertyChangeListener themeListener; @@ -172,4 +174,11 @@ public synchronized IconsRegistry getContentTypeImagesRegistry() { } return this.editorImagesRegistry; } + + public synchronized PreferenceStoreProviderRegistry getPreferenceStoreRegistry() { + if (this.preferenceStoreRegistry == null) { + this.preferenceStoreRegistry = new PreferenceStoreProviderRegistry(); + } + return preferenceStoreRegistry; + } } diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/IconsRegistry.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/IconsRegistry.java index d5909b9a6..59e2e60ad 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/IconsRegistry.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/IconsRegistry.java @@ -43,14 +43,18 @@ public ImageDescriptor getImageDescriptor(IContentType[] contentTypes) { if (this.outOfSync) { sync(); } - return Arrays.stream(contentTypes).sorted(Collections.reverseOrder(Comparator.comparingInt(ContentTypeSpecializationComparator::depth))).map(extensions::get).filter(Objects::nonNull).findFirst().orElse(null); + return Arrays.stream(contentTypes) + .sorted(Collections.reverseOrder(Comparator.comparingInt(ContentTypeSpecializationComparator::depth))) + .map(extensions::get).filter(Objects::nonNull).findFirst().orElse(null); } private void sync() { Set toRemoveContentTypes = new HashSet<>(this.extensions.keySet()); - for (IConfigurationElement extension : Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID)) { + for (IConfigurationElement extension : Platform.getExtensionRegistry() + .getConfigurationElementsFor(EXTENSION_POINT_ID)) { try { - final String contentTypeId = extension.getAttribute(GenericContentTypeRelatedExtension.CONTENT_TYPE_ATTRIBUTE); + final String contentTypeId = extension + .getAttribute(GenericContentTypeRelatedExtension.CONTENT_TYPE_ATTRIBUTE); if (contentTypeId == null || contentTypeId.isEmpty()) { continue; } @@ -65,10 +69,12 @@ private void sync() { if (icon == null || icon.isEmpty()) { continue; } - ResourceLocator.imageDescriptorFromBundle(extension.getNamespaceIdentifier(), icon).ifPresent(imageDescriptor -> this.extensions.put(contentType, imageDescriptor)); + ResourceLocator.imageDescriptorFromBundle(extension.getNamespaceIdentifier(), icon) + .ifPresent(imageDescriptor -> this.extensions.put(contentType, imageDescriptor)); } } 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/PreferenceStoreWrapper.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/PreferenceStoreWrapper.java new file mode 100644 index 000000000..52259febd --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/PreferenceStoreWrapper.java @@ -0,0 +1,302 @@ +package org.eclipse.ui.internal.genericeditor; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.proposed.IPreferenceStoreProvider; +import org.eclipse.core.resources.proposed.PreferenceStoreProviderRegistry; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.proposed.ITextViewerLifecycle; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.internal.genericeditor.preferences.GenericEditorPreferenceConstants; +import org.eclipse.ui.texteditor.ChainedPreferenceStore; +import org.eclipse.ui.texteditor.proposed.ITextEditorAware; + +public class PreferenceStoreWrapper implements IPreferenceStore, ITextViewerLifecycle { + + private IPreferenceStore delegate; + private boolean computed; + + private ExtensionBasedTextEditor editor; + private Set listeners = new HashSet<>(); + private ITextViewer viewer; + private List customPreferenceStore; + + public PreferenceStoreWrapper(ExtensionBasedTextEditor editor) { + this.editor = editor; + this.delegate = createPreferenceStore(null, null); + } + + public static IPreferenceStore createPreferenceStore(Set contentTypes, + ExtensionBasedTextEditor editor) { + List customStores = collectCustomPreferenceStore(contentTypes, editor); + return createPreferenceStore(customStores); + } + + private static IPreferenceStore createPreferenceStore(List customStores) { + List stores = new ArrayList<>(); + if (customStores != null) { + stores.addAll(customStores); + } + stores.add(GenericEditorPreferenceConstants.getPreferenceStore()); + stores.add(EditorsUI.getPreferenceStore()); + return new ChainedPreferenceStore(stores.toArray(new IPreferenceStore[stores.size()])); + } + + public static List collectCustomPreferenceStore(Set contentTypes, + ExtensionBasedTextEditor editor) { + if (contentTypes != null && editor != null) { + PreferenceStoreProviderRegistry registry = GenericEditorPlugin.getDefault().getPreferenceStoreRegistry(); + List providers = registry.getPreferenceStoreProviders(editor.getViewer(), editor, + contentTypes); + List stores = new ArrayList<>(); + for (IPreferenceStoreProvider provider : providers) { + IPreferenceStore editorStore = provider.getPreferenceStore(editor.getResource()); + if (editorStore != null) { + if (editorStore instanceof ITextEditorAware) { + ((ITextEditorAware) editorStore).setEditor(editor); + } + stores.add(editorStore); + } + } + return stores; + } + return null; + } + + @Override + public void addPropertyChangeListener(IPropertyChangeListener arg0) { + listeners.add(arg0); + if (delegate != null) { + getDelegate().addPropertyChangeListener(arg0); + } + } + + @Override + public boolean contains(String arg0) { + return getDelegate().contains(arg0); + } + + @Override + public void firePropertyChangeEvent(String arg0, Object arg1, Object arg2) { + getDelegate().firePropertyChangeEvent(arg0, arg1, arg2); + } + + @Override + public boolean getBoolean(String arg0) { + return getDelegate().getBoolean(arg0); + } + + @Override + public boolean getDefaultBoolean(String arg0) { + return getDelegate().getDefaultBoolean(arg0); + } + + @Override + public double getDefaultDouble(String arg0) { + return getDelegate().getDefaultDouble(arg0); + } + + @Override + public float getDefaultFloat(String arg0) { + return getDelegate().getDefaultFloat(arg0); + } + + @Override + public int getDefaultInt(String arg0) { + return getDelegate().getDefaultInt(arg0); + } + + @Override + public long getDefaultLong(String arg0) { + return getDelegate().getDefaultLong(arg0); + } + + @Override + public String getDefaultString(String arg0) { + return getDelegate().getDefaultString(arg0); + } + + @Override + public double getDouble(String arg0) { + return getDelegate().getDouble(arg0); + } + + @Override + public float getFloat(String arg0) { + return getDelegate().getFloat(arg0); + } + + @Override + public int getInt(String arg0) { + return getDelegate().getInt(arg0); + } + + @Override + public long getLong(String arg0) { + return getDelegate().getLong(arg0); + } + + @Override + public String getString(String arg0) { + return getDelegate().getString(arg0); + } + + @Override + public boolean isDefault(String arg0) { + return getDelegate().isDefault(arg0); + } + + @Override + public boolean needsSaving() { + return getDelegate().needsSaving(); + } + + @Override + public void putValue(String arg0, String arg1) { + getDelegate().putValue(arg0, arg1); + } + + @Override + public void removePropertyChangeListener(IPropertyChangeListener arg0) { + listeners.remove(arg0); + getDelegate().removePropertyChangeListener(arg0); + } + + @Override + public void setDefault(String arg0, boolean arg1) { + getDelegate().setDefault(arg0, arg1); + } + + @Override + public void setDefault(String arg0, double arg1) { + getDelegate().setDefault(arg0, arg1); + } + + @Override + public void setDefault(String arg0, float arg1) { + getDelegate().setDefault(arg0, arg1); + } + + @Override + public void setDefault(String arg0, int arg1) { + getDelegate().setDefault(arg0, arg1); + } + + @Override + public void setDefault(String arg0, long arg1) { + getDelegate().setDefault(arg0, arg1); + } + + @Override + public void setDefault(String arg0, String arg1) { + getDelegate().setDefault(arg0, arg1); + } + + @Override + public void setToDefault(String arg0) { + getDelegate().setToDefault(arg0); + } + + @Override + public void setValue(String arg0, boolean arg1) { + getDelegate().setValue(arg0, arg1); + } + + @Override + public void setValue(String arg0, double arg1) { + getDelegate().setValue(arg0, arg1); + } + + @Override + public void setValue(String arg0, float arg1) { + getDelegate().setValue(arg0, arg1); + } + + @Override + public void setValue(String arg0, int arg1) { + getDelegate().setValue(arg0, arg1); + } + + @Override + public void setValue(String arg0, long arg1) { + getDelegate().setValue(arg0, arg1); + } + + @Override + public void setValue(String arg0, String arg1) { + getDelegate().setValue(arg0, arg1); + } + + private IPreferenceStore getDelegate() { + if (!computed) { + delegate = getPreferenceStore(); + } + return delegate; + } + + private synchronized IPreferenceStore getPreferenceStore() { + if (computed) { + return delegate; + } + IPreferenceStore store = createCustomPreferenceStore(); + if (store != null) { + for (IPropertyChangeListener listener : listeners) { + store.addPropertyChangeListener(listener); + } + for (IPropertyChangeListener listener : listeners) { + delegate.removePropertyChangeListener(listener); + } + delegate = store; + computed = true; + } + return delegate; + } + + IPreferenceStore createCustomPreferenceStore() { + Set contentTypes = editor.getContentTypes(); + if (contentTypes == null || contentTypes.isEmpty()) { + return null; + } + customPreferenceStore = collectCustomPreferenceStore(contentTypes, editor); + if (customPreferenceStore != null) { + installIfNeeded(); + return createPreferenceStore(customPreferenceStore); + } + return null; + } + + private void installIfNeeded() { + if (viewer != null) { + for (IPreferenceStore store : customPreferenceStore) { + if (store instanceof ITextViewerLifecycle) { + ((ITextViewerLifecycle) store).install(viewer); + } + } + } + } + + @Override + public void install(ITextViewer viewer) { + this.viewer = viewer; + installIfNeeded(); + } + + @Override + public void uninstall() { + if (viewer != null && customPreferenceStore != null) { + for (IPreferenceStore store : customPreferenceStore) { + if (store instanceof ITextViewerLifecycle) { + ((ITextViewerLifecycle) store).uninstall(); + } + } + this.customPreferenceStore = null; + } + this.viewer = null; + } +} \ No newline at end of file diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/compare/GenericEditorMergeViewer.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/compare/GenericEditorMergeViewer.java index c6a384552..7850a380f 100644 --- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/compare/GenericEditorMergeViewer.java +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/compare/GenericEditorMergeViewer.java @@ -19,17 +19,15 @@ import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.contentmergeviewer.TextMergeViewer; import org.eclipse.core.runtime.content.IContentType; -import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextInputListener; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.swt.widgets.Composite; -import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.internal.genericeditor.ExtensionBasedTextEditor; import org.eclipse.ui.internal.genericeditor.ExtensionBasedTextViewerConfiguration; -import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; -import org.eclipse.ui.texteditor.ChainedPreferenceStore; +import org.eclipse.ui.internal.genericeditor.PreferenceStoreWrapper; public class GenericEditorMergeViewer extends TextMergeViewer { @@ -45,8 +43,7 @@ protected SourceViewer createSourceViewer(Composite parent, int textOrientation) res.addTextInputListener(new ITextInputListener() { @Override public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { - fallbackContentTypes - .addAll(new ExtensionBasedTextViewerConfiguration(null, null).getContentTypes(newInput)); + fallbackContentTypes.addAll(ExtensionBasedTextEditor.collectContentTypes(newInput, null)); configureTextViewer(res); } @@ -62,8 +59,8 @@ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput protected void configureTextViewer(TextViewer textViewer) { if (textViewer.getDocument() != null && textViewer instanceof ISourceViewer) { ExtensionBasedTextViewerConfiguration configuration = new ExtensionBasedTextViewerConfiguration(null, - new ChainedPreferenceStore(new IPreferenceStore[] { EditorsUI.getPreferenceStore(), - GenericEditorPlugin.getDefault().getPreferenceStore() })); + PreferenceStoreWrapper.createPreferenceStore( + ExtensionBasedTextEditor.collectContentTypes(textViewer.getDocument(), null), null)); configuration.setFallbackContentTypes(fallbackContentTypes); ((ISourceViewer) textViewer).configure(configuration); } diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/texteditor/proposed/ITextEditorAware.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/texteditor/proposed/ITextEditorAware.java new file mode 100644 index 000000000..f03cebaa2 --- /dev/null +++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/texteditor/proposed/ITextEditorAware.java @@ -0,0 +1,8 @@ +package org.eclipse.ui.texteditor.proposed; + +import org.eclipse.ui.texteditor.ITextEditor; + +public interface ITextEditorAware { + + void setEditor(ITextEditor editor); +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractTextEditor.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractTextEditor.java index 878a579a5..765375329 100644 --- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractTextEditor.java +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractTextEditor.java @@ -6175,6 +6175,10 @@ public T getAdapter(Class required) { if (ITextViewer.class.equals(required)) return (fSourceViewer == null ? null : (T) fSourceViewer); + if (IPreferenceStore.class.equals(required)) { + return (T) getPreferenceStore(); + } + return super.getAdapter(required); }