Bug 511101 - Enable quick fix on generic editor

* Available for Quick-Fix command (Ctrl+1)
* Hover on text editor

Change-Id: I6576f109d6f2a1fdc6020ed6510b645e12dcf3f6
Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF b/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF
index d01e762..cf5e719 100644
--- a/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF
@@ -16,7 +16,9 @@
  org.eclipse.jface.text;bundle-version="3.11.0",
  org.eclipse.ui;bundle-version="3.108.0",
  org.eclipse.ui.workbench.texteditor;bundle-version="3.10.0",
- org.eclipse.ui.ide;bundle-version="3.11.0"
+ org.eclipse.ui.ide;bundle-version="3.11.0",
+ org.eclipse.text.tests;bundle-version="3.11.0",
+ org.eclipse.ui.tests.harness
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Eclipse-BundleShape: dir
 Bundle-ActivationPolicy: lazy
diff --git a/org.eclipse.ui.genericeditor.tests/plugin.xml b/org.eclipse.ui.genericeditor.tests/plugin.xml
index 192e1be..c798ba3 100644
--- a/org.eclipse.ui.genericeditor.tests/plugin.xml
+++ b/org.eclipse.ui.genericeditor.tests/plugin.xml
@@ -36,5 +36,12 @@
 					contentType="org.eclipse.core.runtime.text">
 		</presentationReconciler>
    </extension>
+ <extension
+       point="org.eclipse.ui.ide.markerResolution">
+    <markerResolutionGenerator
+          class="org.eclipse.ui.genericeditor.tests.contributions.MarkerResolutionGenerator"
+          markerType="org.eclipse.core.resources.problemmarker">
+    </markerResolutionGenerator>
+ </extension>
 
 </plugin>
diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java
index f8fbd32..4a6a394 100644
--- a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java
+++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016 Red Hat Inc. and others
+ * Copyright (c) 2016, 2017 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
@@ -11,9 +11,11 @@
 package org.eclipse.ui.genericeditor.tests;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
+import java.util.Collections;
 
 import org.junit.After;
 import org.junit.AfterClass;
@@ -23,18 +25,28 @@
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
 
 import org.eclipse.core.resources.IMarker;
 
-import org.eclipse.jface.text.AbstractHoverInformationControlManager;
+import org.eclipse.text.tests.Accessor;
+
+import org.eclipse.jface.text.AbstractInformationControl;
 import org.eclipse.jface.text.AbstractInformationControlManager;
 import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.TextViewer;
 
 import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.genericeditor.tests.contributions.MagicHoverProvider;
+import org.eclipse.ui.genericeditor.tests.contributions.MarkerResolutionGenerator;
 import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.tests.harness.util.DisplayHelper;
 
 import org.eclipse.ui.texteditor.AbstractTextEditor;
 
@@ -71,7 +83,8 @@
 
 	@Test
 	public void testHover() throws Exception {
-		assertEquals("Alrighty!", getHoverData());
+		Shell shell = getHoverShell(triggerCompletionAndRetrieveInformationControlManager());
+		assertNotNull(findControl(shell, StyledText.class, MagicHoverProvider.LABEL));
 	}
 	
 	@Test
@@ -85,19 +98,84 @@
 			marker.setAttribute(IMarker.CHAR_START, 0);
 			marker.setAttribute(IMarker.CHAR_END, 5);
 			marker.setAttribute(IMarker.MESSAGE, problemMessage);
-			assertEquals(problemMessage, getHoverData());
+			marker.setAttribute(MarkerResolutionGenerator.FIXME, true);
+			AbstractInformationControlManager manager = triggerCompletionAndRetrieveInformationControlManager();
+			assertEquals(Collections.singletonList(marker), getHoverData(manager));
+			// check dialog content
+			Shell shell= getHoverShell(manager);
+			assertNotNull(findControl(shell, Label.class, marker.getAttribute(IMarker.MESSAGE, "NONE")));
+			Link link = findControl(shell, Link.class, MarkerResolutionGenerator.FIXME);
+			assertNotNull(link);
+			Event event = new Event();
+			event.widget = link;
+			event.display = link.getDisplay();
+			event.doit = true;
+			event.type = SWT.Selection;
+			link.notifyListeners(SWT.Selection, event);
+			assertFalse(marker.exists());
 		} finally {
-			if (marker != null) {
+			if (marker != null && marker.exists()) {
 				marker.delete();
 			}
 		}
 	}
 
