package com.redhat.ceylon.eclipse.code.hover; import static com.redhat.ceylon.eclipse.ui.CeylonResources.CONFIG_ANN; import static com.redhat.ceylon.eclipse.ui.CeylonResources.CONFIG_ANN_DIS; import static org.eclipse.ui.PlatformUI.getWorkbench; import static org.eclipse.ui.dialogs.PreferencesUtil.createPreferenceDialogOn; import static org.eclipse.ui.editors.text.EditorsUI.getAnnotationPreferenceLookup; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.text.AbstractReusableInformationControlCreator; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IInformationControlExtension4; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationHover; import org.eclipse.jface.text.source.IAnnotationHoverExtension; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IAnnotationModelExtension2; import org.eclipse.jface.text.source.ILineRange; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.LineRange; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.texteditor.AnnotationPreference; import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.ui.CeylonPlugin; /** * Hover for error and warning annotations. */ public class AnnotationHover extends SourceInfoHover implements IAnnotationHover, IAnnotationHoverExtension { public static final String ANNOTATION_PREFERENCE_PAGE_ID = "org.eclipse.ui.editors.preferencePages.Annotations"; /** * Action to configure the annotation preferences. */ static class ConfigureAnnotationsAction extends Action { private final AnnotationInformationControl fInfoControl; public ConfigureAnnotationsAction( AnnotationInformationControl infoControl) { fInfoControl = infoControl; setText("Configure Annotation Appearance"); ImageRegistry imageRegistry = CeylonPlugin.imageRegistry(); setImageDescriptor(imageRegistry.getDescriptor(CONFIG_ANN)); setDisabledImageDescriptor(imageRegistry.getDescriptor(CONFIG_ANN_DIS)); setToolTipText("Configure Annotation Appearance"); } @Override public void run() { Object data = null; Map<Annotation, Position> annotationPositions = fInfoControl.getAnnotationInfo() .getAnnotationPositions(); if (annotationPositions!=null && !annotationPositions.isEmpty()) { Annotation annotation = annotationPositions.keySet() .iterator().next(); AnnotationPreference preference = getAnnotationPreference(annotation); if (preference != null) { data = preference.getPreferenceLabel(); } } Shell shell = getWorkbench() .getActiveWorkbenchWindow() .getShell(); fInfoControl.dispose(); //FIXME: should have protocol to hide, rather than dispose createPreferenceDialogOn(shell, ANNOTATION_PREFERENCE_PAGE_ID, null, data).open(); } } //private final IPreferenceStore fStore= EditorUtil.getPreferences(); private final DefaultMarkerAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess(); private final CeylonEditor editor; private final boolean rulerHover; public AnnotationHover(CeylonEditor editor, boolean rulerHover) { super(editor); this.editor = editor; this.rulerHover = rulerHover; } @Override public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { return null; } @SuppressWarnings("unchecked") @Override public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) { IAnnotationModel model; if (textViewer instanceof ISourceViewer) { ISourceViewer sourceViewer = (ISourceViewer) textViewer; model = sourceViewer.getAnnotationModel(); } else { // Get annotation model from file buffer manager //path= getEditorInputPath(); //model= getAnnotationModel(path); model=null; } if (model == null) return null; Iterator<Annotation> parent; if (model instanceof IAnnotationModelExtension2) { IAnnotationModelExtension2 modelExt = (IAnnotationModelExtension2) model; parent = modelExt.getAnnotationIterator( hoverRegion.getOffset(), hoverRegion.getLength()+1, true, true); } else { parent = model.getAnnotationIterator(); } Map<Annotation,Position> annotationPositions = new LinkedHashMap<Annotation, Position>(); Iterator<Annotation> iter = new AnnotationIterator(parent, rulerHover); while (iter.hasNext()) { Annotation a = (Annotation) iter.next(); Position p = model.getPosition(a); int l = annotationAccess.getLayer(a); //TODO: make higher-layer annotations suppress lower-layer ones if (p!=null && p.overlapsWith( hoverRegion.getOffset(), hoverRegion.getLength())) { String msg = a.getText(); if (msg!=null && msg.trim().length()>0) { if (l>=0) { annotationPositions.put(a, p); } } } } if (!annotationPositions.isEmpty()) { return createAnnotationInfo(textViewer, annotationPositions); } else { return null; } } protected AnnotationInfo createAnnotationInfo( ITextViewer textViewer, Map<Annotation,Position> annotationPositions) { return new AnnotationInfo(editor, annotationPositions, textViewer); } @Override public IInformationControlCreator getHoverControlCreator() { return new AbstractReusableInformationControlCreator() { @Override public IInformationControl doCreateInformationControl(Shell parent) { return new AnnotationInformationControl( parent, "F2 for focus") { /** * Create the "enriched" control */ @Override public IInformationControlCreator getInformationPresenterControlCreator() { final AnnotationInformationControl simpleAnnotationControl = this; return new AbstractReusableInformationControlCreator() { @Override public IInformationControl doCreateInformationControl(Shell parent) { ToolBarManager tbm = new ToolBarManager(SWT.FLAT); AnnotationInformationControl control = new AnnotationInformationControl( parent, tbm) { @Override public void setInput(Object input) { Assert.isLegal(input instanceof AnnotationInfo); AnnotationInfo oldInput = simpleAnnotationControl.getAnnotationInfo(); AnnotationInfo newInput = (AnnotationInfo) input; Tree.CompilationUnit oldRootNode = getTypecheckedRootNode(oldInput); Tree.CompilationUnit newRootNode = getTypecheckedRootNode(newInput); if (newRootNode == oldRootNode) { newInput.setProposals(oldInput.getProposals()); } super.setInput(newInput); }; }; ConfigureAnnotationsAction config = new ConfigureAnnotationsAction( control); config.setEnabled(true); //TODO: add a listener which sets the annotation type // onto the ConfigureAnnotationsAction tbm.add(config); tbm.update(true); return control; } }; } }; } @Override public boolean canReuse(IInformationControl control) { if (!super.canReuse(control)) { return false; } if (control instanceof IInformationControlExtension4) { ((IInformationControlExtension4) control) .setStatusText("F2 for focus"); } return true; } }; } /** * Returns the annotation preference for the given annotation. * * @param annotation the annotation * @return the annotation preference or <code>null</code> if none */ private static AnnotationPreference getAnnotationPreference( Annotation annotation) { if (annotation.isMarkedDeleted()) { return null; } else { return getAnnotationPreferenceLookup() .getAnnotationPreference(annotation); } } @Override public boolean canHandleMouseCursor() { return false; } @Override public Object getHoverInfo(ISourceViewer sourceViewer, ILineRange lineRange, int visibleNumberOfLines) { try { int line = lineRange.getStartLine(); IRegion li = sourceViewer.getDocument() .getLineInformation(line); return getHoverInfo2(sourceViewer, li); } catch (BadLocationException e) { e.printStackTrace(); return null; } } @Override public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) { return new LineRange(lineNumber, 1); } @Override public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) { //TODO: implement to get hover in overview ruler!! return null; } }