Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiko Stotz2019-05-26 21:26:57 +0000
committerMickael Istria2019-11-07 08:05:16 +0000
commitf91dbfae8dd72106f7cb7950b5fd66133a8e7546 (patch)
treef03f6b2507671b4d3fd50208c18d43dbfbbbc3d5
parent386393cc26d2b18b9a77ae91a14eb34a535ce8f0 (diff)
downloadeclipse.platform.text-f91dbfae8dd72106f7cb7950b5fd66133a8e7546.tar.gz
eclipse.platform.text-f91dbfae8dd72106f7cb7950b5fd66133a8e7546.tar.xz
eclipse.platform.text-f91dbfae8dd72106f7cb7950b5fd66133a8e7546.zip
Show info, warning, and error Annotations as line header code minings.
Implements bug 547665 Based on code of bug 540443 Generic implementation for code mining-enabled editors. Amends text editor user settings page for configuration. Change-Id: Ia20c4b0712bdb45d1061daa6d9af4b4ab21be655 Signed-off-by: Niko Stotz <eclipse@nikostotz.de>
-rw-r--r--org.eclipse.ui.editors/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.ui.editors/plugin.xml20
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/EditorsPluginPreferenceInitializer.java2
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java37
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java8
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties8
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMining.java65
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningFilter.java239
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningPreferenceConstants.java136
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningPreferences.java97
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningProvider.java342
-rw-r--r--org.eclipse.ui.genericeditor/plugin.xml7
12 files changed, 964 insertions, 1 deletions
diff --git a/org.eclipse.ui.editors/META-INF/MANIFEST.MF b/org.eclipse.ui.editors/META-INF/MANIFEST.MF
index 395f4ff07..7f9cc478e 100644
--- a/org.eclipse.ui.editors/META-INF/MANIFEST.MF
+++ b/org.eclipse.ui.editors/META-INF/MANIFEST.MF
@@ -12,6 +12,7 @@ Export-Package:
org.eclipse.ui.editors.text.templates,
org.eclipse.ui.internal.editors.quickdiff;x-internal:=true,
org.eclipse.ui.internal.editors.text;x-internal:=true,
+ org.eclipse.ui.internal.editors.text.codemining.annotation;x-internal:=true,
org.eclipse.ui.internal.texteditor;x-internal:=true,
org.eclipse.ui.texteditor
Require-Bundle:
@@ -25,7 +26,8 @@ Require-Bundle:
org.eclipse.ui.workbench.texteditor;bundle-version="[3.14.0,4.0.0)",
org.eclipse.core.filebuffers;visibility:=reexport;bundle-version="[3.5.0,4.0.0)",
org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
- org.eclipse.core.filesystem;bundle-version="[1.2.0,2.0.0)"
+ org.eclipse.core.filesystem;bundle-version="[1.2.0,2.0.0)",
+ org.eclipse.jdt.annotation;bundle-version="2.2";resolution:=optional
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.ibm.icu.text
Automatic-Module-Name: org.eclipse.ui.editors
diff --git a/org.eclipse.ui.editors/plugin.xml b/org.eclipse.ui.editors/plugin.xml
index 1edf63292..cc4828d69 100644
--- a/org.eclipse.ui.editors/plugin.xml
+++ b/org.eclipse.ui.editors/plugin.xml
@@ -1063,4 +1063,24 @@
</extension>
+ <extension
+ point="org.eclipse.ui.workbench.texteditor.codeMiningProviders">
+ <codeMiningProvider
+ class="org.eclipse.ui.internal.editors.text.codemining.annotation.AnnotationCodeMiningProvider"
+ id="org.eclipse.ui.internal.editors.annotationCodeMiningProvider">
+ <enabledWhen>
+ <with
+ variable="editorInput">
+ <adapt
+ type="org.eclipse.core.resources.IFile">
+ <test
+ property="org.eclipse.core.resources.contentTypeId"
+ value="org.eclipse.core.runtime.text"
+ args="kindOf">
+ </test>
+ </adapt>
+ </with>
+ </enabledWhen>
+ </codeMiningProvider>
+ </extension>
</plugin>
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/EditorsPluginPreferenceInitializer.java b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/EditorsPluginPreferenceInitializer.java
index 0e5eed62e..2c16f3afe 100644
--- a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/EditorsPluginPreferenceInitializer.java
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/EditorsPluginPreferenceInitializer.java
@@ -22,6 +22,7 @@ import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.editors.text.codemining.annotation.AnnotationCodeMiningPreferenceConstants;
import org.eclipse.ui.internal.texteditor.ITextEditorThemeConstants;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
@@ -42,6 +43,7 @@ public class EditorsPluginPreferenceInitializer extends AbstractPreferenceInitia
IPreferenceStore store= EditorsPlugin.getDefault().getPreferenceStore();
TextEditorPreferenceConstants.initializeDefaultValues(store);
migrateOverviewRulerPreference(store);
+ AnnotationCodeMiningPreferenceConstants.initializeDefaultValues(store);
}
public static void setThemeBasedPreferences(IPreferenceStore store, boolean fireEvent) {
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java
index 47c22c621..49be4ce04 100644
--- a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java
@@ -74,6 +74,7 @@ import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.internal.editors.text.OverlayPreferenceStore.OverlayKey;
import org.eclipse.ui.internal.editors.text.TextEditorDefaultsPreferencePage.EnumeratedDomain.EnumValue;
+import org.eclipse.ui.internal.editors.text.codemining.annotation.AnnotationCodeMiningPreferenceConstants;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AbstractTextEditor;
@@ -771,6 +772,9 @@ public class TextEditorDefaultsPreferencePage extends PreferencePage implements
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SHOW_TEXT_HOVER_AFFORDANCE));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.INT, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_HOVER_ENRICH_MODE));
+ overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.INT, AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL));
+ overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.INT, AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_MAX));
+
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SHOW_LEADING_SPACES));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SHOW_ENCLOSED_SPACES));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SHOW_TRAILING_SPACES));
@@ -941,6 +945,39 @@ public class TextEditorDefaultsPreferencePage extends PreferencePage implements
Preference smartHomeEnd= new Preference(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SMART_HOME_END, label, null);
addCheckBox(appearanceComposite, smartHomeEnd, new BooleanDomain(), 0);
+ label= TextEditorMessages.TextEditorDefaultsPreferencePage_codeMinings_show;
+ String description= TextEditorMessages.TextEditorDefaultsPreferencePage_codeMinings_description;
+ Preference showCodeMinings= new Preference(AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL, label, description);
+ EnumeratedDomain codeMiningsDomain= new EnumeratedDomain();
+ codeMiningsDomain.addValue(new EnumValue(AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__NONE, TextEditorMessages.TextEditorDefaultsPreferencePage_codeMinings_none));
+ codeMiningsDomain.addValue(new EnumValue(AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR, TextEditorMessages.TextEditorDefaultsPreferencePage_codeMinings_error));
+ codeMiningsDomain.addValue(new EnumValue(AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR_WARNING,
+ TextEditorMessages.TextEditorDefaultsPreferencePage_codeMinings_ErrorWarnings));
+ codeMiningsDomain.addValue(new EnumValue(AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR_WARNING_INFO,
+ TextEditorMessages.TextEditorDefaultsPreferencePage_codeMinings_ErrowWarningsInfo));
+ final Control[] showCodeMiningsControls= addCombo(appearanceComposite, showCodeMinings, codeMiningsDomain, 0);
+
+ label= TextEditorMessages.TextEditorDefaultsPreferencePage_codeMinings_max;
+ description= TextEditorMessages.TextEditorDefaultsPreferencePage_codeMinings_max_description;
+ Preference maxCodeMinings= new Preference(AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_MAX, label, description);
+ IntegerDomain maxCodeMiningsDomain= new IntegerDomain(0, 99999);
+ Control[] maxCodeMiningsControls= addTextField(appearanceComposite, maxCodeMinings, maxCodeMiningsDomain, 15, 20);
+
+ final SelectionListener codeMiningsListener= new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ final int showCodeMiningsSetting= fOverlayStore.getInt(showCodeMinings.getKey());
+ boolean enabled= showCodeMiningsSetting != AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__NONE;
+ for (Control control : maxCodeMiningsControls) {
+ control.setEnabled(enabled);
+ }
+ }
+ };
+
+ ((Combo) showCodeMiningsControls[1]).addSelectionListener(codeMiningsListener);
+ fMasterSlaveListeners.add(codeMiningsListener);
+
+
addFiller(appearanceComposite, 2);
Label l= new Label(appearanceComposite, SWT.LEFT);
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java
index 93e6ea4c5..2ff3e9ed0 100644
--- a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java
@@ -137,6 +137,14 @@ final class TextEditorMessages extends NLS {
public static String TextEditorDefaultsPreferencePage_carriageReturn;
public static String TextEditorDefaultsPreferencePage_transparencyLevel;
+ public static String TextEditorDefaultsPreferencePage_codeMinings_description;
+ public static String TextEditorDefaultsPreferencePage_codeMinings_error;
+ public static String TextEditorDefaultsPreferencePage_codeMinings_ErrorWarnings;
+ public static String TextEditorDefaultsPreferencePage_codeMinings_ErrowWarningsInfo;
+ public static String TextEditorDefaultsPreferencePage_codeMinings_max;
+ public static String TextEditorDefaultsPreferencePage_codeMinings_max_description;
+ public static String TextEditorDefaultsPreferencePage_codeMinings_none;
+ public static String TextEditorDefaultsPreferencePage_codeMinings_show;
public static String TextEditorDefaultsPreferencePage_configureWhitespaceCharacterPainterProperties;
public static String TextEditorDefaultsPreferencePage_deleteSpacesAsTabs;
public static String TextEditorDefaultsPreferencePage_enclosed;
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties
index a316d2436..c41c9eb9b 100644
--- a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties
@@ -39,6 +39,14 @@ TextEditorPreferencePage_accessibility_wideCaret= &Enable thick caret
TextEditorPreferencePage_accessibility_useSaturatedColorsInOverviewRuler=U&se saturated colors in overview ruler
TextEditorDefaultsPreferencePage_carriageReturn=Carriage Return ( \u00a4 )
TextEditorDefaultsPreferencePage_transparencyLevel=&Transparency level (0 is transparent and 255 is opaque):
+TextEditorDefaultsPreferencePage_codeMinings_description=How annotations should be shown in-line in text editors which support Code Minings
+TextEditorDefaultsPreferencePage_codeMinings_error=Error
+TextEditorDefaultsPreferencePage_codeMinings_ErrorWarnings=Error / Warnings
+TextEditorDefaultsPreferencePage_codeMinings_ErrowWarningsInfo=Error / Warnings / Info
+TextEditorDefaultsPreferencePage_codeMinings_max=Maximum annotations shown:
+TextEditorDefaultsPreferencePage_codeMinings_max_description=Limits the number of shown annotations to prevent performance issues
+TextEditorDefaultsPreferencePage_codeMinings_none=None
+TextEditorDefaultsPreferencePage_codeMinings_show=Show Code Minings for Annotations:
TextEditorDefaultsPreferencePage_configureWhitespaceCharacterPainterProperties=Configure visibility of whitespace characters in different regions of a line of text:
TextEditorDefaultsPreferencePage_deleteSpacesAsTabs=Remove &multiple spaces on backspace/delete
TextEditorDefaultsPreferencePage_enclosed=Enclosed
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMining.java b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMining.java
new file mode 100644
index 000000000..2453a30a1
--- /dev/null
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMining.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Altran Netherlands B.V. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Niko Stotz (Altran Netherlands B.V.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.editors.text.codemining.annotation;
+
+import java.util.function.Consumer;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.codemining.ICodeMiningProvider;
+import org.eclipse.jface.text.codemining.LineHeaderCodeMining;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationAccessExtension;
+
+/**
+ * Draws an Annotation's text and icon as Line header code mining.
+ *
+ * @since 3.13
+ */
+@NonNullByDefault
+public class AnnotationCodeMining extends LineHeaderCodeMining {
+ private final Annotation annotation;
+
+ private final IAnnotationAccessExtension annotationAccess;
+
+ public AnnotationCodeMining(IAnnotationAccessExtension annotationAccess, Annotation annotation, int lineNumber, IDocument document, ICodeMiningProvider provider,
+ @Nullable Consumer<MouseEvent> action) throws BadLocationException {
+ super(lineNumber, document, provider, action);
+ this.annotationAccess= annotationAccess;
+
+ setLabel(annotation.getText());
+
+ this.annotation= annotation;
+ }
+
+ @Override
+ @SuppressWarnings("null")
+ public Point draw(GC gc, StyledText textWidget, Color color, int x, int y) {
+ final int width= 16;
+ annotationAccess.paint(this.annotation, gc, textWidget, new Rectangle(x, y, width, 16));
+ final Point result= super.draw(gc, textWidget, color, x + width, y);
+ result.x+= width;
+ return result;
+ }
+}
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningFilter.java b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningFilter.java
new file mode 100644
index 000000000..f855ba8a2
--- /dev/null
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningFilter.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Altran Netherlands B.V. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Niko Stotz (Altran Netherlands B.V.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.editors.text.codemining.annotation;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import org.eclipse.core.resources.IMarker;
+
+import org.eclipse.jface.text.quickassist.IQuickFixableAnnotation;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationAccessExtension;
+import org.eclipse.jface.text.source.IAnnotationPresentation;
+
+import org.eclipse.ui.texteditor.MarkerAnnotation;
+
+/**
+ * Filters and arranges Annotations that are suitable as code minings. Takes user preferences into
+ * account.
+ *
+ * @since 3.13
+ */
+@NonNullByDefault
+public class AnnotationCodeMiningFilter {
+ /**
+ * Callback to locate an Annotation inside the editor.
+ */
+ public interface Locator {
+ public @Nullable Integer getOffset(Annotation annotation);
+
+ public @Nullable Integer getLine(Annotation annotation);
+ }
+
+ final private IAnnotationAccessExtension annotationAccess;
+
+ final private AnnotationCodeMiningPreferences preferences= new AnnotationCodeMiningPreferences();
+
+ final private Stream<Annotation> annotations;
+
+ public AnnotationCodeMiningFilter(IAnnotationAccessExtension annotationAccess, Annotation[]... annotations) {
+ this.annotationAccess= annotationAccess;
+ this.annotations= Arrays.stream(annotations).flatMap(Arrays::stream);
+ }
+
+ public AnnotationCodeMiningFilter(IAnnotationAccessExtension annotationAccess, Iterator<Annotation> annotations) {
+ this.annotationAccess= annotationAccess;
+ this.annotations= StreamSupport.stream(Spliterators.spliteratorUnknownSize(annotations, Spliterator.ORDERED), false);
+ }
+
+ /**
+ * Checks if there are any suitable annotations.
+ */
+ public boolean isEmpty() {
+ return !filterReTrigger(annotations).findAny().isPresent();
+ }
+
+ /**
+ * Returns all suitable annotations.
+ */
+ public Stream<Annotation> sortDistinctLimit(Locator locator) {
+ return limit(distinct(locator, sort(locator, filterShown(filterReTrigger(annotations)))));
+ }
+
+ /**
+ * Filters suitable annotations to decide whether we need to re-trigger code minings.
+ */
+ private Stream<Annotation> filterReTrigger(Stream<Annotation> anns) {
+ return anns
+ .filter(this::isTypeProcessable)
+ .filter(this::isPaintable)
+ .filter(this::isInScope);
+ }
+
+ private boolean isTypeProcessable(Annotation a) {
+ return a instanceof MarkerAnnotation ||
+ a instanceof IQuickFixableAnnotation ||
+ a instanceof IAnnotationPresentation;
+ }
+
+ private boolean isPaintable(Annotation a) {
+ // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=552760
+ // (NPE on JavaAnnotationImageProvider.getQuickFixErrorImage(): No Display)
+ try {
+ return annotationAccess.isPaintable(a);
+ } catch (NullPointerException e) {
+ return false;
+ }
+ }
+
+ private boolean isInScope(Annotation a) {
+ return isError(a) || isWarning(a) || isInfo(a);
+ }
+
+ /**
+ * Filters suitable annotations to show.
+ */
+ private Stream<Annotation> filterShown(Stream<Annotation> anns) {
+ return anns
+ .filter(a -> !a.isMarkedDeleted())
+ .filter(this::isEnabled);
+ }
+
+ private boolean isEnabled(Annotation a) {
+ if (isError(a)) {
+ return preferences.isErrorEnabled();
+ } else if (isWarning(a)) {
+ return preferences.isWarningEnabled();
+ } else if (isInfo(a)) {
+ return preferences.isInfoEnabled();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Sorts annotations based on 1) position in text, 2) layer, 3) severity, 4) text.
+ */
+ private Stream<Annotation> sort(Locator locator, Stream<Annotation> anns) {
+ return anns.sorted((a, b) -> {
+ int resultPosition= comparePosition(locator, a, b);
+ if (resultPosition != 0) {
+ return resultPosition;
+ }
+
+ final int resultLayer= compareLayer(a, b);
+ if (resultLayer != 0) {
+ return resultLayer;
+ }
+
+ final int resultSeverity= compareSeverity(a, b);
+ if (resultSeverity != 0) {
+ return resultSeverity;
+ }
+
+ return a.getText().compareTo(b.getText());
+ });
+ }
+
+ private int comparePosition(Locator locator, Annotation a, Annotation b) {
+ final Integer aOffset= locator.getOffset(a);
+ final Integer bOffset= locator.getOffset(b);
+
+ if (aOffset == null || bOffset == null) {
+ return 0;
+ }
+
+ int resultPosition= Integer.compare(aOffset, bOffset);
+ return resultPosition;
+ }
+
+ private int compareLayer(Annotation a, Annotation b) {
+ final int resultPriority= Integer.compare(annotationAccess.getLayer(a), annotationAccess.getLayer(b));
+ return resultPriority;
+ }
+
+ private int compareSeverity(Annotation a, Annotation b) {
+ final int resultSeverity= Integer.compare(getSeverity(a), getSeverity(b));
+ return resultSeverity;
+ }
+
+ private int getSeverity(Annotation a) {
+ if (isError(a)) {
+ return IMarker.SEVERITY_ERROR;
+ } else if (isWarning(a)) {
+ return IMarker.SEVERITY_WARNING;
+ } else if (isInfo(a)) {
+ return IMarker.SEVERITY_INFO;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Assures annotations are distinct by line and text. Required, as sometimes the same message is
+ * shown at the same place from different annotations.
+ */
+ @SuppressWarnings("null")
+ private Stream<Annotation> distinct(Locator locator, Stream<Annotation> anns) {
+ return anns
+ .filter(distinctByKey(a -> {
+ final Integer line= locator.getLine(a);
+ if (line == null) {
+ return null;
+ }
+ String key= line + a.getText();
+
+ return key;
+ }))
+ .filter(Objects::nonNull);
+ }
+
+ /**
+ * Limits annotations to user-defined amount.
+ */
+ private Stream<Annotation> limit(Stream<Annotation> anns) {
+ return anns.limit(preferences.getMaxMinings());
+ }
+
+ private boolean isInfo(Annotation a) {
+ return annotationAccess.isSubtype(a.getType(), "org.eclipse.ui.workbench.texteditor.info"); //$NON-NLS-1$
+ }
+
+ private boolean isWarning(Annotation a) {
+ return annotationAccess.isSubtype(a.getType(), "org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$
+ }
+
+ private boolean isError(Annotation a) {
+ return annotationAccess.isSubtype(a.getType(), "org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$
+ }
+
+ @SuppressWarnings("null")
+ public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
+ Map<Object, Boolean> seen= new LinkedHashMap<>();
+ return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
+ }
+}
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningPreferenceConstants.java b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningPreferenceConstants.java
new file mode 100644
index 000000000..2ac45d870
--- /dev/null
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningPreferenceConstants.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Altran Netherlands B.V. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Niko Stotz (Altran Netherlands B.V.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.editors.text.codemining.annotation;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.ui.internal.editors.text.EditorsPlugin;
+
+/**
+ * Preference constants used for the annotation code mining preference store.
+ *
+ * @since 3.13
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class AnnotationCodeMiningPreferenceConstants {
+ private AnnotationCodeMiningPreferenceConstants() {
+
+ }
+
+ /**
+ * A named preference that controls which {@link org.eclipse.jface.text.source.Annotation
+ * Annotations} level should be shown as code minings.
+ * <p>
+ * Value is of type <code>Integer</code>.
+ * </p>
+ *
+ * @since 3.13
+ */
+ public final static String SHOW_ANNOTATION_CODE_MINING_LEVEL= "showAnnotationAsCodeMiningLevel"; //$NON-NLS-1$
+
+ /**
+ * Value for {@link #SHOW_ANNOTATION_CODE_MINING_LEVEL} to show no annotation code minings.
+ *
+ * @since 3.13
+ */
+ public final static int SHOW_ANNOTATION_CODE_MINING_LEVEL__NONE= 0b0;
+
+ /**
+ * Value for {@link #SHOW_ANNOTATION_CODE_MINING_LEVEL} to show info annotation code minings.
+ *
+ * @since 3.13
+ */
+ public final static int SHOW_ANNOTATION_CODE_MINING_LEVEL__INFO= 0b10;
+
+ /**
+ * Value for {@link #SHOW_ANNOTATION_CODE_MINING_LEVEL} to show warning annotation code minings.
+ *
+ * @since 3.13
+ */
+ public final static int SHOW_ANNOTATION_CODE_MINING_LEVEL__WARNING= 0b100;
+
+ /**
+ * Value for {@link #SHOW_ANNOTATION_CODE_MINING_LEVEL} to show error annotation code minings.
+ *
+ * @since 3.13
+ */
+ public final static int SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR= 0b1000;
+
+ /**
+ * Value for {@link #SHOW_ANNOTATION_CODE_MINING_LEVEL} to show error and warning annotation
+ * code minings.
+ *
+ * @since 3.13
+ */
+ public final static int SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR_WARNING= SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR
+ | SHOW_ANNOTATION_CODE_MINING_LEVEL__WARNING;
+
+ /**
+ * Value for {@link #SHOW_ANNOTATION_CODE_MINING_LEVEL} to show error, warning, and info
+ * annotation code minings.
+ *
+ * @since 3.13
+ */
+ public final static int SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR_WARNING_INFO= SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR
+ | SHOW_ANNOTATION_CODE_MINING_LEVEL__WARNING
+ | SHOW_ANNOTATION_CODE_MINING_LEVEL__INFO;
+
+ /**
+ * Default value for {@link #SHOW_ANNOTATION_CODE_MINING_LEVEL}.
+ *
+ * @since 3.13
+ */
+ public final static int SHOW_ANNOTATION_CODE_MINING_LEVEL__DEFAULT= SHOW_ANNOTATION_CODE_MINING_LEVEL__NONE;
+
+ /**
+ * A named preference that controls how many {@link org.eclipse.jface.text.source.Annotation
+ * Annotations}s should be shown at most as code minings.
+ * <p>
+ * Value is of type <code>Integer</code>.
+ * </p>
+ *
+ * @since 3.13
+ */
+ public final static String SHOW_ANNOTATION_CODE_MINING_MAX= "showAnnotationAsCodeMiningMax"; //$NON-NLS-1$
+
+ /**
+ * Default value for {@link #SHOW_ANNOTATION_CODE_MINING_MAX}.
+ *
+ * @since 3.13
+ */
+ public final static int SHOW_ANNOTATION_CODE_MINING_MAX__DEFAULT= 100;
+
+ /**
+ * Returns the Generic Editor preference store.
+ *
+ * @return the Generic Editor preference store
+ */
+ public static IPreferenceStore getPreferenceStore() {
+ return EditorsPlugin.getDefault().getPreferenceStore();
+ }
+
+ /**
+ * Initializes the given preference store with the default values.
+ *
+ * @param store the preference store to be initialized
+ *
+ * @since 3.13
+ */
+ public static void initializeDefaultValues(IPreferenceStore store) {
+ store.setDefault(SHOW_ANNOTATION_CODE_MINING_LEVEL, SHOW_ANNOTATION_CODE_MINING_LEVEL__DEFAULT);
+ store.setDefault(SHOW_ANNOTATION_CODE_MINING_MAX, SHOW_ANNOTATION_CODE_MINING_MAX__DEFAULT);
+ }
+
+}
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningPreferences.java b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningPreferences.java
new file mode 100644
index 000000000..fc8a2f779
--- /dev/null
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningPreferences.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Altran Netherlands B.V. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Niko Stotz (Altran Netherlands B.V.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.editors.text.codemining.annotation;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+/**
+ * Simplifies access to user preferences related to Annotation-based code minings.
+ *
+ * <p>
+ * All methods fall back to defaults if the preference store is unavailable.
+ * </p>
+ *
+ * <p>
+ * The following preferences are available:
+ * </p>
+ *
+ * <dl>
+ * <dt>show infos <i>(default: <tt>false</tt>)</i></dt>
+ * <dd>Whether INFO-level annotations should be shown as code minings.</dd>
+ *
+ * <dt>show warnings <i>(default: <tt>false</tt>)</i></dt>
+ * <dd>Whether WARNING-level annotations should be shown as code minings.</dd>
+ *
+ * <dt>show errors <i>(default: <tt>false</tt>)</i></dt>
+ * <dd>Whether ERROR-level annotations should be shown as code minings.</dd>
+ *
+ * <dt>Maximum annotations shown <i>(default: <tt>100</tt>)</i></dt>
+ * <dd>How many annotations should be shown at most as code minings. Mainly to prevent bad editor
+ * performance.</dd>
+ * </dl>
+ *
+ * @since 3.13
+ */
+public class AnnotationCodeMiningPreferences {
+ private IPreferenceStore preferenceStore;
+
+ public boolean isInfoEnabled() {
+ return (getLevel() & AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__INFO) > 0;
+ }
+
+ public boolean isWarningEnabled() {
+ return (getLevel() & AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__WARNING) > 0;
+ }
+
+ public boolean isErrorEnabled() {
+ return (getLevel() & AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__ERROR) > 0;
+ }
+
+ public boolean isEnabled() {
+ final IPreferenceStore node= getPreferences();
+ if (node == null) {
+ return false;
+ }
+
+ final int max= getMaxMinings();
+
+ return max > 0 && getLevel() > 0;
+ }
+
+ int getLevel() {
+ IPreferenceStore node= getPreferences();
+
+ return node != null
+ ? node.getInt(AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL)
+ : AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL__DEFAULT;
+ }
+
+ int getMaxMinings() {
+ final IPreferenceStore node= getPreferences();
+
+ return node != null
+ ? node.getInt(AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_MAX)
+ : AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_MAX__DEFAULT;
+ }
+
+ protected @Nullable IPreferenceStore getPreferences() {
+ if (preferenceStore == null) {
+ preferenceStore= AnnotationCodeMiningPreferenceConstants.getPreferenceStore();
+ }
+
+ return preferenceStore;
+ }
+}
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningProvider.java b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningProvider.java
new file mode 100644
index 000000000..ed6be29f2
--- /dev/null
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/codemining/annotation/AnnotationCodeMiningProvider.java
@@ -0,0 +1,342 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Altran Netherlands B.V. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Niko Stotz (Altran Netherlands B.V.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.editors.text.codemining.annotation;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import org.eclipse.swt.events.MouseEvent;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.codemining.AbstractCodeMining;
+import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider;
+import org.eclipse.jface.text.codemining.ICodeMining;
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
+import org.eclipse.jface.text.quickassist.IQuickFixableAnnotation;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationModelEvent;
+import org.eclipse.jface.text.source.IAnnotationAccess;
+import org.eclipse.jface.text.source.IAnnotationAccessExtension;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelListener;
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
+import org.eclipse.jface.text.source.ISourceViewerExtension2;
+import org.eclipse.jface.text.source.ISourceViewerExtension3;
+import org.eclipse.jface.text.source.ISourceViewerExtension5;
+
+import org.eclipse.ui.internal.editors.text.EditorsPlugin;
+
+import org.eclipse.ui.editors.text.EditorsUI;
+
+/**
+ * Shows <i>info</i>, <i>warning</i>, and <i>error</i> Annotations as line header code minings.
+ *
+ * <p>
+ * If the annotation is quickfixable, clicking on the code mining triggers the quickfix.
+ * </p>
+ * <p>
+ * The user can configure which and how many Annotations should be shown in preferences.
+ * </p>
+ * <p>
+ * Works out-of-the-box for all code mining-enabled text editors.
+ * </p>
+ *
+ * @since 3.13
+ * @see org.eclipse.ui.internal.editors.text.codemining.annotation.AnnotationCodeMiningPreferences
+ */
+@NonNullByDefault
+public class AnnotationCodeMiningProvider extends AbstractCodeMiningProvider
+ implements AnnotationCodeMiningFilter.Locator {
+
+ /**
+ * Updates code minings after changes to preferences.
+ */
+ private class PropertyChangeListener implements IPropertyChangeListener {
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ switch (event.getProperty()) {
+ case AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_LEVEL:
+ case AnnotationCodeMiningPreferenceConstants.SHOW_ANNOTATION_CODE_MINING_MAX:
+ getCodeMiningViewer().updateCodeMinings();
+ break;
+ default:
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Updates code minings after changes to annotations.
+ */
+ private class AnnotationModelListener implements IAnnotationModelListener, IAnnotationModelListenerExtension {
+ @Override
+ public void modelChanged(@Nullable IAnnotationModel model) {
+ // ignore
+ }
+
+ @Override
+ public void modelChanged(@SuppressWarnings("null") AnnotationModelEvent event) {
+ if (viewer == null) {
+ return;
+ }
+
+ if (!event.isValid() || event.isEmpty()) {
+ return;
+ }
+
+ AnnotationCodeMiningFilter filter= new AnnotationCodeMiningFilter(getAnnotationAccess(),
+ event.getAddedAnnotations(), event.getRemovedAnnotations(), event.getChangedAnnotations());
+
+ if (!filter.isEmpty()) {
+ getCodeMiningViewer().updateCodeMinings();
+ }
+ }
+ }
+
+ private @Nullable ITextViewer viewer= null;
+
+ private @Nullable AnnotationModelListener annotationModelListener= null;
+
+ private @Nullable PropertyChangeListener propertyChangeListener= null;
+
+ private @Nullable IAnnotationAccessExtension annotationAccess= null;
+
+ @Override
+ @SuppressWarnings("null")
+ public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) {
+ if (!(viewer instanceof ISourceViewerExtension5)) {
+ throw new IllegalArgumentException("Cannot attach to TextViewer without code mining support"); //$NON-NLS-1$
+ }
+
+ if (!new AnnotationCodeMiningPreferences().isEnabled()) {
+ return CompletableFuture.completedFuture(Collections.emptyList());
+ }
+
+ this.viewer= viewer;
+
+ final IAnnotationAccess annotationAccess= getAdapter(IAnnotationAccess.class);
+ if (!(annotationAccess instanceof IAnnotationAccessExtension)) {
+ throw new IllegalStateException("annotationAccess must implement IAnnotationAccessExtension"); //$NON-NLS-1$
+ }
+ this.annotationAccess= (IAnnotationAccessExtension) annotationAccess;
+
+ return provideCodeMiningsInternal(monitor);
+ }
+
+ @Override
+ public void dispose() {
+ unregisterPropertyChangeListener();
+ unregisterAnnotationModelListener();
+
+ this.viewer= null;
+
+ super.dispose();
+ }
+
+ @Override
+ @SuppressWarnings("boxing")
+ public @Nullable Integer getLine(Annotation annotation) {
+ Integer offset= getOffset(annotation);
+ if (offset == null) {
+ return null;
+ }
+
+ try {
+ return getDocument().getLineOfOffset(offset);
+ } catch (BadLocationException e) {
+ return null;
+ }
+ }
+
+ @Override
+ @SuppressWarnings("boxing")
+ public @Nullable Integer getOffset(Annotation annotation) {
+ final Position position= getAnnotationModel().getPosition(annotation);
+ if (position == null) {
+ return null;
+ }
+
+ return position.getOffset();
+ }
+
+ private CompletableFuture<List<? extends ICodeMining>> provideCodeMiningsInternal(IProgressMonitor monitor) {
+ return CompletableFuture.supplyAsync(() -> {
+ if (!checkAnnotationModelAvailable()) {
+ return Collections.emptyList();
+ }
+
+ registerAnnotationModelListener();
+ registerPropertyChangeListener();
+
+ final Stream<Annotation> annotations= getAnnotations();
+ final List<AbstractCodeMining> codeMinings= createCodeMinings(annotations, monitor);
+
+ return codeMinings;
+ });
+ }
+
+ private void registerAnnotationModelListener() {
+ if (annotationModelListener == null) {
+ annotationModelListener= new AnnotationModelListener();
+ getAnnotationModel().addAnnotationModelListener(annotationModelListener);
+ }
+ }
+
+ private void unregisterAnnotationModelListener() {
+ if (this.annotationModelListener != null) {
+ getAnnotationModel().removeAnnotationModelListener(annotationModelListener);
+ this.annotationModelListener= null;
+ }
+ }
+
+ private void registerPropertyChangeListener() {
+ if (propertyChangeListener == null) {
+ final @Nullable IPreferenceStore store= new AnnotationCodeMiningPreferences().getPreferences();
+ if (store != null) {
+ propertyChangeListener= new PropertyChangeListener();
+ store.addPropertyChangeListener(propertyChangeListener);
+ }
+ }
+ }
+
+ private void unregisterPropertyChangeListener() {
+ if (this.propertyChangeListener != null) {
+ final @Nullable IPreferenceStore store= new AnnotationCodeMiningPreferences().getPreferences();
+ if (store != null) {
+ store.removePropertyChangeListener(propertyChangeListener);
+ }
+ this.propertyChangeListener= null;
+ }
+ }
+
+ private Stream<Annotation> getAnnotations() {
+ return new AnnotationCodeMiningFilter(getAnnotationAccess(), getAnnotationModel().getAnnotationIterator())
+ .sortDistinctLimit(this);
+ }
+
+ private List<AbstractCodeMining> createCodeMinings(Stream<Annotation> annotations, IProgressMonitor monitor) {
+ @SuppressWarnings("null")
+ final Stream<AbstractCodeMining> result= annotations
+ .filter(m -> !monitor.isCanceled())
+ .map(this::createCodeMining)
+ .filter(Objects::nonNull);
+ return result.collect(Collectors.toList());
+ }
+
+ @SuppressWarnings("boxing")
+ private @Nullable AbstractCodeMining createCodeMining(Annotation annotation) {
+ final Integer lineNumber= getLine(annotation);
+
+ if (lineNumber == null) {
+ return null;
+ }
+
+ try {
+ final Consumer<MouseEvent> action= createAction(annotation);
+ return new AnnotationCodeMining(getAnnotationAccess(), annotation, lineNumber, getDocument(), this, action);
+ } catch (BadLocationException e) {
+ return null;
+ }
+ }
+
+ /**
+ * The action selects the text attached to the Annotation and activates quickfixes.
+ */
+ private @Nullable Consumer<MouseEvent> createAction(Annotation annotation) {
+ if (!(annotation instanceof IQuickFixableAnnotation) || !(getTextViewer() instanceof ISourceViewerExtension3)) {
+ return null;
+ }
+
+ final Position position= getAnnotationModel().getPosition(annotation);
+ if (position == null) {
+ return null;
+ }
+
+ return (e -> {
+ final IQuickFixableAnnotation quickFixableAnnotation= (IQuickFixableAnnotation) annotation;
+ if (!quickFixableAnnotation.isQuickFixableStateSet() || !quickFixableAnnotation.isQuickFixable()) {
+ return;
+ }
+
+ final IQuickAssistAssistant quickAssistAssistant= ((ISourceViewerExtension3) getTextViewer()).getQuickAssistAssistant();
+ if (quickAssistAssistant == null) {
+ return;
+ }
+
+ if (!quickAssistAssistant.canFix(annotation)) {
+ return;
+ }
+
+ getTextViewer().setSelectedRange(position.getOffset(), position.getLength());
+
+ final String message= quickAssistAssistant.showPossibleQuickAssists();
+
+ if (message != null) {
+ EditorsPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, EditorsUI.PLUGIN_ID, message));
+ }
+
+ });
+ }
+
+ private boolean checkAnnotationModelAvailable() {
+ return viewer != null && getAnnotationViewer().getVisualAnnotationModel() != null;
+ }
+
+ private IAnnotationModel getAnnotationModel() {
+ return getAnnotationViewer().getVisualAnnotationModel();
+ }
+
+ private ITextViewer getTextViewer() {
+ Assert.isNotNull(viewer);
+ return viewer;
+ }
+
+ private ISourceViewerExtension5 getCodeMiningViewer() {
+ return (ISourceViewerExtension5) getTextViewer();
+ }
+
+ private ISourceViewerExtension2 getAnnotationViewer() {
+ return (ISourceViewerExtension2) getTextViewer();
+ }
+
+ private IDocument getDocument() {
+ return getTextViewer().getDocument();
+ }
+
+ private IAnnotationAccessExtension getAnnotationAccess() {
+ Assert.isNotNull(annotationAccess);
+ return annotationAccess;
+ }
+}
diff --git a/org.eclipse.ui.genericeditor/plugin.xml b/org.eclipse.ui.genericeditor/plugin.xml
index e086d38ce..9a8755e5f 100644
--- a/org.eclipse.ui.genericeditor/plugin.xml
+++ b/org.eclipse.ui.genericeditor/plugin.xml
@@ -231,4 +231,11 @@
class="org.eclipse.ui.internal.genericeditor.GenericEditorWithIconAssociationOverride"
id="org.eclipse.ui.internal.genericeditor.GenericEditorAssociationOverride"/>
</extension>
+ <extension
+ point="org.eclipse.ui.genericeditor.reconcilers">
+ <reconciler
+ class="org.eclipse.jface.text.codemining.CodeMiningReconciler"
+ contentType="org.eclipse.core.runtime.text">
+ </reconciler>
+ </extension>
</plugin>

Back to the top