From cc756ba2d473b36080dd19fa10620f898626ec24 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Mon, 23 Jul 2018 16:37:03 -0400 Subject: [PATCH] New boot-hints for Eclipse client --- .../plugin.xml | 1 - .../eclipse/commons/BootInlineAnnotation.java | 55 ++++++++++++++ .../LanguageServerCommonsActivator.java | 3 + .../commons/STS4LanguageClientImpl.java | 75 ++++++++++++++++++- 4 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/BootInlineAnnotation.java diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/plugin.xml b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/plugin.xml index 0008b87b0..cf580e9d0 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/plugin.xml +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/plugin.xml @@ -13,7 +13,6 @@ contributesToHeader="false" highlightPreferenceKey="STS4BootMarkerHighlighting" highlightPreferenceValue="true" - icon="icons/boot-icon.png" label="Boot Dynamic Info" overviewRulerPreferenceKey="STS4BootMarkerIndicationInOverviewRuler" overviewRulerPreferenceValue="true" diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/BootInlineAnnotation.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/BootInlineAnnotation.java new file mode 100644 index 000000000..3120dd27f --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/BootInlineAnnotation.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.commons; + +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.inlined.LineContentAnnotation; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; + +/** + * Boot icon inlined annotation + * + * @author Alex Boyko + * + */ +public class BootInlineAnnotation extends LineContentAnnotation { + + private static final int SPACING = 2; + + public BootInlineAnnotation(Position pos, ISourceViewer viewer) { + super(pos, viewer); + } + + @Override + protected int drawAndComputeWidth(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { + FontMetrics fontMetrics = gc.getFontMetrics(); + int height = fontMetrics.getHeight(); + + Image bootImage = LanguageServerCommonsActivator.getInstance().getImageRegistry().get(LanguageServerCommonsActivator.BOOT_ICON_2X_KEY); + Rectangle bootImgBounds = bootImage.getBounds(); + int width = (int) Math.round(bootImgBounds.width / (double) bootImgBounds.height * height); + + Rectangle backgroundRect = new Rectangle(x, y, width + SPACING, fontMetrics.getHeight()); + gc.setBackground(textWidget.getBackground()); + gc.fillRectangle(backgroundRect); + + gc.drawImage(bootImage, bootImgBounds.x, bootImgBounds.y, bootImgBounds.width, bootImgBounds.height, x, y, width, height); + + return backgroundRect.width; + } + +} \ No newline at end of file diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/LanguageServerCommonsActivator.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/LanguageServerCommonsActivator.java index 5588de19c..382f95889 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/LanguageServerCommonsActivator.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/LanguageServerCommonsActivator.java @@ -26,6 +26,8 @@ public class LanguageServerCommonsActivator extends AbstractUIPlugin { public static final String PLUGIN_ID = "org.springframework.tooling.ls.eclipse.commons"; + public static final String BOOT_ICON_2X_KEY = "boot-icon-key"; + private static LanguageServerCommonsActivator instance; public LanguageServerCommonsActivator() { @@ -35,6 +37,7 @@ public class LanguageServerCommonsActivator extends AbstractUIPlugin { public void start(BundleContext context) throws Exception { instance = this; super.start(context); + getImageRegistry().put(BOOT_ICON_2X_KEY, getImageDescriptor("icons/boot-icon@2x.png")); } public final static ImageDescriptor getImageDescriptor(String path) { diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java index 9269b7be5..f084f9229 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java @@ -14,8 +14,11 @@ import java.lang.reflect.Method; import java.net.URI; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.CompletableFuture; import org.eclipse.core.runtime.IProgressMonitor; @@ -24,11 +27,16 @@ import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewerExtension2; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationPainter; +import org.eclipse.jface.text.source.IAnnotationAccess; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IAnnotationModelExtension; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.inlined.AbstractInlinedAnnotation; +import org.eclipse.jface.text.source.inlined.InlinedAnnotationSupport; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageClientImpl; import org.eclipse.lsp4e.LanguageServiceAccessor; @@ -114,7 +122,7 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La IDocument doc = sourceViewer.getDocument(); if (sourceViewer!=null) { if (doc!=null && annotationModel instanceof IAnnotationModelExtension) { - updateAnnotations(target, doc, (IAnnotationModelExtension) annotationModel); + updateAnnotations(target, sourceViewer, (IAnnotationModelExtension) annotationModel); } } } @@ -137,8 +145,11 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La */ private Map currentAnnotations = new HashMap<>(); - private synchronized void updateAnnotations(String target, IDocument doc, IAnnotationModelExtension annotationModel) { + private Map viewerInlinedAnnotationSupport = new WeakHashMap<>(); + + private synchronized void updateAnnotations(String target, ISourceViewer sourceViewer, IAnnotationModelExtension annotationModel) { if (target!=null) { + IDocument doc = sourceViewer.getDocument(); Collection infos = LanguageServiceAccessor.getLSPDocumentInfosFor(doc, (x) -> true); for (LSPDocumentInfo docInfo : infos) { URI uri = docInfo.getFileUri(); @@ -147,14 +158,72 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La if (toRemove==null) { toRemove = new Annotation[0]; } - Map newAnnotations = createAnnotations(doc, currentHighlights.get(target)); + List highlights = currentHighlights.get(target); + Map newAnnotations = createAnnotations(doc, highlights); annotationModel.replaceAnnotations(toRemove, newAnnotations); currentAnnotations.put(target, newAnnotations.keySet().toArray(new Annotation[newAnnotations.size()])); + updateInlinedAnnotations(sourceViewer, highlights); } } } } + private void updateInlinedAnnotations(final ISourceViewer sourceViewer, List highlights) { + InlinedAnnotationSupport support = viewerInlinedAnnotationSupport.get(sourceViewer); + if (support == null) { + final InlinedAnnotationSupport inlinedSupport = new InlinedAnnotationSupport(); + inlinedSupport.install(sourceViewer, createAnnotationPainter(sourceViewer)); + viewerInlinedAnnotationSupport.put(sourceViewer, inlinedSupport); + sourceViewer.getTextWidget().addDisposeListener((e) -> { + inlinedSupport.uninstall(); + viewerInlinedAnnotationSupport.remove(sourceViewer); + }); + support = inlinedSupport; + } + Set annotations = new HashSet<>(); + if (highlights==null) { + highlights = ImmutableList.of(); + } + IDocument doc = sourceViewer.getDocument(); + for (Range rng : highlights) { + try { + int start = LSPEclipseUtils.toOffset(rng.getStart(), doc); + Position colorPos = new Position(start, 0); + BootInlineAnnotation colorAnnotation = support.findExistingAnnotation(colorPos); + if (colorAnnotation == null) { + colorAnnotation = new BootInlineAnnotation(colorPos, sourceViewer); + } + annotations.add(colorAnnotation); + } catch (BadLocationException e) { + //ignore invalid highlights + } + } + support.updateAnnotations(annotations); + } + + private static AnnotationPainter createAnnotationPainter(ISourceViewer viewer) { + IAnnotationAccess annotationAccess = new IAnnotationAccess() { + @Override + public Object getType(Annotation annotation) { + return annotation.getType(); + } + + @Override + public boolean isMultiLine(Annotation annotation) { + return true; + } + + @Override + public boolean isTemporary(Annotation annotation) { + return true; + } + + }; + AnnotationPainter painter = new AnnotationPainter(viewer, annotationAccess); + ((ITextViewerExtension2) viewer).addPainter(painter); + return painter; + } + private Map createAnnotations(IDocument doc, List highlights) { ImmutableMap.Builder annotations = ImmutableMap.builder(); if (highlights==null) {