-	private Object getHoverData() throws Exception {
+	private Shell getHoverShell(AbstractInformationControlManager manager) {
+		AbstractInformationControl control = null;
+		do {
+			DisplayHelper.runEventLoop(this.editor.getSite().getShell().getDisplay(), 100);
+			control = (AbstractInformationControl)new Accessor(manager, AbstractInformationControlManager.class).get("fInformationControl");
+		} while (control == null);
+		Shell shell = (Shell)new Accessor(control, AbstractInformationControl.class).get("fShell");
+		assertTrue(shell.isVisible());
+		return shell;
+	}
+
+	private <T extends Control> T findControl(Control control, Class<T> controlType, String label) {
+		if (control.getClass() == controlType) {
+			T res = (T)control;
+			if (label == null) {
+				return res;
+			}
+			String controlLabel = null;
+			if (control instanceof Label) {
+				controlLabel = ((Label)control).getText();
+			} else if (control instanceof Link) {
+				controlLabel = ((Link) control).getText();
+			} else if (control instanceof Text) {
+				controlLabel = ((Text) control).getText();
+			} else if (control instanceof StyledText) {
+				controlLabel = ((StyledText) control).getText();
+			}
+			if (controlLabel != null && controlLabel.contains(label)) {
+				return res;
+			}
+		} else if (control instanceof Composite) {
+			for (Control child : ((Composite) control).getChildren()) {
+				T res = findControl(child, controlType, label);
+				if (res != null) {
+					return res;
+				}
+			}
+		}
+		return null;
+	}
+
+	private Object getHoverData(AbstractInformationControlManager manager) throws Exception {
+		Object hoverData = new Accessor(manager, AbstractInformationControlManager.class).get("fInformation");
+		return hoverData;
+	}
+
+	private AbstractInformationControlManager triggerCompletionAndRetrieveInformationControlManager() {
 		this.editor.selectAndReveal(2, 0);
-		GenericEditorTestUtils.waitAndDispatch(1000);
+		final StyledText editorTextWidget = (StyledText) this.editor.getAdapter(Control.class);
+		new DisplayHelper() {
+			@Override
+			protected boolean condition() {
+				return editorTextWidget.isFocusControl() && editorTextWidget.getSelection().x == 2;
+			}
+		}.waitForCondition(editorTextWidget.getDisplay(), 1000);
 		// sending event to trigger hover computation
-		StyledText editorTextWidget = (StyledText) this.editor.getAdapter(Control.class);
 		editorTextWidget.getShell().forceActive();
 		editorTextWidget.getShell().setActive();
 		editorTextWidget.getShell().setFocus();
@@ -109,21 +187,14 @@
 		hoverEvent.y = editorTextWidget.getClientArea().y + 5;
 		hoverEvent.display = editorTextWidget.getDisplay();
 		hoverEvent.doit = true;
+		editorTextWidget.getDisplay().setCursorLocation(editorTextWidget.toDisplay(hoverEvent.x, hoverEvent.y));
 		editorTextWidget.notifyListeners(SWT.MouseHover, hoverEvent);
 		// Events need to be processed for hover listener to work correctly
-		GenericEditorTestUtils.waitAndDispatch(1000);
+		DisplayHelper.runEventLoop(editorTextWidget.getDisplay(), 1000);
 		// retrieving hover content
-		Method getSourceViewerMethod= AbstractTextEditor.class.getDeclaredMethod("getSourceViewer");
-		getSourceViewerMethod.setAccessible(true);
-		ITextViewer viewer = (ITextViewer) getSourceViewerMethod.invoke(editor);
-		Field textHoverManagerField= TextViewer.class.getDeclaredField("fTextHoverManager");
-		textHoverManagerField.setAccessible(true);
-		AbstractHoverInformationControlManager hover = (AbstractHoverInformationControlManager) textHoverManagerField.get(viewer);
-		Field informationField = AbstractInformationControlManager.class.getDeclaredField("fInformation");
-		informationField.setAccessible(true);
-		Object hoverData = informationField.get(hover);
-		GenericEditorTestUtils.waitAndDispatch(1000);
-		return hoverData;
+		ITextViewer viewer = (ITextViewer)new Accessor(editor, AbstractTextEditor.class).invoke("getSourceViewer", new Object[0]);
+		AbstractInformationControlManager textHoverManager = (AbstractInformationControlManager)new Accessor(viewer, TextViewer.class).get("fTextHoverManager");
+		return textHoverManager;
 	}
 
 }
diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/MagicHoverProvider.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/MagicHoverProvider.java
index d8f8311..0bba76c 100644
--- a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/MagicHoverProvider.java
+++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/MagicHoverProvider.java
@@ -18,6 +18,8 @@
 
 public class MagicHoverProvider implements ITextHover,ITextHoverExtension2 {
 
+	public static final String LABEL= "Alrighty!";
+
 	@Deprecated
 	@Override
 	public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
@@ -31,7 +33,7 @@
 
 	@Override
 	public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
-		return "Alrighty!";
+		return LABEL;
 	}
 
 }
diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/MarkerResolutionGenerator.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/MarkerResolutionGenerator.java
new file mode 100644
index 0000000..ae4c20d
--- /dev/null
+++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/contributions/MarkerResolutionGenerator.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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.genericeditor.tests.contributions;
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.core.resources.IMarker;
+
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.IMarkerResolution2;
+import org.eclipse.ui.IMarkerResolutionGenerator;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+
+public class MarkerResolutionGenerator implements IMarkerResolutionGenerator {
+
+	public static final String FIXME= MarkerResolutionGenerator.class.getName() + ".fixme";
+	
+	private static class MarkerResolution implements IMarkerResolution2 {
+		@Override
+		public String getDescription() {
+			return "resolution.description";
+		}
+		@Override
+		public Image getImage() {
+			return PlatformUI.getWorkbench().getSharedImages().getImage(IDE.SharedImages.IMG_OBJ_PROJECT);
+		}
+		@Override
+		public String getLabel() {
+			return FIXME;
+		}
+
+		@Override
+		public void run(IMarker marker) {
+			try {
+				marker.delete();
+			} catch (CoreException e) {
+				e.printStackTrace();
+			}
+		}
+		
+	}
+
+	@Override
+	public IMarkerResolution[] getResolutions(IMarker marker) {
+		if (marker.getAttribute(FIXME, false)) {
+			return new IMarkerResolution[] {
+				new MarkerResolution()
+			};
+		}
+		return new IMarkerResolution[0];
+	}
+
+}
diff --git a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
index 64ae867..1b3ce36 100644
--- a/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
+++ b/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
@@ -11,7 +11,9 @@
  org.eclipse.jface.text;bundle-version="3.11.0",
  org.eclipse.core.runtime;bundle-version="3.12.0",
  org.eclipse.ui.workbench;bundle-version="3.109.0",
- org.eclipse.jface;bundle-version="3.13.0"
+ org.eclipse.jface;bundle-version="3.12.0",
+ org.eclipse.ui.ide;bundle-version="3.12.0",
+ org.eclipse.core.resources;bundle-version="3.11.0"
 Export-Package: org.eclipse.ui.internal.genericeditor;x-internal:=true
 Bundle-Activator: org.eclipse.ui.internal.genericeditor.GenericEditorPlugin
 Bundle-Localization: plugin
diff --git a/org.eclipse.ui.genericeditor/plugin.xml b/org.eclipse.ui.genericeditor/plugin.xml
index 060b9a6..06b6de7 100644
--- a/org.eclipse.ui.genericeditor/plugin.xml
+++ b/org.eclipse.ui.genericeditor/plugin.xml
@@ -74,7 +74,14 @@
    <extension
          point="org.eclipse.ui.genericeditor.hoverProviders">
       <hoverProvider
+         isBefore="*"
+         class="org.eclipse.ui.internal.genericeditor.markers.MarkerAnnotationHover"
+         contentType="org.eclipse.core.runtime.text"
+         id="org.eclipse.ui.genericeditor.markers.annotationsHoverProvider">
+      </hoverProvider>
+      <hoverProvider
             isBefore="*"
