From 51d1cf96cd37a54d0155071748cb1da8a9844e3b Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Tue, 28 Aug 2018 23:51:14 -0400 Subject: [PATCH] Range highlights and CodeLenses --- .../plugin.xml | 8 +- .../LanguageServerCommonsActivator.java | 53 +++++- .../commons/STS4LanguageClientImpl.java | 159 +++++++++++++----- .../tooling/ls/eclipse/commons/Utils.java | 21 ++- ...LanguageServerConsolesPreferencesPage.java | 2 +- .../preferences/PreferenceConstants.java | 2 + .../commons/preferences/PrefsInitializer.java | 5 +- 7 files changed, 192 insertions(+), 58 deletions(-) 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 5f60ce97a..47126e5e1 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,16 +13,16 @@ contributesToHeader="false" highlightPreferenceKey="STS4BootMarkerHighlighting" highlightPreferenceValue="true" + icon="icons/boot-icon.png" label="Boot Dynamic Info" overviewRulerPreferenceKey="STS4BootMarkerIndicationInOverviewRuler" - overviewRulerPreferenceValue="true" + overviewRulerPreferenceValue="false" presentationLayer="4" showInNextPrevDropdownToolbarAction="false" textPreferenceKey="STS4BootMarkerIndication" - textPreferenceValue="true" - textStylePreferenceKey="STS4BootMarkerTextStyle" + textPreferenceValue="false" verticalRulerPreferenceKey="STS4BootMarkerIndicationInVerticalRuler" - verticalRulerPreferenceValue="true"> + verticalRulerPreferenceValue="false"> new ProjectSorter() ); - public STS4LanguageClientImpl() { - } - private static final String ANNOTION_TYPE_ID = "org.springframework.tooling.bootinfo"; + private static final String ALT_ANNOTATION_DRAWING_STRATEGY_ID = "boot.hint.strategy"; + private static final String ALT_ANNOTATION_TYPE_ID = "org.springframework.tooling.bootinfoCodeLens"; + + /** + * Latest highlight request params. It is sufficient to only remember the last request per uri, because + * each new request is expected to replace the previous highlights. + */ + static final Map currentHighlights = new ConcurrentHashMap<>(); + + /** + * Current markers... indexed per document uri, needed sp we to be removed upon next update. + */ + private static Map currentAnnotations = new ConcurrentHashMap<>(); + + private static final IDrawingStrategy BOOT_RANGE_HIGHLIGHT_DRAWING_STRATEGY = new IDrawingStrategy() { + + @Override + public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) { + + if (gc == null) { + textWidget.redrawRange(offset, length, true); + } else { + int oldAlpha = gc.getAlpha(); + Font oldFont = gc.getFont(); + + Point left= textWidget.getLocationAtOffset(offset); + Point right = textWidget.getLocationAtOffset(offset + length); + gc.setFont(textWidget.getFont()); + int fontHeight = gc.getFontMetrics().getHeight(); + Rectangle r = new Rectangle(left.x, left.y + textWidget.getLineHeight(offset) - fontHeight, right.x - left.x, fontHeight); + gc.setAlpha(0x40); + gc.setBackground(color); + gc.fillRectangle(r); + + gc.setAlpha(oldAlpha); + gc.setFont(oldFont); + } + } + + }; + static class UpdateHighlights extends UIJob { private String target; @@ -71,51 +122,77 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La @Override public IStatus runInUIThread(IProgressMonitor monitor) { - Utils.getActiveSourceViewers().forEach(this::updateSourceViewer); + Utils.getActiveEditors().forEach(this::updateSourceViewer); return Status.OK_STATUS; } - protected void updateSourceViewer(ISourceViewer sourceViewer) { - IAnnotationModel annotationModel = sourceViewer.getAnnotationModel(); - if (annotationModel != null) { - IDocument doc = sourceViewer.getDocument(); - if (doc != null && sourceViewer != null) { - if (target != null) { - HighlightParams highlightParams = currentHighlights.get(target); - if (Utils.isProperDocumentIdFor(doc, highlightParams.getDoc())) { - if (annotationModel instanceof IAnnotationModelExtension) { - updateAnnotations(target, sourceViewer, (IAnnotationModelExtension) annotationModel); + protected void updateSourceViewer(IEditorPart editor) { + ITextViewer viewer = editor.getAdapter(ITextViewer.class); + if (viewer instanceof ISourceViewer) { + ISourceViewer sourceViewer = (ISourceViewer) viewer; + IAnnotationModel annotationModel = sourceViewer.getAnnotationModel(); + if (annotationModel != null) { + IDocument doc = sourceViewer.getDocument(); + if (doc != null && sourceViewer != null) { + if (target != null) { + HighlightParams highlightParams = currentHighlights.get(target); + if (Utils.isProperDocumentIdFor(doc, highlightParams.getDoc())) { + updateHighlightAnnotations(editor, sourceViewer, annotationModel, target); } - if (sourceViewer instanceof ISourceViewerExtension5) { - ((ISourceViewerExtension5) sourceViewer).updateCodeMinings(); - } - } - } else { - URI uri = Utils.findDocUri(doc); - if (uri != null) { - if (annotationModel instanceof IAnnotationModelExtension) { - updateAnnotations(uri.toString(), sourceViewer, (IAnnotationModelExtension) annotationModel); - } - if (sourceViewer instanceof ISourceViewerExtension5) { - ((ISourceViewerExtension5) sourceViewer).updateCodeMinings(); + } else { + URI uri = Utils.findDocUri(doc); + if (uri != null) { + updateHighlightAnnotations(editor, sourceViewer, annotationModel, uri.toString()); } } } } } } + }; - /** - * Latest highlight request params. It is sufficient to only remember the last request per uri, because - * each new request is expected to replace the previous highlights. - */ - static final Map currentHighlights = new ConcurrentHashMap<>(); + private static void updateHighlightAnnotations(IEditorPart editor, ISourceViewer sourceViewer, + IAnnotationModel annotationModel, String docUri) { + if (annotationModel instanceof IAnnotationModelExtension) { + if (isCodeLensHighlightOn()) { + addBootRangeHighlightSupport(editor, sourceViewer); + } + updateAnnotations(docUri, sourceViewer, (IAnnotationModelExtension) annotationModel); + } + if (sourceViewer instanceof ISourceViewerExtension5) { + ((ISourceViewerExtension5) sourceViewer).updateCodeMinings(); + } + } - /** - * Current markers... indexed per document uri, needed sp we to be removed upon next update. - */ - private static Map currentAnnotations = new ConcurrentHashMap<>(); + @SuppressWarnings("unchecked") + private static void addBootRangeHighlightSupport(IEditorPart editor, ISourceViewer sourceViewer) { + try { + Field f = AbstractDecoratedTextEditor.class.getDeclaredField("fSourceViewerDecorationSupport"); + f.setAccessible(true); + SourceViewerDecorationSupport support = (SourceViewerDecorationSupport) f.get(editor); + f = SourceViewerDecorationSupport.class.getDeclaredField("fAnnotationPainter"); + f.setAccessible(true); + AnnotationPainter painter = (AnnotationPainter) f.get(support); + + f = AnnotationPainter.class.getDeclaredField("fAnnotationType2Color"); + f.setAccessible(true); + if (((Map)f.get(painter)).get(ALT_ANNOTATION_TYPE_ID) != LanguageServerCommonsActivator.getInstance().getBootHighlightRangeColor()) { + painter.setAnnotationTypeColor(ALT_ANNOTATION_TYPE_ID, LanguageServerCommonsActivator.getInstance().getBootHighlightRangeColor()); + painter.addDrawingStrategy(ALT_ANNOTATION_DRAWING_STRATEGY_ID, BOOT_RANGE_HIGHLIGHT_DRAWING_STRATEGY); + painter.addAnnotationType(ALT_ANNOTATION_TYPE_ID, ALT_ANNOTATION_DRAWING_STRATEGY_ID); + } + + } catch (Exception e) { + LanguageServerCommonsActivator.logError(e, + "Failed to contribute alternative range highlight annotation. Switch off highlight CodeLense under STS Language Server preferences!"); + } + } + + private static boolean isCodeLensHighlightOn() { + IPreferenceStore store = LanguageServerCommonsActivator.getInstance().getPreferenceStore(); + return store.getBoolean(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS); + } static synchronized void updateAnnotations(String target, ISourceViewer sourceViewer, IAnnotationModelExtension annotationModel) { @@ -124,24 +201,24 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La toRemove = new Annotation[0]; } HighlightParams highlightParams = currentHighlights.get(target); - IPreferenceStore store = LanguageServerCommonsActivator.getInstance().getPreferenceStore(); - List highlights = store.getBoolean(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS) || highlightParams == null ? null : highlightParams.getCodeLenses(); - Map newAnnotations = createAnnotations(sourceViewer.getDocument(), highlights); + List highlights = highlightParams == null ? null : highlightParams.getCodeLenses(); + String annotationType = isCodeLensHighlightOn() ? ALT_ANNOTATION_TYPE_ID : ANNOTION_TYPE_ID; + Map newAnnotations = createAnnotations(sourceViewer.getDocument(), highlights, annotationType); annotationModel.replaceAnnotations(toRemove, newAnnotations); currentAnnotations.put(target, newAnnotations.keySet().toArray(new Annotation[newAnnotations.size()])); } - private static Map createAnnotations(IDocument doc, List highlights) { + private static Map createAnnotations(IDocument doc, List highlights, String annotationType) { ImmutableMap.Builder annotations = ImmutableMap.builder(); if (highlights==null) { highlights = ImmutableList.of(); } - highlights.stream().map(CodeLens::getRange).forEach(rng -> { + highlights.stream().map(CodeLens::getRange).distinct().forEach(rng -> { try { int start = LSPEclipseUtils.toOffset(rng.getStart(), doc); int end = LSPEclipseUtils.toOffset(rng.getEnd(), doc); Position e_rng = new Position(start, Math.max(0, end-start)); - annotations.put(new Annotation(ANNOTION_TYPE_ID, false, null), e_rng); + annotations.put(new Annotation(annotationType, false, null), e_rng); } catch (BadLocationException e) { //ignore invalid highlights } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/Utils.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/Utils.java index 29d61c87d..0a02c5a92 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/Utils.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/Utils.java @@ -21,24 +21,29 @@ import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.lsp4e.LanguageServiceAccessor; import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo; import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; +import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PlatformUI; @SuppressWarnings("restriction") public class Utils { public static Stream getActiveTextViewers() { - return Arrays.stream(PlatformUI.getWorkbench().getWorkbenchWindows()) - .filter(Objects::nonNull) - .flatMap(ww -> Arrays.stream(ww.getPages())) - .filter(Objects::nonNull) - .flatMap(page -> Arrays.stream(page.getEditorReferences())) - .filter(Objects::nonNull) - .map(ref -> ref.getEditor(false)) - .filter(Objects::nonNull) + return getActiveEditors() .map(editorPart -> editorPart.getAdapter(ITextViewer.class)) .filter(Objects::nonNull); } + public static Stream getActiveEditors() { + return Arrays.stream(PlatformUI.getWorkbench().getWorkbenchWindows()) + .filter(Objects::nonNull) + .flatMap(ww -> Arrays.stream(ww.getPages())) + .filter(Objects::nonNull) + .flatMap(page -> Arrays.stream(page.getEditorReferences())) + .filter(Objects::nonNull) + .map(ref -> ref.getEditor(false)) + .filter(Objects::nonNull); + } + public static Stream getActiveSourceViewers() { return getActiveTextViewers() .filter(viewer -> viewer instanceof ISourceViewer) diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/LanguageServerConsolesPreferencesPage.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/LanguageServerConsolesPreferencesPage.java index 51c76e383..5e2d8b091 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/LanguageServerConsolesPreferencesPage.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/LanguageServerConsolesPreferencesPage.java @@ -47,7 +47,7 @@ public class LanguageServerConsolesPreferencesPage extends FieldEditorPreference SWTFactory.createLabel(parent, "Settings for Spring Languare Server extensions:", 2); - addField(new BooleanFieldEditor(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS, "Highlights CodeLens", parent)); + addField(new BooleanFieldEditor(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS, "Highlights CodeLens (Experimental)", parent)); } } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/PreferenceConstants.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/PreferenceConstants.java index 04b092fda..bc1a5a784 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/PreferenceConstants.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/PreferenceConstants.java @@ -14,4 +14,6 @@ public class PreferenceConstants { public static final String HIGHLIGHT_CODELENS_PREFS = "highlight.codelens"; + public static final String HIGHLIGHT_RANGE_COLOR_PREFS = "STS4BootMarkerIndicationColor"; + } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/PrefsInitializer.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/PrefsInitializer.java index 1feb82850..e973c5a69 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/PrefsInitializer.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/preferences/PrefsInitializer.java @@ -10,7 +10,8 @@ *******************************************************************************/ package org.springframework.tooling.ls.eclipse.commons.preferences; -import static org.springframework.tooling.ls.eclipse.commons.preferences.LanguageServerConsolePreferenceConstants.*; +import static org.springframework.tooling.ls.eclipse.commons.preferences.LanguageServerConsolePreferenceConstants.ALL_SERVERS; +import static org.springframework.tooling.ls.eclipse.commons.preferences.LanguageServerConsolePreferenceConstants.ENABLE_BY_DEFAULT; import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; import org.eclipse.jface.preference.IPreferenceStore; @@ -23,6 +24,6 @@ public class PrefsInitializer extends AbstractPreferenceInitializer { for (ServerInfo s : ALL_SERVERS) { store.setDefault(s.preferenceKey, ENABLE_BY_DEFAULT); } - store.setDefault(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS, true); + store.setDefault(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS, false); } } \ No newline at end of file