com.google.dart.tools.ui.internal.text.editor.DartHover.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.tools.ui.internal.text.editor.DartHover.java

Source

/*
 * Copyright (c) 2013, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.dart.tools.ui.internal.text.editor;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.MethodDeclaration;
import com.google.dart.engine.ast.visitor.ElementLocator;
import com.google.dart.engine.element.CompilationUnitElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ExecutableElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.ParameterElement;
import com.google.dart.engine.element.PropertyAccessorElement;
import com.google.dart.engine.services.util.DartDocUtilities;
import com.google.dart.engine.type.Type;
import com.google.dart.engine.utilities.general.StringUtilities;
import com.google.dart.engine.utilities.source.SourceRange;
import com.google.dart.server.GetHoverConsumer;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.DartCoreDebug;
import com.google.dart.tools.ui.internal.actions.NewSelectionConverter;
import com.google.dart.tools.ui.internal.problemsview.ProblemsView;
import com.google.dart.tools.ui.internal.util.GridDataFactory;
import com.google.dart.tools.ui.internal.util.GridLayoutFactory;
import com.google.dart.tools.ui.text.DartSourceViewerConfiguration;

import org.apache.commons.lang3.text.WordUtils;
import org.dartlang.analysis.server.protocol.HoverInformation;
import org.dartlang.analysis.server.protocol.RequestError;
import org.eclipse.core.resources.IMarker;
import org.eclipse.jface.text.AbstractInformationControl;
import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension2;
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.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension2;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.MarkerAnnotation;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class DartHover implements ITextHover, ITextHoverExtension, ITextHoverExtension2 {

    private static class AnnotationsSection {
        private final FormToolkit toolkit;
        private final Section section;
        private final Composite container;

        public AnnotationsSection(Composite parent, String title) {
            toolkit = createToolkit(parent.getDisplay());
            this.section = toolkit.createSection(parent, Section.TITLE_BAR);
            GridDataFactory.create(section).grabHorizontal().fill();
            section.setText(title);
            container = toolkit.createComposite(section);
            GridLayoutFactory.create(container).columns(2).spacingHorizontal(0);
            section.setClient(container);
        }

        public void setAnnotations(List<Annotation> annotations) {
            for (Control child : container.getChildren()) {
                child.dispose();
            }
            annotations = getSortedAnnotations(annotations);
            for (Annotation annotation : annotations) {
                // prepare marker
                IMarker marker = null;
                if (annotation instanceof MarkerAnnotation) {
                    marker = ((MarkerAnnotation) annotation).getMarker();
                }
                // icon
                {
                    Label imageLabel = new Label(container, SWT.NONE);
                    if (marker != null) {
                        imageLabel.setImage(ProblemsView.DESCRIPTION_LABEL_PROVIDER.getImage(marker));
                    }
                }
                // message
                toolkit.createLabel(container, annotation.getText());
                // correction
                if (marker != null) {
                    String correction = marker.getAttribute(DartCore.MARKER_ATTR_CORRECTION, (String) null);
                    if (correction != null) {
                        new Label(container, SWT.NONE);
                        toolkit.createLabel(container, correction);
                    }
                }
            }
        }
    }

    private static class DartInformationControl extends AbstractInformationControl
            implements IInformationControlExtension2 {
        private static final Point SIZE_CONSTRAINTS = new Point(10000, 10000);

        private static boolean isGridVisible(AnnotationsSection section) {
            return section.section.getVisible();
        }

        private static boolean isGridVisible(DocSection section) {
            return section.section.getVisible();
        }

        private static boolean isGridVisible(TextSection section) {
            return section.section.getVisible();
        }

        private static void setGridVisible(AnnotationsSection section, boolean visible) {
            setGridVisible(section.section, visible);
        }

        private static void setGridVisible(Control control, boolean visible) {
            GridDataFactory.modify(control).exclude(!visible);
            control.setVisible(visible);
            control.getParent().layout();
        }

        private static void setGridVisible(DocSection section, boolean visible) {
            setGridVisible(section.section, visible);
        }

        private static void setGridVisible(TextSection section, boolean visible) {
            setGridVisible(section.section, visible);
        }

        private boolean hasContents;

        private Composite container;
        private TextSection elementSection;
        private TextSection classSection;
        private TextSection librarySection;
        private AnnotationsSection problemsSection;
        private DocSection docSection;
        private TextSection staticTypeSection;
        private TextSection propagatedTypeSection;
        private TextSection parameterSection;

        public DartInformationControl(Shell parentShell) {
            super(parentShell, false);
            toolkit = createToolkit(parentShell.getDisplay());
            create();
        }

        @Override
        public Point computeSizeConstraints(int widthInChars, int heightInChars) {
            return SIZE_CONSTRAINTS;
        }

        @Override
        public Point computeSizeHint() {
            // Shell was already packed and has the required size.
            return getShell().getSize();
        }

        @Override
        public IInformationControlCreator getInformationPresenterControlCreator() {
            return new DartInformationControlCreator();
        }

        @Override
        public boolean hasContents() {
            return hasContents;
        }

        @Override
        public void setInput(Object input) {
            hasContents = false;
            // Hide all sections.
            setGridVisible(elementSection, false);
            setGridVisible(classSection, false);
            setGridVisible(librarySection, false);
            setGridVisible(problemsSection, false);
            setGridVisible(docSection, false);
            setGridVisible(staticTypeSection, false);
            setGridVisible(propagatedTypeSection, false);
            setGridVisible(parameterSection, false);
            if (input instanceof HoverInfo_NEW) {
                //
                // Display hover based on Analysis Server response
                //
                HoverInformation hover = ((HoverInfo_NEW) input).hover;
                if (hover != null) {
                    // Element
                    if (hover.getElementKind() != null) {
                        // show Element
                        {
                            String description = hover.getElementDescription();
                            if (description != null) {
                                String text = WordUtils.wrap(description, 100);
                                setGridVisible(elementSection, true);
                                elementSection.setTitle(WordUtils.capitalize(hover.getElementKind()));
                                elementSection.setText(text);
                            }
                        }
                        // show Class
                        {
                            String className = hover.getContainingClassDescription();
                            if (className != null) {
                                setGridVisible(classSection, true);
                                classSection.setText(className);
                            }
                        }
                        // show Library
                        {
                            String unitName = hover.getContainingLibraryPath();
                            String libraryName = hover.getContainingLibraryName();
                            if (unitName != null && libraryName != null) {
                                String text = StringUtilities.abbreviateLeft(libraryName, 25) + " | "
                                        + StringUtilities.abbreviateLeft(unitName, 35);
                                setGridVisible(librarySection, true);
                                librarySection.setText(text);
                            }
                        }
                        // Dart Doc
                        {
                            String dartDoc = hover.getDartdoc();
                            if (dartDoc != null) {
                                setGridVisible(docSection, true);
                                docSection.setDoc(dartDoc);
                            }
                        }
                    }
                    // parameter
                    {
                        String parameter = hover.getParameter();
                        if (parameter != null) {
                            setGridVisible(parameterSection, true);
                            parameterSection.setText(parameter);
                        }
                    }
                    // static type
                    {
                        String staticType = hover.getStaticType();
                        if (staticType != null) {
                            setGridVisible(staticTypeSection, true);
                            staticTypeSection.setText(staticType);
                        }
                    }
                    // propagated type
                    {
                        String propagatedType = hover.getPropagatedType();
                        if (propagatedType != null) {
                            setGridVisible(propagatedTypeSection, true);
                            propagatedTypeSection.setText(propagatedType);
                        }
                    }
                }
                // Annotations.
                {
                    List<Annotation> annotations = ((HoverInfo_NEW) input).annotations;
                    int size = annotations.size();
                    if (size != 0) {
                        setGridVisible(problemsSection, true);
                        problemsSection.setAnnotations(annotations);
                    }
                }
            } else if (input instanceof HoverInfo_OLD) {
                //
                // Display hover based upon java base Analysis Engine information
                //
                HoverInfo_OLD hoverInfo = (HoverInfo_OLD) input;
                AstNode node = hoverInfo.node;
                Element element = hoverInfo.element;
                // Element
                if (element != null) {
                    // show variable, if synthetic accessor
                    if (element instanceof PropertyAccessorElement) {
                        PropertyAccessorElement accessor = (PropertyAccessorElement) element;
                        if (accessor.isSynthetic()) {
                            element = accessor.getVariable();
                        }
                    }
                    // show Element
                    {
                        String text = element.toString();
                        text = WordUtils.wrap(text, 100);
                        setGridVisible(elementSection, true);
                        elementSection.setTitle(WordUtils.capitalize(element.getKind().getDisplayName()));
                        elementSection.setText(text);
                    }
                    // show Library
                    {
                        LibraryElement library = element.getLibrary();
                        CompilationUnitElement unit = element.getAncestor(CompilationUnitElement.class);
                        if (library != null && unit != null) {
                            String unitName = unit.getSource().getFullName();
                            String libraryName = library.getDisplayName();
                            String text = StringUtilities.abbreviateLeft(libraryName, 25) + " | "
                                    + StringUtilities.abbreviateLeft(unitName, 35);
                            setGridVisible(librarySection, true);
                            librarySection.setText(text);
                        }
                    }
                    // Dart Doc
                    try {
                        String dartDoc = element.computeDocumentationComment();
                        if (dartDoc != null) {
                            dartDoc = DartDocUtilities.cleanDartDoc(dartDoc);
                            setGridVisible(docSection, true);
                            docSection.setDoc(dartDoc);
                        }
                    } catch (Throwable e) {
                    }
                }
                // types
                if (node instanceof Expression) {
                    Expression expression = (Expression) node;
                    // parameter
                    {
                        AstNode n = expression;
                        while (n != null) {
                            if (n instanceof Expression) {
                                ParameterElement parameterElement = ((Expression) n).getBestParameterElement();
                                if (parameterElement != null) {
                                    setGridVisible(parameterSection, true);
                                    parameterSection
                                            .setText(DartDocUtilities.getTextSummary(null, parameterElement));
                                    break;
                                }
                            }
                            n = n.getParent();
                        }
                    }
                    // static type
                    Type staticType = expression.getStaticType();
                    if (staticType != null && element == null) {
                        setGridVisible(staticTypeSection, true);
                        staticTypeSection.setText(staticType.getDisplayName());
                    }
                    // propagated type
                    if (!(element instanceof ExecutableElement)) {
                        Type propagatedType = expression.getPropagatedType();
                        if (propagatedType != null && !propagatedType.equals(staticType)) {
                            setGridVisible(propagatedTypeSection, true);
                            propagatedTypeSection.setText(propagatedType.getDisplayName());
                        }
                    }
                }
                // Annotations.
                {
                    List<Annotation> annotations = hoverInfo.annotations;
                    int size = annotations.size();
                    if (size != 0) {
                        setGridVisible(problemsSection, true);
                        problemsSection.setAnnotations(annotations);
                    }
                }
            } else {
                return;
            }
            // update 'hasContents' flag
            hasContents |= isGridVisible(elementSection);
            hasContents |= isGridVisible(librarySection);
            hasContents |= isGridVisible(problemsSection);
            hasContents |= isGridVisible(docSection);
            hasContents |= isGridVisible(staticTypeSection);
            hasContents |= isGridVisible(propagatedTypeSection);
            hasContents |= isGridVisible(parameterSection);
            // Layout and pack.
            Shell shell = getShell();
            shell.layout(true, true);
            shell.pack();
            shell.layout(true, true);
            shell.pack();
        }

        @Override
        protected void createContent(Composite parent) {
            container = toolkit.createComposite(parent);
            GridLayoutFactory.create(container);
            elementSection = new TextSection(container, "Element");
            classSection = new TextSection(container, "Containing class");
            librarySection = new TextSection(container, "Containing library");
            problemsSection = new AnnotationsSection(container, "Problems");
            docSection = new DocSection(container, "Documentation");
            staticTypeSection = new TextSection(container, "Static type");
            propagatedTypeSection = new TextSection(container, "Propagated type");
            parameterSection = new TextSection(container, "Parameter");
        }
    }

    private static class DartInformationControlCreator extends AbstractReusableInformationControlCreator {
        @Override
        protected IInformationControl doCreateInformationControl(Shell parent) {
            return new DartInformationControl(parent);
        }
    }

    private static class DocSection {
        private final FormToolkit toolkit;
        private final Section section;
        private final StyledText textWidget;

        public DocSection(Composite parent, String title) {
            toolkit = createToolkit(parent.getDisplay());
            this.section = toolkit.createSection(parent, Section.TITLE_BAR);
            GridDataFactory.create(section).grab().fill();
            section.setText(title);
            // create Composite to draw flat border
            Composite body = toolkit.createComposite(section);
            GridLayoutFactory.create(body).margins(2);
            section.setClient(body);
            // create StyledText widget
            textWidget = new StyledText(body, SWT.H_SCROLL | SWT.V_SCROLL);
            textWidget.setMargins(5, 5, 5, 5);
            // We do this to prevent line spacing changing.
            // See https://code.google.com/p/dart/issues/detail?id=15899
            textWidget.setLineSpacing(1);
            // configure flat border
            textWidget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
            toolkit.paintBordersFor(body);
        }

        public void setDoc(String doc) {
            textWidget.setText(doc);
            textWidget.setSelection(0);
            // apply size
            Point requiredSize = textWidget.computeSize(SWT.DEFAULT, SWT.DEFAULT);
            GridDataFactory gdf = GridDataFactory.create(textWidget);
            int maxWidth = gdf.convertWidthInCharsToPixels(85);
            int maxHeight = gdf.convertHeightInCharsToPixels(15);
            int width = Math.min(requiredSize.x, maxWidth);
            int height = Math.min(requiredSize.y, maxHeight);
            gdf.hint(width, height).grab().fill();
        }
    }

    private static class HoverInfo_NEW {
        private HoverInformation hover;
        private List<Annotation> annotations;

        public HoverInfo_NEW(HoverInformation hover, List<Annotation> annotations) {
            this.hover = hover;
            this.annotations = annotations;
        }
    }

    private static class HoverInfo_OLD {
        AstNode node;
        Element element;
        List<Annotation> annotations;

        public HoverInfo_OLD(AstNode node, Element element, List<Annotation> annotations) {
            this.node = node;
            this.element = element;
            this.annotations = annotations;
        }
    }

    private static class TextSection {
        private final FormToolkit toolkit;
        private final Section section;
        private final StyledText textWidget;

        public TextSection(Composite parent, String title) {
            toolkit = createToolkit(parent.getDisplay());
            this.section = toolkit.createSection(parent, Section.TITLE_BAR);
            GridDataFactory.create(section).grabHorizontal().fill();
            section.setText(title);
            textWidget = new StyledText(section, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP);
            toolkit.adapt(textWidget, false, false);
            section.setClient(textWidget);
        }

        public void setText(String text) {
            textWidget.setText(text);
            textWidget.setSelection(0);
        }

        public void setTitle(String title) {
            section.setText(title);
        }
    }

    private static final List<ITextHover> hoverContributors = Lists.newArrayList();

    private static FormToolkit toolkit;

    /**
     * Register a {@link ITextHover} tooltip contributor.
     */
    public static void addContributer(ITextHover hoverContributor) {
        hoverContributors.add(hoverContributor);
    }

    private static FormToolkit createToolkit(Display display) {
        if (toolkit == null) {
            toolkit = new FormToolkit(display);
        }

        return toolkit;
    }

    /**
     * Sorts given {@link Annotation}s by severity and location.
     */
    private static List<Annotation> getSortedAnnotations(List<Annotation> annotations) {
        annotations = Lists.newArrayList(annotations);
        Collections.sort(annotations, new Comparator<Annotation>() {
            @Override
            public int compare(Annotation o1, Annotation o2) {
                IMarker m1 = getMarker(o1);
                IMarker m2 = getMarker(o2);
                // no marker(s)
                if (m1 != null && m2 == null) {
                    return 1;
                }
                if (m1 == null && m2 != null) {
                    return -1;
                }
                if (m1 == null && m2 == null) {
                    return 0;
                }
                // compare severity
                int val = m2.getAttribute(IMarker.SEVERITY, 0) - m1.getAttribute(IMarker.SEVERITY, 0);
                if (val != 0) {
                    return val;
                }
                // compare offset
                return m2.getAttribute(IMarker.CHAR_START, 0) - m1.getAttribute(IMarker.CHAR_START, 0);
            }

            private IMarker getMarker(Annotation annotation) {
                return (annotation instanceof MarkerAnnotation) ? ((MarkerAnnotation) annotation).getMarker()
                        : null;
            }
        });
        return annotations;
    }

    private final ISourceViewer viewer;
    private final DartSourceViewerConfiguration viewerConfiguration;
    private CompilationUnitEditor editor;
    private IInformationControlCreator informationControlCreator;

    private ITextHover lastReturnedHover;
    private int lastClickOffset;

    public DartHover(ITextEditor editor, ISourceViewer viewer, DartSourceViewerConfiguration viewerConfiguration) {
        this.viewer = viewer;
        this.viewerConfiguration = viewerConfiguration;
        if (editor instanceof CompilationUnitEditor) {
            this.editor = (CompilationUnitEditor) editor;
            StyledText textWidget = this.editor.getViewer().getTextWidget();
            textWidget.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseDown(MouseEvent e) {
                    SourceRange range = DartHover.this.editor.getTextSelectionRange();
                    lastClickOffset = range != null ? range.getOffset() : -1;
                }
            });
            textWidget.addMouseMoveListener(new MouseMoveListener() {
                @Override
                public void mouseMove(MouseEvent e) {
                    lastClickOffset = -1;
                }
            });
        }
    }

    @Override
    public IInformationControlCreator getHoverControlCreator() {
        if (lastReturnedHover instanceof ITextHoverExtension) {
            return ((ITextHoverExtension) lastReturnedHover).getHoverControlCreator();
        }
        if (informationControlCreator == null) {
            informationControlCreator = new DartInformationControlCreator();
        }
        return informationControlCreator;
    }

    @Override
    public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
        lastReturnedHover = null;
        return null;
    }

    @Override
    public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
        lastReturnedHover = null;
        // Check through the contributed hover providers.
        for (ITextHover hoverContributer : hoverContributors) {
            if (hoverContributer instanceof ITextHoverExtension2) {
                Object hoverInfo = ((ITextHoverExtension2) hoverContributer).getHoverInfo2(textViewer, hoverRegion);
                if (hoverInfo != null) {
                    lastReturnedHover = hoverContributer;
                    return hoverInfo;
                }
            }
        }
        // Editor based hover.
        if (editor != null) {
            List<Annotation> annotations = getAnnotations(hoverRegion);
            // prepare node
            int offset = hoverRegion.getOffset();
            if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
                String file = editor.getInputFilePath();
                if (file != null) {
                    final CountDownLatch latch = new CountDownLatch(1);
                    final HoverInformation[] hoverInformation = new HoverInformation[1];
                    DartCore.getAnalysisServer().analysis_getHover(file, offset, new GetHoverConsumer() {
                        @Override
                        public void computedHovers(HoverInformation[] hovers) {
                            if (hovers != null && hovers.length > 0) {
                                hoverInformation[0] = hovers[0];
                                latch.countDown();
                            }
                        }

                        @Override
                        public void onError(RequestError requestError) {
                            latch.countDown();
                        }
                    });
                    // This executes on a background thread that does not hold the workspace lock
                    // so block until analysis server responds or time expires.
                    // Wait a long time only if there is nothing else to show
                    long waitTimeMillis = annotations.isEmpty() ? 4000 : 500;
                    Uninterruptibles.awaitUninterruptibly(latch, waitTimeMillis, TimeUnit.MILLISECONDS);
                    return new HoverInfo_NEW(hoverInformation[0], annotations);
                }
            } else {
                AstNode node = NewSelectionConverter.getNodeAtOffset(editor, offset);
                if (node instanceof MethodDeclaration) {
                    MethodDeclaration method = (MethodDeclaration) node;
                    node = method.getName();
                }
                // show Expression
                if (node instanceof Expression) {
                    Element element = ElementLocator.locateWithOffset(node, offset);
                    return new HoverInfo_OLD(node, element, annotations);
                }
            }
            // always show annotations, even if no node
            if (!annotations.isEmpty()) {
                return new HoverInfo_OLD(null, null, annotations);
            }
        }
        return null;
    }

    @Override
    public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
        IRegion wordRange = findWord(textViewer.getDocument(), offset);
        // ignore word if it was clicked
        {
            int wordOffset = wordRange.getOffset();
            int wordEnd = wordOffset + wordRange.getLength();
            if (wordOffset <= lastClickOffset && lastClickOffset <= wordEnd) {
                return null;
            }
        }
        // OK
        return wordRange;
    }

    private IRegion findWord(IDocument document, int offset) {
        int start = -2;
        int end = -1;

        try {

            int pos = offset;
            char c;

            while (pos >= 0) {
                c = document.getChar(pos);
                if (!Character.isUnicodeIdentifierPart(c)) {
                    break;
                }
                --pos;
            }

            start = pos;

            pos = offset;
            int length = document.getLength();

            while (pos < length) {
                c = document.getChar(pos);
                if (!Character.isUnicodeIdentifierPart(c)) {
                    break;
                }
                ++pos;
            }

            end = pos;

        } catch (BadLocationException x) {
        }

        if (start >= -1 && end > -1) {
            if (start == offset && end == offset) {
                return new Region(offset, 0);
            } else if (start == offset) {
                return new Region(start, end - start);
            } else {
                return new Region(start + 1, end - start - 1);
            }
        }

        return null;
    }

    private IAnnotationModel getAnnotationModel() {
        if (viewer instanceof ISourceViewerExtension2) {
            ISourceViewerExtension2 extension = (ISourceViewerExtension2) viewer;
            return extension.getVisualAnnotationModel();
        }
        return viewer.getAnnotationModel();
    }

    private List<Annotation> getAnnotations(IRegion region) {
        List<Annotation> annotations = Lists.newArrayList();
        IAnnotationModel model = getAnnotationModel();
        if (model != null) {
            @SuppressWarnings("unchecked")
            Iterator<Annotation> iter = model.getAnnotationIterator();
            while (iter.hasNext()) {
                Annotation annotation = iter.next();
                if (viewerConfiguration.isShownInText(annotation)) {
                    Position p = model.getPosition(annotation);
                    if (p != null && p.overlapsWith(region.getOffset(), region.getLength())) {
                        String msg = annotation.getText();
                        if (msg != null && msg.trim().length() > 0) {
                            annotations.add(annotation);
                        }
                    }
                }
            }
        }
        return annotations;
    }

}