| /* |
| * 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.server.generated.types.HoverInformation; |
| import com.google.dart.server.generated.types.RequestError; |
| 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.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; |
| } |
| |
| } |