+            isAfter="org.eclipse.ui.genericeditor.markers.annotationsHoverProvider"
             class="org.eclipse.ui.internal.genericeditor.AnnotationHoverDelegate"
             contentType="org.eclipse.core.runtime.text"
             id="org.eclipse.ui.genericeditor.annotationsHoverProvider">
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AnnotationHoverDelegate.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AnnotationHoverDelegate.java
index ba9f0e5..bcbf86a 100644
--- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AnnotationHoverDelegate.java
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/AnnotationHoverDelegate.java
@@ -18,6 +18,7 @@
 import org.eclipse.jface.text.source.ISourceViewer;
 import org.eclipse.ui.editors.text.EditorsUI;
 import org.eclipse.ui.texteditor.AnnotationPreference;
+import org.eclipse.ui.texteditor.MarkerAnnotation;
 
 /**
  * Delegate to {@link DefaultTextHover}, since we need a parameter-less
@@ -33,6 +34,10 @@
 			this.delegate = new DefaultTextHover(sourceViewer) {
 				@Override
 				protected boolean isIncluded(Annotation annotation) {
+					if (annotation instanceof MarkerAnnotation) {
+						// this is handled by MarkerAnnotationHover
+						return false;
+					}
 					AnnotationPreference preference= EditorsUI.getAnnotationPreferenceLookup().getAnnotationPreference(annotation);
 					if (preference == null)
 						return false;
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 0b5aa83..9d96612 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
@@ -29,11 +29,14 @@
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IDocumentPartitioningListener;
 import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
 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.quickassist.IQuickAssistAssistant;
+import org.eclipse.jface.text.quickassist.QuickAssistAssistant;
 import org.eclipse.jface.text.source.ISourceViewer;
 
 import org.eclipse.ui.IEditorPart;
@@ -42,6 +45,8 @@
 import org.eclipse.ui.texteditor.ITextEditor;
 
 import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
+import org.eclipse.ui.internal.editors.text.EditorsPlugin;
+import org.eclipse.ui.internal.genericeditor.markers.MarkerResoltionQuickAssistProcessor;
 
 /**
  * The configuration of the {@link ExtensionBasedTextEditor}. It registers the proxy composite
@@ -161,4 +166,17 @@
 		}
 	}
 
+	@Override
+	public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) {
+		QuickAssistAssistant quickAssistAssistant = new QuickAssistAssistant();
+		quickAssistAssistant.setQuickAssistProcessor(new MarkerResoltionQuickAssistProcessor());
+		quickAssistAssistant.setRestoreCompletionProposalSize(EditorsPlugin.getDefault().getDialogSettingsSection("quick_assist_proposal_size")); //$NON-NLS-1$
+		quickAssistAssistant.setInformationControlCreator(new IInformationControlCreator() {
+			@Override
+			public IInformationControl createInformationControl(Shell parent) {
+				return new DefaultInformationControl(parent, EditorsPlugin.getAdditionalInfoAffordanceString());
+			}
+		});
+		return quickAssistAssistant;
+	}
 }
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/OrderedExtensionComparator.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/OrderedExtensionComparator.java
index 9d51ef0..0ae0bba 100644
--- a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/OrderedExtensionComparator.java
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/OrderedExtensionComparator.java
@@ -49,7 +49,7 @@
 		if (before0 == null) {
 			return false;
 		}
-		if ("*".equals(before0)) { //$NON-NLS-1$
+		if ("*".equals(before0) && !"*".equals(arg1.getIsBefore())) { //$NON-NLS-1$ //$NON-NLS-2$
 			return true;
 		}
 		String id1 = arg1.getId();
@@ -71,7 +71,7 @@
 		if (after0 == null) {
 			return false;
 		}
-		if ("*".equals(after0)) { //$NON-NLS-1$
+		if ("*".equals(after0) && !"*".equals(arg1.getIsAfter())) { //$NON-NLS-1$ //$NON-NLS-2$
 			return true;
 		}
 		String id1 = arg1.getId();
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerAnnotationHover.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerAnnotationHover.java
new file mode 100644
index 0000000..d496ebe
--- /dev/null
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerAnnotationHover.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.genericeditor.markers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.ITextHoverExtension;
+import org.eclipse.jface.text.ITextHoverExtension2;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISourceViewerExtension2;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.texteditor.AnnotationPreference;
+import org.eclipse.ui.texteditor.MarkerAnnotation;
+
+public class MarkerAnnotationHover implements ITextHoverExtension, ITextHoverExtension2, ITextHover {
+
+	protected static boolean isIncluded(Annotation annotation) {
+		if (!(annotation instanceof MarkerAnnotation)) {
+			return false;
+		}
+		AnnotationPreference preference= EditorsUI.getAnnotationPreferenceLookup().getAnnotationPreference(annotation);
+		if (preference == null) {
+			return false;
+		}
+		String key= preference.getTextPreferenceKey();
+		if (key != null) {
+			if (!EditorsUI.getPreferenceStore().getBoolean(key))
+				return false;
+		} else {
+			key= preference.getHighlightPreferenceKey();
+			if (key == null || !EditorsUI.getPreferenceStore().getBoolean(key))
+				return false;
+		}
+		return true;
+	}
+
+
+	@Override
+	public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
+		Object hoverInfo = getHoverInfo2(textViewer, hoverRegion);
+		if (hoverInfo == null) {
+			return null;
+		}
+		return hoverInfo.toString();
+	}
+
+	@Override
+	public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
+		if (!(textViewer instanceof ISourceViewerExtension2)) {
+			return null;
+		}
+		ISourceViewerExtension2 viewer = (ISourceViewerExtension2)textViewer;
+		List<MarkerAnnotation> annotations = findMarkerAnnotations(viewer, new Region(offset, 0));
+		if (annotations.isEmpty()) {
+			return null;
+		}
+		// find intersection of regions
+		int highestOffsetStart = 0;
+		int lowestOffsetEnd = Integer.MAX_VALUE;
+		IAnnotationModel annotationModel = viewer.getVisualAnnotationModel();
+		for (Annotation annotation : annotations) {
+			Position position = annotationModel.getPosition(annotation);
+			highestOffsetStart = Math.max(highestOffsetStart, position.getOffset());
+			lowestOffsetEnd = Math.min(lowestOffsetEnd, position.getOffset() + position.getLength());
+		}
+		return new Region(highestOffsetStart, Math.max(0, lowestOffsetEnd - highestOffsetStart));
+	}
+
+	@Override
+	public List<IMarker> getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
+		if (!(textViewer instanceof ISourceViewerExtension2)) {
+			return null;
+		}
+		List<MarkerAnnotation> annotations = findMarkerAnnotations((ISourceViewerExtension2)textViewer, hoverRegion);
+		if (annotations.isEmpty()) {
+			return null;
+		}
+		List<IMarker> markers = new ArrayList<>(annotations.size());
+		for (MarkerAnnotation annotation : annotations) {
+			markers.add(annotation.getMarker());
+		}
+		return markers;
+	}
+	
+	private static List<MarkerAnnotation> findMarkerAnnotations(ISourceViewerExtension2 viewer, IRegion region) {
+		List<MarkerAnnotation> res = new ArrayList<>();
+		IAnnotationModel annotationModel = viewer.getVisualAnnotationModel();
+		annotationModel.getAnnotationIterator().forEachRemaining(annotation -> {
+			if (isIncluded(annotation)) {
+				Position position = annotationModel.getPosition(annotation);
+				if (region.getOffset() >= position.getOffset() && region.getOffset() + region.getLength() <= position.getOffset() + position.getLength()) {
+					res.add((MarkerAnnotation)annotation);
+				}
+			}
+		});
+		return res;
+	}
+
+	@Override
+	public IInformationControlCreator getHoverControlCreator() {
+		return new MarkerHoverControlCreator();
+	}
+
+}
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerHoverControlCreator.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerHoverControlCreator.java
new file mode 100644
index 0000000..8d81994
--- /dev/null
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerHoverControlCreator.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.genericeditor.markers;
+
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IInformationControlCreatorExtension;
+import org.eclipse.swt.widgets.Shell;
+
+public class MarkerHoverControlCreator implements IInformationControlCreator, IInformationControlCreatorExtension {
+
+	@Override
+	public boolean canReuse(IInformationControl control) {
+		return false;
+	}
+
+	@Override
+	public boolean canReplace(IInformationControlCreator creator) {
+		return false;
+	}
+
+	@Override
+	public IInformationControl createInformationControl(Shell parent) {
+		return new MarkerInformationControl(parent,this);
+	}
+
+}
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerInformationControl.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerInformationControl.java
new file mode 100644
index 0000000..5a1cd3e
--- /dev/null
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerInformationControl.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.genericeditor.markers;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.text.AbstractInformationControl;
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IInformationControlExtension;
+import org.eclipse.jface.text.IInformationControlExtension2;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.IMarkerResolution2;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.ide.IDE.SharedImages;
+
+public class MarkerInformationControl extends AbstractInformationControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension2 {
+
+	private IInformationControlCreator creator;
+
+	public MarkerInformationControl(Shell parentShell, IInformationControlCreator creator) {
+		super(parentShell, EditorsUI.getTooltipAffordanceString());
+		this.creator = creator;
+		create();
+	}
+
+	private List<IMarker> markers;
+	private Composite parent;
+
+	@Override
+	public boolean hasContents() {
+		return this.markers != null && !this.markers.isEmpty();
+	}
+	
+	@Override
+	protected void createContent(Composite parent) {
+		parent.setLayout(new RowLayout(SWT.VERTICAL));
+		parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		parent.setBackgroundMode(SWT.INHERIT_DEFAULT);
+		this.parent = parent;
+	}
+
+	private static Image getImage(IMarker marker) {
+		switch (marker.getAttribute(IMarker.SEVERITY, -1)) {
+		case IMarker.SEVERITY_ERROR: return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
+		case IMarker.SEVERITY_WARNING: return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_WARN_TSK);
+		case IMarker.SEVERITY_INFO: return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_INFO_TSK);
+		default:
+			break;
+		}
+		return null;
+	}
+
+	@Override
+	public void setInput(Object input) {
+		this.markers = (List<IMarker>)input;
+		for (IMarker marker : this.markers) {
+			Composite markerComposite = new Composite(parent, SWT.NONE);
+			GridLayout gridLayout = new GridLayout(1, false);
+			gridLayout.verticalSpacing = 0;
+			markerComposite.setLayout(gridLayout);
+			Composite markerLine = new Composite(markerComposite, SWT.NONE);
+			markerLine.setLayout(new RowLayout());
+			Label markerImage = new Label(markerLine, SWT.NONE);
+			markerImage.setImage(getImage(marker));
+			Label markerLabel = new Label(markerLine, SWT.NONE);
+			markerLabel.setText(marker.getAttribute(IMarker.MESSAGE, "missing message")); //$NON-NLS-1$
+			for (IMarkerResolution resolution : IDE.getMarkerHelpRegistry().getResolutions(marker)) {
+				Composite resolutionComposite = new Composite(markerComposite, SWT.NONE);
+				GridData layoutData = new GridData();
+				layoutData.horizontalIndent = 10;
+				resolutionComposite.setLayoutData(layoutData);
+				RowLayout rowLayout = new RowLayout();
+				rowLayout.marginBottom = 0;
+				resolutionComposite.setLayout(rowLayout);
+				Label resolutionImage = new Label(resolutionComposite, SWT.NONE);
+				// TODO: try to retrieve icon from QuickFix command
+				Image resolutionPic = null; 
+				if (resolution instanceof IMarkerResolution2) {
+					resolutionPic = ((IMarkerResolution2) resolution).getImage();
+				}
+				if (resolutionPic == null) {
+					resolutionPic = PlatformUI.getWorkbench().getSharedImages().getImage(SharedImages.IMG_OPEN_MARKER);
+				}
+				resolutionImage.setImage(resolutionPic);
+				Link resolutionLink = new Link(resolutionComposite, SWT.NONE);
+				resolutionLink.setText("<A>" + resolution.getLabel() + "</a>"); //$NON-NLS-1$ //$NON-NLS-2$
+				resolutionLink.addSelectionListener(new SelectionAdapter() {
+					@Override
+					public void widgetSelected(SelectionEvent e) {
+						Job resolutionJob = new Job("apply resolution - " + resolution.getLabel()) { //$NON-NLS-1$
+							@Override
+							protected IStatus run(IProgressMonitor monitor) {
+								resolution.run(marker);
+								return Status.OK_STATUS;
+							}
+						};
+						resolutionJob.setUser(true);
+						resolutionJob.setSystem(true);
+						resolutionJob.setPriority(Job.INTERACTIVE);
+						resolutionJob.schedule();
+						getShell().dispose();
+					}
+				});
+			}
+		}
+		parent.pack(true);
+	}
+	
+	@Override
+	public IInformationControlCreator getInformationPresenterControlCreator() {
+		return new AbstractReusableInformationControlCreator() {
+			@Override
+			protected IInformationControl doCreateInformationControl(Shell parent) {
+				return creator.createInformationControl(parent);
+			}
+		};
+	}
+	
+	@Override
+	public Point computeSizeHint() {
+		getShell().pack();
+		return getShell().getSize();
+	}
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerResoltionQuickAssistProcessor.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerResoltionQuickAssistProcessor.java
new file mode 100644
index 0000000..d3d49dd
--- /dev/null
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerResoltionQuickAssistProcessor.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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.) - initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.internal.genericeditor.markers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.texteditor.MarkerAnnotation;
+
+public class MarkerResoltionQuickAssistProcessor implements IQuickAssistProcessor {
+
+	@Override
+	public String getErrorMessage() {
+		return null;
+	}
+
+	@Override
+	public boolean canFix(Annotation annotation) {
+		return annotation instanceof MarkerAnnotation;
+	}
+
+	@Override
+	public boolean canAssist(IQuickAssistInvocationContext invocationContext) {
+		return false;
+	}
+
+	@Override
+	public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext invocationContext) {
+		IAnnotationModel annotationModel = invocationContext.getSourceViewer().getAnnotationModel();
+		Collection<MarkerAnnotation> annotations = new HashSet<>();
+		annotationModel.getAnnotationIterator().forEachRemaining(annotation -> {
+			Position position = annotationModel.getPosition(annotation);
+			if (invocationContext.getOffset() >= position.getOffset() &&
+				invocationContext.getOffset() + Math.max(0, invocationContext.getLength()) <= position.getOffset() + position.getLength() &&
+				annotation instanceof MarkerAnnotation) {
+				annotations.add((MarkerAnnotation)annotation);
+			}
+		});
+		Collection<MarkerResolutionCompletionProposal> resolutions = new ArrayList<>();
+		for (MarkerAnnotation annotation : annotations) {
+			IMarker marker = annotation.getMarker();
+			for (IMarkerResolution resolution : IDE.getMarkerHelpRegistry().getResolutions(marker)) {
+				resolutions.add(new MarkerResolutionCompletionProposal(marker, resolution));
+			}
+		}
+		return resolutions.toArray(new ICompletionProposal[resolutions.size()]);
+	}
+	
+}
diff --git a/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerResolutionCompletionProposal.java b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerResolutionCompletionProposal.java
new file mode 100644
index 0000000..bdaa16a
--- /dev/null
+++ b/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/markers/MarkerResolutionCompletionProposal.java
@@ -0,0 +1,58 @@
+package org.eclipse.ui.internal.genericeditor.markers;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.IMarkerResolution2;
+
+public class MarkerResolutionCompletionProposal implements ICompletionProposal {
+
+	private IMarkerResolution markerResolution;
+	private IMarker marker;
+
+	public MarkerResolutionCompletionProposal(IMarker marker, IMarkerResolution markerResolution) {
+		this.marker = marker;
+		this.markerResolution = markerResolution;
+	}
+
+	@Override
+	public void apply(IDocument document) {
+		this.markerResolution.run(this.marker);
+	}
+
+	@Override
+	public Point getSelection(IDocument document) {
+		return null;
+	}
+
+	@Override
+	public String getAdditionalProposalInfo() {
+		if (this.markerResolution instanceof IMarkerResolution2) {
+			return ((IMarkerResolution2)this.markerResolution).getDescription();
+		}
+		return null;
+	}
+
+	@Override
+	public String getDisplayString() {
+		return this.markerResolution.getLabel();
+	}
+
+	@Override
+	public Image getImage() {
+		if (this.markerResolution instanceof IMarkerResolution2) {
+			return ((IMarkerResolution2)this.markerResolution).getImage();
+		}
+		return null;
+	}
+
+	@Override
+	public IContextInformation getContextInformation() {
+		return null;
+	}
+
+}