CodeLens for boot hints in VSCode

This commit is contained in:
BoykoAlex
2018-07-31 13:37:23 -04:00
parent 6772b05934
commit 374b5f0ad7
31 changed files with 571 additions and 200 deletions

View File

@@ -50,7 +50,7 @@
"dependencies": {
"atom-package-deps": "^4.6.0",
"download": "^6.2.5",
"@pivotal-tools/atom-languageclient-commons": "0.0.7"
"@pivotal-tools/atom-languageclient-commons": "0.0.8"
},
"devDependencies": {
"typescript": "^2.7.2",

View File

@@ -19,7 +19,7 @@
"properties.json"
],
"dependencies": {
"@pivotal-tools/atom-languageclient-commons": "0.0.7",
"@pivotal-tools/atom-languageclient-commons": "0.0.8",
"atom-package-deps": "^4.6.0",
"download": "^6.2.5"
},

View File

@@ -1,6 +1,6 @@
import {Convert} from 'atom-languageclient';
import {AtomEnvironment, TextEditor} from 'atom';
import {Position, Range, TextDocumentIdentifier} from 'vscode-languageserver-protocol';
import {Position, CodeLens, TextDocumentIdentifier} from 'vscode-languageserver-protocol';
export class StsAdapter {
@@ -34,5 +34,5 @@ export interface ProgressParams {
export interface HighlightParams {
readonly doc: TextDocumentIdentifier;
readonly ranges: Range[];
readonly codeLenses: CodeLens[];
}

View File

@@ -1,6 +1,6 @@
{
"name": "@pivotal-tools/atom-languageclient-commons",
"version": "0.0.7",
"version": "0.0.8",
"description": "Atom language client commons for STS4 language servers",
"repository": "https://github.com/spring-projects/sts4",
"license": "MIT",

View File

@@ -21,7 +21,7 @@
"dependencies": {
"atom-package-deps": "^4.6.0",
"download": "^6.2.5",
"@pivotal-tools/atom-languageclient-commons": "0.0.7"
"@pivotal-tools/atom-languageclient-commons": "0.0.8"
},
"devDependencies": {
"typescript": "^2.7.2",

View File

@@ -1,6 +1,6 @@
import {StsAdapter, HighlightParams} from '@pivotal-tools/atom-languageclient-commons';
import {Convert} from 'atom-languageclient';
import { Range } from 'vscode-languageserver-protocol';
import { Range, CodeLens } from 'vscode-languageserver-protocol';
import {TextEditor, DecorationOptions } from 'atom';
const BOOT_HINT_GUTTER_NAME = 'boot-hint-gutter';
@@ -18,11 +18,12 @@ export class BootStsAdapter extends StsAdapter {
}
onHighlight(params: HighlightParams) {
this.findEditors(params.doc.uri).forEach(editor => this.markHintsForEditor(editor, params.ranges));
this.findEditors(params.doc.uri).forEach(editor => this.markHintsForEditor(editor, params.codeLenses));
}
private markHintsForEditor(editor: TextEditor, ranges: Range[]) {
private markHintsForEditor(editor: TextEditor, codeLenses: CodeLens[]) {
editor.getDecorations(DECORATION_OPTIONS).map(decoration => decoration.getMarker()).forEach(m => m.destroy());
const ranges = codeLenses.map(cl => cl.range);
if (Array.isArray(ranges)) {
ranges.forEach(range => this.createHintMarker(editor, range));
}

View File

@@ -33,7 +33,7 @@
},
"dependencies": {
"atom-languageclient": "0.9.5",
"@pivotal-tools/atom-languageclient-commons": "0.0.7",
"@pivotal-tools/atom-languageclient-commons": "0.0.8",
"atom-package-deps": "^4.6.0",
"download": "^6.2.5"
},

View File

@@ -0,0 +1,52 @@
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.LineHeaderAnnotation;
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;
public class BootHeadlineAnnotation extends LineHeaderAnnotation {
public BootHeadlineAnnotation(Position position, ISourceViewer viewer) {
super(position, viewer);
}
// @Override
// public void draw(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) {
// initGC(textWidget, color, gc);
//
// 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);
//
// gc.drawImage(bootImage, bootImgBounds.x, bootImgBounds.y, bootImgBounds.width, bootImgBounds.height, x, y, width, height);
//
// int textX = x + width + 4;
// int textY = y;
//
// gc.drawText("CODE LENS goes here!", textX, textY);
// }
//
// /**
// * Initialize GC with given color and styled text background color and font.
// *
// * @param textWidget the text widget
// * @param color the color
// * @param gc the gc to initialize
// */
// private void initGC(StyledText textWidget, Color color, GC gc) {
// gc.setForeground(color);
// gc.setBackground(textWidget.getBackground());
// gc.setFont(textWidget.getFont());
// }
}

View File

@@ -12,7 +12,7 @@ package org.springframework.tooling.ls.eclipse.commons;
import java.util.List;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
public class HighlightParams {
@@ -23,15 +23,15 @@ public class HighlightParams {
// a update site.
private VersionedTextDocumentIdentifier doc;
private List<Range> ranges;
private List<CodeLens> codeLenses;
public HighlightParams() {
}
public HighlightParams(VersionedTextDocumentIdentifier doc, List<Range> ranges) {
public HighlightParams(VersionedTextDocumentIdentifier doc, List<CodeLens> codeLenses) {
super();
this.doc = doc;
this.ranges = ranges;
this.codeLenses = codeLenses;
}
public VersionedTextDocumentIdentifier getDoc() {
return doc;
@@ -39,15 +39,13 @@ public class HighlightParams {
public void setDoc(VersionedTextDocumentIdentifier doc) {
this.doc = doc;
}
public List<Range> getRanges() {
return ranges;
}
public void setRanges(List<Range> ranges) {
this.ranges = ranges;
public List<CodeLens> getCodeLenses() {
return codeLenses;
}
@Override
public String toString() {
return "HighlightParams [doc=" + doc + ", ranges=" + ranges + "]";
public void setCodeLenses(List<CodeLens> codeLenses) {
this.codeLenses = codeLenses;
}
}

View File

@@ -23,8 +23,11 @@ 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;
@@ -32,7 +35,7 @@ import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageClientImpl;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
@@ -137,6 +140,8 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
*/
private Map<String, Annotation[]> currentAnnotations = new HashMap<>();
// private Map<ISourceViewer, InlinedAnnotationSupport> viewerInlinedAnnotationSupport = new WeakHashMap<>();
private synchronized void updateAnnotations(String target, ISourceViewer sourceViewer, IAnnotationModelExtension annotationModel) {
if (target!=null) {
HighlightParams highlightParams = currentHighlights.get(target);
@@ -146,10 +151,11 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
if (toRemove==null) {
toRemove = new Annotation[0];
}
List<Range> highlights = highlightParams == null ? null : highlightParams.getRanges();
List<CodeLens> highlights = highlightParams == null ? null : highlightParams.getCodeLenses();
Map<Annotation, Position> newAnnotations = createAnnotations(doc, highlights);
annotationModel.replaceAnnotations(toRemove, newAnnotations);
currentAnnotations.put(target, newAnnotations.keySet().toArray(new Annotation[newAnnotations.size()]));
// updateInlinedAnnotations(sourceViewer, highlights);
}
}
}
@@ -166,12 +172,75 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
return false;
}
private Map<Annotation, Position> createAnnotations(IDocument doc, List<Range> highlights) {
// private void updateInlinedAnnotations(final ISourceViewer sourceViewer, List<CodeLens> 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<AbstractInlinedAnnotation> annotations = new HashSet<>();
// if (highlights==null) {
// highlights = ImmutableList.of();
// }
// IDocument doc = sourceViewer.getDocument();
// final InlinedAnnotationSupport inlinedSupport = support;
// highlights.stream().filter(hl -> hl.getCommand() != null && hl.getCommand().getTitle() != null).forEach(codeLens -> {
// try {
// Range rng = codeLens.getRange();
// int start = LSPEclipseUtils.toOffset(rng.getStart(), doc);
//
// // "Code Lens" line header annotation
// Position headerPos = new Position(start + 1, 1);
// BootHeadlineAnnotation headlineAnnotation = inlinedSupport.findExistingAnnotation(headerPos);
// if (headlineAnnotation == null) {
// headlineAnnotation = new BootHeadlineAnnotation(headerPos, sourceViewer);
// }
// headlineAnnotation.setText(codeLens.getCommand().getTitle());
// annotations.add(headlineAnnotation);
// } catch (BadLocationException e) {
// //ignore invalid highlights
// }
// });
// highlights.forEach(codeLens -> {
// });
// 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<Annotation, Position> createAnnotations(IDocument doc, List<CodeLens> highlights) {
ImmutableMap.Builder<Annotation, Position> annotations = ImmutableMap.builder();
if (highlights==null) {
highlights = ImmutableList.of();
}
for (Range rng : highlights) {
highlights.stream().map(CodeLens::getRange).forEach(rng -> {
try {
int start = LSPEclipseUtils.toOffset(rng.getStart(), doc);
int end = LSPEclipseUtils.toOffset(rng.getEnd(), doc);
@@ -180,7 +249,7 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
} catch (BadLocationException e) {
//ignore invalid highlights
}
}
});
return annotations.build();
}

View File

@@ -12,21 +12,21 @@ package org.springframework.ide.vscode.commons.languageserver;
import java.util.List;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
public class HighlightParams {
private VersionedTextDocumentIdentifier doc;
private List<Range> ranges;
private List<CodeLens> codeLenses;
public HighlightParams() {
}
public HighlightParams(VersionedTextDocumentIdentifier doc, List<Range> ranges) {
public HighlightParams(VersionedTextDocumentIdentifier doc, List<CodeLens> codeLenses) {
super();
this.doc = doc;
this.ranges = ranges;
this.codeLenses = codeLenses;
}
public VersionedTextDocumentIdentifier getDoc() {
return doc;
@@ -34,11 +34,13 @@ public class HighlightParams {
public void setDoc(VersionedTextDocumentIdentifier doc) {
this.doc = doc;
}
public List<Range> getRanges() {
return ranges;
public List<CodeLens> getCodeLenses() {
return codeLenses;
}
public void setRanges(List<Range> ranges) {
this.ranges = ranges;
public void setCodeLenses(List<CodeLens> codeLenses) {
this.codeLenses = codeLenses;
}
}

View File

@@ -14,6 +14,7 @@ package org.springframework.ide.vscode.languageserver.testharness;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness.getDocString;
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains;
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertDoesNotContain;
@@ -31,6 +32,7 @@ import java.util.stream.Collectors;
import javax.swing.text.BadLocationException;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.Diagnostic;
@@ -56,8 +58,6 @@ import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
import static org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness.*;
public class Editor {
public static final Predicate<CompletionItem> RELAXED_COMPLETION
@@ -210,7 +210,7 @@ public class Editor {
public List<Range> assertHighlights(String... expectedHighlights) throws Exception {
HighlightParams highlights = harness.getHighlights(doc);
List<Range> ranges = highlights != null ? new ArrayList<>(highlights.getRanges()) : ImmutableList.of();
List<Range> ranges = highlights != null ? highlights.getCodeLenses().stream().map(CodeLens::getRange).collect(Collectors.toList()) : ImmutableList.of();
Collections.sort(ranges, RANGE_COMPARATOR);
List<String> actualHighlights = ranges.stream()
.map(this::getText)

View File

@@ -145,7 +145,7 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
this.defaultLanguageId = defaultLanguageId;
}
public static final Duration HIGHLIGHTS_TIMEOUT = Duration.ofMillis(15_000); //Why so long?
public static final Duration HIGHLIGHTS_TIMEOUT = Duration.ofMillis(15000000000L); //Why so long?
public LanguageServerHarness(Callable<S> factory) throws Exception {
this(factory, LanguageId.PLAINTEXT);

View File

@@ -296,78 +296,92 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
RunningAppProvider runningAppProvider) {
AnnotationHierarchyAwareLookup<HoverProvider> providers = new AnnotationHierarchyAwareLookup<>();
providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueHoverProvider());
ValueHoverProvider valueHoverProvider = new ValueHoverProvider();
RequestMappingHoverProvider requestMappingHoverProvider = new RequestMappingHoverProvider();
AutowiredHoverProvider autowiredHoverProvider = new AutowiredHoverProvider(this);
ComponentInjectionsHoverProvider componentInjectionsHoverProvider = new ComponentInjectionsHoverProvider(this);
BeanInjectedIntoHoverProvider beanInjectedIntoHoverProvider = new BeanInjectedIntoHoverProvider(this);
ConditionalsLiveHoverProvider conditionalsLiveHoverProvider = new ConditionalsLiveHoverProvider();
providers.put(Annotations.SPRING_REQUEST_MAPPING, new RequestMappingHoverProvider());
providers.put(Annotations.SPRING_GET_MAPPING, new RequestMappingHoverProvider());
providers.put(Annotations.SPRING_POST_MAPPING, new RequestMappingHoverProvider());
providers.put(Annotations.SPRING_PUT_MAPPING, new RequestMappingHoverProvider());
providers.put(Annotations.SPRING_DELETE_MAPPING, new RequestMappingHoverProvider());
providers.put(Annotations.SPRING_PATCH_MAPPING, new RequestMappingHoverProvider());
providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, valueHoverProvider);
providers.put(Annotations.SPRING_REQUEST_MAPPING, requestMappingHoverProvider);
providers.put(Annotations.SPRING_GET_MAPPING, requestMappingHoverProvider);
providers.put(Annotations.SPRING_POST_MAPPING, requestMappingHoverProvider);
providers.put(Annotations.SPRING_PUT_MAPPING, requestMappingHoverProvider);
providers.put(Annotations.SPRING_DELETE_MAPPING, requestMappingHoverProvider);
providers.put(Annotations.SPRING_PATCH_MAPPING, requestMappingHoverProvider);
providers.put(Annotations.PROFILE, new ActiveProfilesProvider());
providers.put(Annotations.AUTOWIRED, new AutowiredHoverProvider(this));
providers.put(Annotations.INJECT, new AutowiredHoverProvider(this));
providers.put(Annotations.COMPONENT, new ComponentInjectionsHoverProvider(this));
providers.put(Annotations.BEAN, new BeanInjectedIntoHoverProvider(this));
providers.put(Annotations.AUTOWIRED, autowiredHoverProvider);
providers.put(Annotations.INJECT, autowiredHoverProvider);
providers.put(Annotations.COMPONENT, componentInjectionsHoverProvider);
providers.put(Annotations.BEAN, beanInjectedIntoHoverProvider);
providers.put(Annotations.CONDITIONAL, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_BEAN, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_MISSING_BEAN, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_PROPERTY, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_RESOURCE, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_CLASS, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_MISSING_CLASS, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_CLOUD_PLATFORM, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_WEB_APPLICATION, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_NOT_WEB_APPLICATION, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_INFO_CONTRIBUTOR, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_RESOURCE_CHAIN, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_ENDPOINT, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_HEALTH_INDICATOR, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_EXPRESSION, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_JAVA, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_JNDI, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, new ConditionalsLiveHoverProvider());
providers.put(Annotations.CONDITIONAL, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_BEAN, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_MISSING_BEAN, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_PROPERTY, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_RESOURCE, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_CLASS, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_MISSING_CLASS, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_CLOUD_PLATFORM, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_WEB_APPLICATION, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_NOT_WEB_APPLICATION, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_ENABLED_INFO_CONTRIBUTOR, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_ENABLED_RESOURCE_CHAIN, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_ENABLED_ENDPOINT, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_ENABLED_HEALTH_INDICATOR, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_EXPRESSION, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_JAVA, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_JNDI, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, conditionalsLiveHoverProvider);
return new BootJavaHoverProvider(this, javaProjectFinder, providers, runningAppProvider);
}
protected SpringIndexer createAnnotationIndexer(SimpleLanguageServer server, BootLanguageServerParams params) {
AnnotationHierarchyAwareLookup<SymbolProvider> providers = new AnnotationHierarchyAwareLookup<>();
providers.put(Annotations.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider());
providers.put(Annotations.SPRING_GET_MAPPING, new RequestMappingSymbolProvider());
providers.put(Annotations.SPRING_POST_MAPPING, new RequestMappingSymbolProvider());
providers.put(Annotations.SPRING_PUT_MAPPING, new RequestMappingSymbolProvider());
providers.put(Annotations.SPRING_DELETE_MAPPING, new RequestMappingSymbolProvider());
providers.put(Annotations.SPRING_PATCH_MAPPING, new RequestMappingSymbolProvider());
RequestMappingSymbolProvider requestMappingSymbolProvider = new RequestMappingSymbolProvider();
BeansSymbolProvider beansSymbolProvider = new BeansSymbolProvider();
ComponentSymbolProvider componentSymbolProvider = new ComponentSymbolProvider();
RestrictedDefaultSymbolProvider restrictedDefaultSymbolProvider = new RestrictedDefaultSymbolProvider();
DataRepositorySymbolProvider dataRepositorySymbolProvider = new DataRepositorySymbolProvider();
WebfluxRouterSymbolProvider webfluxRouterSymbolProvider = new WebfluxRouterSymbolProvider();
providers.put(Annotations.BEAN, new BeansSymbolProvider());
providers.put(Annotations.COMPONENT, new ComponentSymbolProvider());
providers.put(Annotations.SPRING_REQUEST_MAPPING, requestMappingSymbolProvider);
providers.put(Annotations.SPRING_GET_MAPPING, requestMappingSymbolProvider);
providers.put(Annotations.SPRING_POST_MAPPING, requestMappingSymbolProvider);
providers.put(Annotations.SPRING_PUT_MAPPING, requestMappingSymbolProvider);
providers.put(Annotations.SPRING_DELETE_MAPPING, requestMappingSymbolProvider);
providers.put(Annotations.SPRING_PATCH_MAPPING, requestMappingSymbolProvider);
providers.put(Annotations.PROFILE, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.BEAN, beansSymbolProvider);
providers.put(Annotations.COMPONENT, componentSymbolProvider);
providers.put(Annotations.CONDITIONAL, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_BEAN, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_MISSING_BEAN, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_PROPERTY, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_RESOURCE, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_CLASS, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_MISSING_CLASS, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_CLOUD_PLATFORM, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_WEB_APPLICATION, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_NOT_WEB_APPLICATION, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_INFO_CONTRIBUTOR, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_RESOURCE_CHAIN, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_ENDPOINT, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_HEALTH_INDICATOR, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_EXPRESSION, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_JAVA, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_JNDI, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.PROFILE, restrictedDefaultSymbolProvider);
providers.put(Annotations.REPOSITORY, new DataRepositorySymbolProvider());
providers.put("", new WebfluxRouterSymbolProvider());
providers.put(Annotations.CONDITIONAL, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_BEAN, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_MISSING_BEAN, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_PROPERTY, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_RESOURCE, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_CLASS, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_MISSING_CLASS, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_CLOUD_PLATFORM, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_WEB_APPLICATION, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_NOT_WEB_APPLICATION, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_ENABLED_INFO_CONTRIBUTOR, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_ENABLED_RESOURCE_CHAIN, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_ENABLED_ENDPOINT, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_ENABLED_HEALTH_INDICATOR, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_EXPRESSION, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_JAVA, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_JNDI, restrictedDefaultSymbolProvider);
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, restrictedDefaultSymbolProvider);
providers.put(Annotations.REPOSITORY, dataRepositorySymbolProvider);
providers.put("", webfluxRouterSymbolProvider);
return new SpringIndexer(server, params, providers);
}

View File

@@ -22,8 +22,11 @@ import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -65,7 +68,7 @@ public class AutowiredHoverProvider implements HoverProvider {
}
@Override
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
LiveBean definedBean = getDefinedBeanForTypeDeclaration(ASTUtils.findDeclaringType(annotation));
// Annotation is MarkerNode, parent is some field, method, variable declaration node.
@@ -80,13 +83,32 @@ public class AutowiredHoverProvider implements HoverProvider {
return null;
}
private Collection<Range> getLiveHoverHints(IJavaProject project, ASTNode declarationNode, Range range,
private Collection<CodeLens> getLiveHoverHints(IJavaProject project, ASTNode declarationNode, Range range,
SpringBootApp[] runningApps, LiveBean definedBean) {
if (declarationNode != null && definedBean != null) {
for (SpringBootApp app : runningApps) {
List<LiveBean> relevantBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean);
if (!relevantBeans.isEmpty()) {
return ImmutableList.of(range);
CodeLens codeLens = new CodeLens();
codeLens.setRange(range);
StringBuilder sb = new StringBuilder("\u21D0 ");
if (LiveHoverUtils.doBeansFitInline(relevantBeans, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR)) {
sb.append(relevantBeans.stream().map(LiveHoverUtils::getShortDisplayType).collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR)));
} else {
sb.append(relevantBeans.size());
sb.append(" bean");
if (relevantBeans.size() > 1) {
sb.append("s");
}
}
codeLens.setData(sb.toString());
Command cmd = new Command();
cmd.setTitle(sb.toString());
cmd.setCommand("org.springframework.showHoverAtPosition");
cmd.setArguments(ImmutableList.of(range.getStart()));
codeLens.setCommand(cmd);
return ImmutableList.of(codeLens);
}
}
}
@@ -100,7 +122,15 @@ public class AutowiredHoverProvider implements HoverProvider {
LiveBean definedBean = getDefinedBeanForTypeDeclaration(ASTUtils.findDeclaringType(annotation));
// Annotation is MarkerNode, parent is some field, method, variable declaration node.
ASTNode declarationNode = annotation.getParent();
return provideHover(definedBean, declarationNode, offset, doc, project, runningApps);
Hover hover = provideHover(definedBean, declarationNode, offset, doc, project, runningApps);
if (hover != null) {
try {
hover.setRange(doc.toRange(annotation.getStartPosition(), annotation.getLength()));
} catch (BadLocationException e) {
log.error("", e);
}
}
return hover;
}
return null;
}
@@ -266,11 +296,20 @@ public class AutowiredHoverProvider implements HoverProvider {
@Override
public Hover provideHover(MethodDeclaration methodDeclaration, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(methodDeclaration);
return provideHover(definedBean, methodDeclaration, offset, doc, project, runningApps);
Hover hover = provideHover(definedBean, methodDeclaration, offset, doc, project, runningApps);
if (hover != null) {
SimpleName name = methodDeclaration.getName();
try {
hover.setRange(doc.toRange(name.getStartPosition(), name.getLength()));
} catch (BadLocationException e) {
log.error("", e);
}
}
return hover;
}
@Override
public Collection<Range> getLiveHoverHints(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc,
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc,
SpringBootApp[] runningApps) {
LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(methodDeclaration);
try {

View File

@@ -21,16 +21,18 @@ import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.MarkedString;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.LiveConditional;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
@@ -42,6 +44,8 @@ import com.google.common.collect.ImmutableList;
*/
public class ConditionalsLiveHoverProvider implements HoverProvider {
private static final Logger log = LoggerFactory.getLogger(ConditionalsLiveHoverProvider.class);
@Override
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
@@ -49,15 +53,15 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
}
@Override
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
try {
Optional<List<LiveConditional>> val = getMatchedLiveConditionals(annotation, runningApps);
if (val.isPresent()) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
return ImmutableList.of(hoverRange);
return ImmutableList.of(new CodeLens(hoverRange));
}
} catch (Exception e) {
Log.log(e);
log.error("", e);
}
return null;
@@ -103,7 +107,7 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
return hover;
} catch (Exception e) {
Log.log(e);
log.error("", e);
}
return null;

View File

@@ -25,8 +25,8 @@ import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -90,9 +90,9 @@ public class BootJavaHoverProvider implements HoverHandler {
return SimpleTextDocumentService.NO_HOVER;
}
public Range[] getLiveHoverHints(final TextDocument document, final SpringBootApp[] runningBootApps) {
public CodeLens[] getLiveHoverHints(final TextDocument document, final SpringBootApp[] runningBootApps) {
return server.getCompilationUnitCache().withCompilationUnit(document, cu -> {
Collection<Range> result = new HashSet<>();
Collection<CodeLens> result = new HashSet<>();
try {
if (cu != null) {
cu.accept(new ASTVisitor() {
@@ -158,18 +158,18 @@ public class BootJavaHoverProvider implements HoverHandler {
} catch (Exception e) {
logger.error("error extracting live hint information for docURI '" + document.getUri(), e);
}
return result.toArray(new Range[result.size()]);
return result.toArray(new CodeLens[result.size()]);
});
}
protected void extractLiveHintsForMethod(MethodDeclaration methodDeclaration, TextDocument doc,
SpringBootApp[] runningApps, Collection<Range> result) {
SpringBootApp[] runningApps, Collection<CodeLens> result) {
Collection<HoverProvider> providers = this.hoverProviders.getAll();
if (!providers.isEmpty()) {
for (HoverProvider provider : providers) {
getProject(doc).ifPresent(project -> {
if (hasActuatorDependency(project)) {
Collection<Range> hints = provider.getLiveHoverHints(project, methodDeclaration, doc, runningApps);
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, methodDeclaration, doc, runningApps);
if (hints!=null) {
result.addAll(hints);
}
@@ -182,13 +182,13 @@ public class BootJavaHoverProvider implements HoverHandler {
}
}
protected void extractLiveHintsForType(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps, Collection<Range> result) {
protected void extractLiveHintsForType(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps, Collection<CodeLens> result) {
Collection<HoverProvider> providers = this.hoverProviders.getAll();
if (!providers.isEmpty()) {
for (HoverProvider provider : providers) {
getProject(doc).ifPresent(project -> {
if (hasActuatorDependency(project)) {
Collection<Range> hints = provider.getLiveHoverHints(project, typeDeclaration, doc, runningApps);
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, typeDeclaration, doc, runningApps);
if (hints!=null) {
result.addAll(hints);
}
@@ -201,14 +201,14 @@ public class BootJavaHoverProvider implements HoverHandler {
}
}
protected void extractLiveHintsForAnnotation(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps, Collection<Range> result) {
protected void extractLiveHintsForAnnotation(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps, Collection<CodeLens> result) {
ITypeBinding type = annotation.resolveTypeBinding();
if (type != null) {
if (runningApps.length > 0) {
for (HoverProvider provider : this.hoverProviders.get(type)) {
getProject(doc).ifPresent(project -> {
if (hasActuatorDependency(project)) {
Collection<Range> hints = provider.getLiveHoverHints(project, annotation, doc, runningApps);
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, annotation, doc, runningApps);
if (hints!=null) {
result.addAll(hints);
}

View File

@@ -17,8 +17,8 @@ import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -38,14 +38,16 @@ public interface HoverProvider {
return null;
}
default Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
return null;
}
default Collection<Range> getLiveHoverHints(IJavaProject project,TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
return null;
}
default Collection<Range> getLiveHoverHints(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
return null;
}
}
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project,TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
return null;
}
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
return null;
}
}

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -20,6 +21,8 @@ import java.util.stream.Stream;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -50,7 +53,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
}
@Override
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
// Highlight if any running app contains an instance of this component
try {
if (runningApps.length > 0) {
@@ -59,7 +62,8 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
Optional<Range> nameRange = ASTUtils.nameRange(doc, annotation);
if (nameRange.isPresent()) {
return ImmutableList.of(nameRange.get());
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get());
return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses;
}
}
}
@@ -71,18 +75,63 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
}
@Override
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc,
IJavaProject project, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
LiveBean definedBean = getDefinedBean(annotation);
if (definedBean != null) {
return assembleHover(project, runningApps, definedBean);
Hover hover = assembleHover(project, runningApps, definedBean);
if (hover != null) {
Optional<Range> nameRange = ASTUtils.nameRange(doc, annotation);
if (nameRange.isPresent()) {
hover.setRange(nameRange.get());
}
}
return hover;
}
}
return null;
}
protected List<CodeLens> assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, Range range) {
List<CodeLens> codeLensList = new ArrayList<>();
for (SpringBootApp app : runningApps) {
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
if (!relevantBeans.isEmpty()) {
List<LiveBean> injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans);
if (!injectedBeans.isEmpty()) {
CodeLens codeLens = new CodeLens();
codeLens.setRange(range);
StringBuilder sb = new StringBuilder("\u21D2 ");
if (LiveHoverUtils.doBeansFitInline(relevantBeans, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR)) {
sb.append(relevantBeans.stream().map(LiveHoverUtils::getShortDisplayType).collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR)));
} else {
sb.append(relevantBeans.size());
sb.append(" bean");
if (relevantBeans.size() > 1) {
sb.append("s");
}
}
codeLens.setData(sb.toString());
Command cmd = new Command();
cmd.setTitle(sb.toString());
cmd.setCommand("org.springframework.showHoverAtPosition");
cmd.setArguments(ImmutableList.of(range.getStart()));
codeLens.setCommand(cmd);
codeLensList.add(codeLens);
// Break out of the loop. Just look for the first app with injected into beans
break;
}
}
}
return codeLensList;
}
protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean) {
StringBuilder hover = new StringBuilder();

View File

@@ -22,14 +22,16 @@ import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
@@ -41,6 +43,8 @@ import com.google.common.collect.ImmutableSet;
*/
public class ActiveProfilesProvider implements HoverProvider {
private static final Logger log = LoggerFactory.getLogger(ActiveProfilesProvider.class);
@Override
public Hover provideHover(
ASTNode node,
@@ -71,19 +75,26 @@ public class ActiveProfilesProvider implements HoverProvider {
}
}
if (hasInterestingApp) {
return new Hover(
Hover hover = new Hover(
ImmutableList.of(Either.forLeft(markdown.toString()))
);
if (hover != null) {
Optional<Range> optional = nameRange(doc, annotation);
if (optional.isPresent()) {
hover.setRange(optional.get());
}
}
return hover;
}
}
return null;
}
@Override
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
Builder<Range> ranges = ImmutableList.builder();
nameRange(doc, annotation).ifPresent(ranges::add);
Builder<CodeLens> codeLenses = ImmutableList.builder();
nameRange(doc, annotation).map(CodeLens::new).ifPresent(codeLenses::add);
Set<String> allActiveProfiles = getAllActiveProfiles(runningApps);
annotation.accept(new ASTVisitor() {
@@ -91,12 +102,12 @@ public class ActiveProfilesProvider implements HoverProvider {
public boolean visit(StringLiteral node) {
String value = ASTUtils.getLiteralValue(node);
if (value!=null && allActiveProfiles.contains(value)) {
rangeOf(doc, node).ifPresent(ranges::add);
rangeOf(doc, node).map(CodeLens::new).ifPresent(codeLenses::add);
}
return true;
}
});
return ranges.build();
return codeLenses.build();
}
return ImmutableList.of();
}
@@ -124,7 +135,7 @@ public class ActiveProfilesProvider implements HoverProvider {
}
return Optional.of(doc.toRange(start, end-start));
} catch (Exception e) {
Log.log(e);
log.error("", e);
return Optional.empty();
}
}

View File

@@ -19,7 +19,9 @@ import java.util.stream.Stream;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.slf4j.Logger;
@@ -31,6 +33,7 @@ import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -98,7 +101,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
}
@Override
public Collection<Range> getLiveHoverHints(IJavaProject project, TypeDeclaration typeDeclaration, TextDocument doc,
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, TypeDeclaration typeDeclaration, TextDocument doc,
SpringBootApp[] runningApps) {
if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) {
try {
@@ -107,7 +110,8 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
Optional<Range> nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange());
if (nameRange.isPresent()) {
return ImmutableList.of(nameRange.get());
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get());
return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses;
}
}
}
@@ -126,7 +130,16 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null);
if (definedBean != null) {
return assembleHover(project, runningApps, definedBean);
Hover hover = assembleHover(project, runningApps, definedBean);
if (hover != null) {
SimpleName name = typeDeclaration.getName();
try {
hover.setRange(doc.toRange(name.getStartPosition(), name.getLength()));
} catch (BadLocationException e) {
LOG.error("", e);
}
}
return hover;
}
}
return null;

View File

@@ -69,6 +69,12 @@ public class LiveHoverUtils {
return buf.toString();
}
public static String getShortDisplayType(LiveBean bean) {
String type = bean.getType(true);
int idx = type.lastIndexOf('.');
return idx < 0 || idx == type.length() - 1 ? type : type.substring(idx + 1);
}
public static String showBeanInline(BootJavaLanguageServerComponents server, IJavaProject project, LiveBean bean) {
String id = bean.getId();
String type = bean.getType(true);

View File

@@ -22,6 +22,7 @@ import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.MarkedString;
import org.eclipse.lsp4j.Range;
@@ -57,18 +58,18 @@ public class RequestMappingHoverProvider implements HoverProvider {
}
@Override
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
try {
if (runningApps.length > 0) {
List<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (!val.isEmpty()) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
return ImmutableList.of(hoverRange);
return ImmutableList.of(new CodeLens(hoverRange));
}
}
}
catch (BadLocationException e) {
log.error("", e);;
log.error("", e);
}
return null;
@@ -83,17 +84,19 @@ public class RequestMappingHoverProvider implements HoverProvider {
if (!val.isEmpty()) {
addHoverContent(val, hoverContent);
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
Hover hover = new Hover();
hover.setContents(hoverContent);
hover.setRange(hoverRange);
return hover;
} else {
return null;
}
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
Hover hover = new Hover();
hover.setContents(hoverContent);
hover.setRange(hoverRange);
return hover;
} catch (Exception e) {
log.error("", e);;
log.error("", e);
}
return null;
@@ -114,7 +117,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
}
}
} catch (Exception e) {
log.error("", e);;
log.error("", e);
}
return results;
}

View File

@@ -19,7 +19,7 @@ import java.util.TimerTask;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.stream.Stream;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.slf4j.Logger;
@@ -156,8 +156,8 @@ public class SpringLiveHoverWatchdog {
if (hasCurrentRunningBootApps) {
TextDocument doc = this.server.getTextDocumentService().get(docURI);
if (doc != null) {
Range[] ranges = this.hoverProvider.getLiveHoverHints(doc, runningBootApps);
publishLiveHints(docURI, ranges);
CodeLens[] infos = this.hoverProvider.getLiveHoverHints(doc, runningBootApps);
publishLiveHints(docURI, infos);
}
}
else {
@@ -195,15 +195,15 @@ public class SpringLiveHoverWatchdog {
}
}
private void publishLiveHints(String docURI, Range[] ranges) {
private void publishLiveHints(String docURI, CodeLens[] codeLenses) {
int version = server.getTextDocumentService().get(docURI).getVersion();
VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(version);
id.setUri(docURI);
server.getClient().highlight(new HighlightParams(id, Arrays.asList(ranges)));
server.getClient().highlight(new HighlightParams(id, Arrays.asList(codeLenses)));
}
private void cleanupLiveHints(String docURI) {
publishLiveHints(docURI, new Range[0]);
publishLiveHints(docURI, new CodeLens[0]);
}
private void cleanupResources() {

View File

@@ -1,6 +1,6 @@
import { injectable, inject } from 'inversify';
import { NotificationType } from 'vscode-jsonrpc';
import { TextDocumentIdentifier, Range } from 'vscode-base-languageclient/lib/base';
import { TextDocumentIdentifier, Range, CodeLens } from 'vscode-base-languageclient/lib/base';
import { SetDecorationParams, EditorDecorationStyle, TextEditor, DeltaDecorationParams, EditorManager } from '@theia/editor/lib/browser';
import { ILanguageClient } from '@theia/languages/lib/common';
import { DiffUris } from '@theia/core/lib/browser/diff-uris';
@@ -11,17 +11,11 @@ const HIGHLIGHTS_NOTIFICATION_TYPE = new NotificationType<HighlightParams,void>(
const BOOT_LIVE_HINTS = 'Boot-Live-Hints';
const INLINE_BOOT_HINT_DECORATION_STYLE = new EditorDecorationStyle('inline-boot-hint-decoration', style => {
style.borderStyle = 'dotted';
style.borderColor = '#32BA56';
style.borderWidth = '1px';
});
const LINE_BOOT_HINT_DECORATION_STYLE = new EditorDecorationStyle('line-boot-hint-decoration', style => {
style.backgroundImage = 'url(../../images/boot-icon.png)';
style.display = 'block';
style.width = '10px';
style.height = '1em';
style.margin = '0 2px 0 0';
style.backgroundColor = 'rgba(109,179,63,0.25)',
style.borderColor = 'rgba(109,179,63,0.25)',
style.borderSpacing = '4px',
style.borderRadius = '4px',
style.borderWidth = '4px'
});
@injectable()
@@ -43,13 +37,11 @@ export class HighlightService {
const decorationParams: SetDecorationParams = {
uri: params.doc.uri,
kind: BOOT_LIVE_HINTS,
newDecorations: params.ranges.map(r => {
newDecorations: params.codeLenses.map(cl => {
return {
range: Range.create(r.start.line, r.start.character, r.end.line, r.end.character),
range: Range.create(cl.range.start.line, cl.range.start.character, cl.range.end.line, cl.range.end.character),
options: {
inlineClassName: INLINE_BOOT_HINT_DECORATION_STYLE.className,
glyphMarginClassName: LINE_BOOT_HINT_DECORATION_STYLE.className,
hoverMessage: 'Ho-ho, Boot Hint!',
isWholeLine: false
}
}
@@ -80,6 +72,6 @@ export class HighlightService {
export interface HighlightParams {
doc: TextDocumentIdentifier
ranges: Range[]
codeLenses: CodeLens[]
}

View File

@@ -25,7 +25,8 @@
},
"dependencies": {
"@pivotal-tools/jvm-launch-utils": "0.0.11",
"portfinder": "^0.4.0"
"portfinder": "^0.4.0",
"deep-equal": "^1.0.1"
},
"devDependencies": {
"typescript": "2.6.1",

View File

@@ -0,0 +1,50 @@
import * as deepEqual from 'deep-equal';
import {
CancellationToken,
CodeLens,
CodeLensProvider,
Event,
EventEmitter,
ProviderResult,
TextDocument
} from "vscode";
import {HighlightParams, toVSRange} from './highlight-service';
import * as Lsp from 'vscode-languageclient';
export class HighlightCodeLensProvider implements CodeLensProvider {
private highlights : Map<string, HighlightParams> = new Map<string, HighlightParams>();
private _onDidChangeCodeLenses = new EventEmitter<void>();
public get onDidChangeCodeLenses(): Event<void> {
return this._onDidChangeCodeLenses.event;
}
handle(highlghtParams: HighlightParams) {
if (!deepEqual(this.highlights.get(highlghtParams.doc.uri), highlghtParams)) {
this.highlights.set(highlghtParams.doc.uri, highlghtParams);
this._onDidChangeCodeLenses.fire();
}
}
static toVSCodeLens(cl: Lsp.CodeLens): CodeLens {
const codeLens: CodeLens = {
range: toVSRange(cl.range),
isResolved: true,
command: cl.command
};
return codeLens;
}
provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult<CodeLens[]> {
const activeUri = document.uri.toString();
const activeVersion = document.version;
const highlightParams = this.highlights.get(activeUri);
if (highlightParams && highlightParams.doc.version === activeVersion) {
const codeLenses = highlightParams.codeLenses || [];
return codeLenses.filter(cl => cl.command).map(cl => HighlightCodeLensProvider.toVSCodeLens(cl));
}
return [];
};
}

View File

@@ -1,8 +1,7 @@
import {VersionedTextDocumentIdentifier, Position, Range} from 'vscode-languageclient'
import {VersionedTextDocumentIdentifier, MarkupContent, Position, Range, CodeLens} from 'vscode-languageclient'
import * as VSCode from 'vscode';
import * as path from "path";
function toDecoration(rng : Range) : VSCode.Range {
export function toVSRange(rng : Range) : VSCode.Range {
return new VSCode.Range(toPosition(rng.start), toPosition(rng.end));
}
@@ -11,15 +10,15 @@ function toPosition(p : Position) : VSCode.Position {
}
export interface HighlightParams {
doc: VersionedTextDocumentIdentifier
ranges: Range[]
doc: VersionedTextDocumentIdentifier;
codeLenses: CodeLens[];
}
export class HighlightService {
DECORATION : VSCode.TextEditorDecorationType;
highlights : Map<String, Range[]>;
highlights : Map<String, HighlightParams>;
dispose() {
this.DECORATION.dispose();
@@ -41,7 +40,7 @@ export class HighlightService {
}
handle(params : HighlightParams) : void {
this.highlights.set(params.doc.uri, params.ranges);
this.highlights.set(params.doc.uri, params);
this.refresh(params.doc);
}
@@ -52,10 +51,11 @@ export class HighlightService {
const activeVersion = editor.document.version;
if (docId.uri === activeUri && docId.version === activeVersion) {
//We only update highlights in the active editor for now
let highlights : Range[] = this.highlights.get(docId.uri) || [];
let decorations = highlights.map(hl => toDecoration(hl));
const highlightParams: HighlightParams = this.highlights.get(docId.uri);
const highlights: CodeLens[] = highlightParams.codeLenses || [];
let decorations = highlights.map(hl => toVSRange(hl.range));
editor.setDecorations(this.DECORATION, decorations);
}
}
}
}
}

View File

@@ -6,17 +6,17 @@ import * as FS from 'fs';
import PortFinder = require('portfinder');
import * as Net from 'net';
import * as ChildProcess from 'child_process';
import { TextDocumentIdentifier, RequestType, LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo, Range } from 'vscode-languageclient';
import { TextDocument, OutputChannel, Disposable, window } from 'vscode';
import { TextDocumentIdentifier, RequestType, LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo, Position } from 'vscode-languageclient';
import {TextDocument, OutputChannel, Disposable, window, Event, EventEmitter} from 'vscode';
import { Trace, NotificationType } from 'vscode-jsonrpc';
import * as P2C from 'vscode-languageclient/lib/protocolConverter';
import {WorkspaceEdit, Position} from 'vscode-languageserver-types';
import {HighlightService, HighlightParams} from './highlight-service';
import { log } from 'util';
import { tmpdir } from 'os';
import { JVM, findJvm, findJdk } from '@pivotal-tools/jvm-launch-utils';
import { registerClasspathService } from './classpath';
import { registerJavadocService } from './javadoc';
import {HighlightCodeLensProvider} from "./code-lens-service";
let p2c = P2C.createConverter();
@@ -34,6 +34,7 @@ export interface ActivatorOptions {
workspaceOptions: VSCode.WorkspaceConfiguration;
checkjvm?: (context: VSCode.ExtensionContext, jvm: JVM) => any;
preferJdk?: boolean;
highlightCodeLensSettingKey?: string;
}
type JavaOptions = {
@@ -203,18 +204,52 @@ function setupLanguageClient(context: VSCode.ExtensionContext, createServer: Ser
let disposable = client.start();
const codeLensListanableSetting = options.highlightCodeLensSettingKey ? new ListenablePreferenceSetting<boolean>(options.highlightCodeLensSettingKey) : undefined;
let progressService = new ProgressService();
let highlightService = new HighlightService();
const codelensService = new HighlightCodeLensProvider();
let codeLensProviderSubscription: Disposable;
VSCode.commands.registerCommand('org.springframework.showHoverAtPosition', (position: Position) => {
const editor = VSCode.window.activeTextEditor;
const vsPosition = new VSCode.Position(position.line, position.character);
editor.selection = new VSCode.Selection(vsPosition, vsPosition);
VSCode.commands.executeCommand('editor.action.showHover');
});
context.subscriptions.push(disposable);
context.subscriptions.push(progressService);
context.subscriptions.push(highlightService);
function toggleHighlightCodeLens() {
if (!codeLensProviderSubscription && codeLensListanableSetting.value) {
codeLensProviderSubscription = VSCode.languages.registerCodeLensProvider(options.clientOptions.documentSelector, codelensService);
context.subscriptions.push(codeLensProviderSubscription);
} else if (codeLensProviderSubscription) {
codeLensProviderSubscription.dispose();
const idx = context.subscriptions.indexOf(codeLensProviderSubscription);
if (idx >= 0) {
context.subscriptions.splice(idx, 1);
}
codeLensProviderSubscription = null;
}
}
if (codeLensListanableSetting) {
toggleHighlightCodeLens();
codeLensListanableSetting.onDidChangeValue(() => toggleHighlightCodeLens())
}
return client.onReady().then(() => {
client.onNotification(progressNotification, (params: ProgressParams) => {
progressService.handle(params);
});
client.onNotification(highlightNotification, (params: HighlightParams) => {
highlightService.handle(params);
if (codeLensListanableSetting && codeLensListanableSetting.value) {
codelensService.handle(params);
}
});
client.onRequest(moveCursorRequest, (params: MoveCursorParams) => {
let editors = VSCode.window.visibleTextEditors;
@@ -278,3 +313,31 @@ class ProgressService {
this.status = null;
}
}
export interface ListenableSetting<T> {
value: T;
onDidChangeValue: VSCode.Event<void>
}
export class ListenablePreferenceSetting<T> implements ListenableSetting<T> {
private _onDidChangeValue = new EventEmitter<void>();
constructor(private section: string) {
VSCode.workspace.onDidChangeConfiguration(e => {
console.log('Settings changed! value = ' + this.value);
if (e.affectsConfiguration(this.section)) {
this._onDidChangeValue.fire();
}
});
}
get value(): T {
return VSCode.workspace.getConfiguration().get(this.section);
}
get onDidChangeValue(): Event<void> {
return this._onDidChangeValue.event;
}
}

View File

@@ -1,23 +1,19 @@
'use strict';
import * as VSCode from 'vscode';
import * as Path from 'path';
import * as FS from 'fs';
import * as Net from 'net';
import * as ChildProcess from 'child_process';
import {LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo} from 'vscode-languageclient';
import { workspace, TextDocument } from 'vscode';
import {workspace} from 'vscode';
import * as commons from '@pivotal-tools/commons-vscode';
import {generate_pipeline, UserQuestioner} from '@pivotal-tools/pipeline-builder';
import {LanguageClient} from "vscode-languageclient";
const PROPERTIES_LANGUAGE_ID = "spring-boot-properties";
const YAML_LANGUAGE_ID = "spring-boot-properties-yaml";
const JAVA_LANGUAGE_ID = "java";
/** Called when extension is activated */
export function activate(context: VSCode.ExtensionContext) {
export function activate(context: VSCode.ExtensionContext): Thenable<LanguageClient> {
// registerPipelineGenerator(context);
@@ -53,7 +49,8 @@ export function activate(context: VSCode.ExtensionContext) {
initializationOptions: {
workspaceFolders: workspace.workspaceFolders ? workspace.workspaceFolders.map(f => f.uri.toString()) : null
}
}
},
highlightCodeLensSettingKey: 'boot-java.highlight-codelens.on'
};
return commons.activate(options, context);

View File

@@ -68,6 +68,11 @@
"default": true,
"description": "Enable/Disable Spring running Boot application live hints decorators in Java source code"
},
"boot-java.highlight-codelens.on": {
"type": "boolean",
"default": true,
"description": "Enable/Disable Spring running Boot application Code Lenses"
},
"boot-java.change-detection.on": {
"type": "boolean",
"default": false,