diff --git a/atom-extensions/atom-spring-boot/styles/hints.atom-text-editor.less b/atom-extensions/atom-spring-boot/styles/hints.atom-text-editor.less index 2ef68d7c5..3a4ace587 100644 --- a/atom-extensions/atom-spring-boot/styles/hints.atom-text-editor.less +++ b/atom-extensions/atom-spring-boot/styles/hints.atom-text-editor.less @@ -1,7 +1,11 @@ .boot-hint .region { - border-color: #32BA56; - border-style: dotted; - border-width: 1px; + background-color: rgba(109,179,63,0.25); + border-color: rgba(109,179,63,0.25); + border-radius: 4px; + border-spacing: 4px; + //border-color: #32BA56; + //border-style: dotted; + //border-width: 1px; } atom-text-editor.editor { diff --git a/eclipse-distribution/common/html/nightly-distributions.html b/eclipse-distribution/common/html/nightly-distributions.html index 9a145dc4f..3855e7c18 100644 --- a/eclipse-distribution/common/html/nightly-distributions.html +++ b/eclipse-distribution/common/html/nightly-distributions.html @@ -23,7 +23,8 @@

STS4 Distribution:

@@ -48,10 +49,10 @@

Eclipse-based Distribution Builds

-

Spring Tool Suite 4 - based on Eclipse Photon Milestone Builds (4.8.0 Mx)

-
+

Spring Tool Suite 4 - based on Eclipse 2018-09 Milestone Builds (4.9.0) - and beyond

+

Spring Tools 4 - Visual Studio Code Extensions

@@ -68,13 +69,6 @@ $('#atom').load('atom-packages/atom-packages-snippet.html'); -

No longer updated...

-

Spring Tool Suite 4 - based on Eclipse Oxygen.2 (4.7.2)

-
- - diff --git a/eclipse-distribution/org.springframework.boot.ide.product.e49/org.springframework.boot.ide.product b/eclipse-distribution/org.springframework.boot.ide.product.e49/org.springframework.boot.ide.product new file mode 100644 index 000000000..867949d75 --- /dev/null +++ b/eclipse-distribution/org.springframework.boot.ide.product.e49/org.springframework.boot.ide.product @@ -0,0 +1,109 @@ + + + + + + + + + -product org.springframework.boot.ide.branding.sts4 +--launcher.defaultAction +openFile + -Dosgi.requiredJavaVersion=1.8 +--add-modules=ALL-SYSTEM +-Xms40m + -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts -Xdock:icon=../Resources/sts4.icns + -Xmx1200m + + + -Xmx1200m + + + -Xmx1200m + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eclipse-distribution/org.springframework.boot.ide.product.e49/p2.inf b/eclipse-distribution/org.springframework.boot.ide.product.e49/p2.inf new file mode 100644 index 000000000..27e8d9201 --- /dev/null +++ b/eclipse-distribution/org.springframework.boot.ide.product.e49/p2.inf @@ -0,0 +1,23 @@ +instructions.configure=\ + org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:0,location:http${#58}//download.eclipse.org/releases/2018-09,name:2018-09);\ + org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:1,location:http${#58}//download.eclipse.org/releases/2018-09,name:2018-09);\ + org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:0,location:http${#58}//download.springsource.com/release/TOOLS/sts4/update/latest,name:Spring Tool Suite 4);\ + org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:1,location:http${#58}//download.springsource.com/release/TOOLS/sts4/update/latest,name:Spring Tool Suite 4);\ + org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:0,location:http${#58}//download.springsource.com/release/TOOLS/sts4-language-server-integrations,name:Spring Tools 4 Language Servers for Eclipse);\ + org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:1,location:http${#58}//download.springsource.com/release/TOOLS/sts4-language-server-integrations,name:Spring Tools 4 Language Servers for Eclipse); + +instructions.unconfigure=\ + org.eclipse.equinox.p2.touchpoint.eclipse.removeRepository(type:0,location:http${#58}//download.eclipse.org/releases/2018-09);\ + org.eclipse.equinox.p2.touchpoint.eclipse.removeRepository(type:1,location:http${#58}//download.eclipse.org/releases/2018-09);\ + org.eclipse.equinox.p2.touchpoint.eclipse.removeRepository(type:0,location:http${#58}//download.springsource.com/release/TOOLS/sts4/update/latest);\ + org.eclipse.equinox.p2.touchpoint.eclipse.removeRepository(type:1,location:http${#58}//download.springsource.com/release/TOOLS/sts4/update/latest);\ + org.eclipse.equinox.p2.touchpoint.eclipse.removeRepository(type:0,location:http${#58}//download.springsource.com/release/TOOLS/sts4-language-server-integrations);\ + org.eclipse.equinox.p2.touchpoint.eclipse.removeRepository(type:1,location:http${#58}//download.springsource.com/release/TOOLS/sts4-language-server-integrations); + + # Bug 530093: make sure we have latest version of a.jre.javase included with product + requires.3.namespace=org.eclipse.equinox.p2.iu + requires.3.name=a.jre.javase + requires.3.range=[9.0.0,9.0.0] + requires.4.namespace=org.eclipse.equinox.p2.iu + requires.4.name=config.a.jre.javase + requires.4.range=[9.0.0,9.0.0] diff --git a/eclipse-distribution/org.springframework.boot.ide.product.e49/pom.xml b/eclipse-distribution/org.springframework.boot.ide.product.e49/pom.xml new file mode 100644 index 000000000..d1effaa8e --- /dev/null +++ b/eclipse-distribution/org.springframework.boot.ide.product.e49/pom.xml @@ -0,0 +1,285 @@ + + + 4.0.0 + + + org.springframework.boot.ide + org.springframework.boot.ide + 4.0.0-SNAPSHOT + ../pom.xml + + + org.springframework.boot.ide.product + + eclipse-repository + + + ${accessKey} + ${secretKey} + + + + + + + org.apache.maven.plugins + maven-install-plugin + + + default-install + none + + + + + + org.eclipse.tycho + tycho-p2-repository-plugin + ${tycho-version} + + false + + + + + org.eclipse.tycho + tycho-p2-publisher-plugin + ${tycho-version} + + true + + + + + org.eclipse.tycho + target-platform-configuration + ${tycho-version} + + JavaSE-9 + + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + materialize-products + + materialize-products + + package + + + archive-products + + archive-products + + verify + + + + + + org.springframework.boot.ide.branding.sts4 + sts-${unqualifiedVersion}.${p2.qualifier} + + SpringToolSuite4.app + + spring-tool-suite-4-${unqualifiedVersion}.${p2.qualifier}-${dist.target} + + + + tar.gz + tar.gz + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + + osx-app-signing + package + + ${skip.osx.signing} + + + + + + + + + + + + + + + + + run + + + + + osx-dmg-creation + verify + + + + + + + + + + + + + + run + + + + + osx-dmg-signing + verify + + ${skip.osx.signing} + + + + + + + + + + + + + + + + + + + run + + + + + upload-product-bundles + deploy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + run + + + + + update-nightly-downloads + deploy + + ${skip.update-nightly-download-page} + + + + + + + + + + + + + + + + + + run + + + + + + + org.springframework.build + org.springframework.build.aws.ant + 3.1.0.RELEASE + + + net.java.dev.jets3t + jets3t + 0.8.1 + + + ant-contrib + ant-contrib + 20020829 + + + + + + + + diff --git a/eclipse-distribution/org.springframework.boot.ide.product.e49/spring-tool-suite-4-dmg-config-e4.9.json b/eclipse-distribution/org.springframework.boot.ide.product.e49/spring-tool-suite-4-dmg-config-e4.9.json new file mode 100644 index 000000000..c86b0023f --- /dev/null +++ b/eclipse-distribution/org.springframework.boot.ide.product.e49/spring-tool-suite-4-dmg-config-e4.9.json @@ -0,0 +1,10 @@ +{ + "title": "Spring Tool Suite 4", + "icon": "../org.springframework.boot.ide.branding/sts4.icns", + "contents": [ + { "x": 192, "y": 100, "type": "file", "path": "target/products/org.springframework.boot.ide.branding.sts4/macosx/cocoa/x86_64/SpringToolSuite4.app" }, + { "x": 448, "y": 100, "type": "link", "path": "/Applications" }, + { "x": 1000, "y": 2000, "type": "file", "path": "../org.springframework.boot.ide.branding/sts4.icns", "name": ".VolumeIcon.icns" } + ], + "format": "UDZO" +} diff --git a/eclipse-distribution/org.springframework.boot.ide.repository.e49/category.xml b/eclipse-distribution/org.springframework.boot.ide.repository.e49/category.xml new file mode 100644 index 000000000..b145883a2 --- /dev/null +++ b/eclipse-distribution/org.springframework.boot.ide.repository.e49/category.xml @@ -0,0 +1,206 @@ + + + + Spring Tool Suite 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eclipse-distribution/org.springframework.boot.ide.repository.e49/pom.xml b/eclipse-distribution/org.springframework.boot.ide.repository.e49/pom.xml new file mode 100644 index 000000000..5bf988898 --- /dev/null +++ b/eclipse-distribution/org.springframework.boot.ide.repository.e49/pom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + + + org.springframework.boot.ide + org.springframework.boot.ide + 4.0.0-SNAPSHOT + ../pom.xml + + + org.springframework.boot.ide.repository + + eclipse-repository + + + ${accessKey} + ${secretKey} + + + + + + + org.apache.maven.plugins + maven-install-plugin + + + default-install + none + + + + + + org.apache.maven.plugins + maven-install-plugin + + + default-install + none + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + + zip-sts-repository + install + + + + + + + + + + + + + + + + + + + + + + + run + + + + + upload-sts-repository + deploy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + run + + + + + + + org.springframework.build + org.springframework.build.aws.ant + 3.1.0.RELEASE + + + net.java.dev.jets3t + jets3t + 0.8.1 + + + ant-contrib + ant-contrib + 20020829 + + + + + + + + diff --git a/eclipse-distribution/pom.xml b/eclipse-distribution/pom.xml index 7cb661a53..962210bc8 100644 --- a/eclipse-distribution/pom.xml +++ b/eclipse-distribution/pom.xml @@ -294,6 +294,90 @@ + + e49 + + e4.9.0 + e4.9 + 2018-09 + 2018-09 (4.9.0) + 2018-09 + 4.9 + e49 + + + + photon + p2 + http://download.eclipse.org/releases/2018-09/ + + + staging + p2 + http://download.eclipse.org/staging/2018-09/ + + + orbit + p2 + http://download.eclipse.org/tools/orbit/downloads/drops/S20180710163057/repository + + + latest-m2e + p2 + http://download.eclipse.org/technology/m2e/releases/1.8 + + + maven-extras-mirror + p2 + http://download.springsource.com/release/TOOLS/third-party/m2e-sts310-signed/ + + + maven-egit + p2 + http://repo1.maven.org/maven2/.m2e/connectors/m2eclipse-egit/0.15.1/N/LATEST + + + maven-wro4j + p2 + http://download.jboss.org/jbosstools/updates/m2e-wro4j/ + + + maven-devtools + p2 + http://dist.springsource.com/release/TOOLS/mavendevtools/ + + + maven-dependency-support + p2 + http://ianbrandt.github.io/m2e-maven-dependency-plugin/ + + + ansi-console + p2 + http://www.mihai-nita.net/eclipse + + + xtext-base + p2 + http://download.eclipse.org/modeling/tmf/xtext/updates/milestones/ + + + lsp4e + p2 + http://download.eclipse.org/lsp4e/snapshots/ + + + + + org.springframework.boot.ide.product.e49 + org.springframework.boot.ide.repository.e49 + + + build.springsource.com diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/icons/boot.png b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/icons/boot.png new file mode 100644 index 000000000..4ca59b805 Binary files /dev/null and b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/icons/boot.png differ 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 69f72c3e0..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 @@ -2,28 +2,27 @@ - + + annotationType="org.springframework.tooling.bootinfo" + colorPreferenceKey="STS4BootMarkerIndicationColor" + colorPreferenceValue="219,236,207" + contributesToHeader="false" + highlightPreferenceKey="STS4BootMarkerHighlighting" + highlightPreferenceValue="true" + label="Boot Dynamic Info" + overviewRulerPreferenceKey="STS4BootMarkerIndicationInOverviewRuler" + overviewRulerPreferenceValue="true" + presentationLayer="4" + showInNextPrevDropdownToolbarAction="false" + textPreferenceKey="STS4BootMarkerIndication" + textPreferenceValue="true" + textStylePreferenceKey="STS4BootMarkerTextStyle" + verticalRulerPreferenceKey="STS4BootMarkerIndicationInVerticalRuler" + verticalRulerPreferenceValue="true"> 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,73 @@ 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); + int end = LSPEclipseUtils.toOffset(rng.getEnd(), doc); + Position colorPos = new Position(start, end - start); + 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) { diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBean.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBean.java index e21badb98..ead218549 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBean.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBean.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 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 @@ -103,9 +103,6 @@ public class LiveBean { type = type.substring(0, chop); } } - - // convert inner classes from $ to . notation - type = type.replace('$', '.'); } return type; diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index 85351652f..62a9c5dc2 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -600,18 +600,26 @@ public class Editor { } public void assertTrimmedHover(String hoverOver, String expectedHover) throws Exception { - int hoverPosition = getHoverPosition(hoverOver,1); + assertTrimmedHover(hoverOver, 1, expectedHover); + } + + public void assertTrimmedHover(String hoverOver, int occurence, String expectedHover) throws Exception { + int hoverPosition = getHoverPosition(hoverOver,occurence); Hover hover = harness.getHover(doc, doc.toPosition(hoverPosition)); assertEquals(expectedHover.trim(), hoverString(hover).trim()); } - public void assertNoHover(String hoverOver) throws Exception { - int hoverPosition = getRawText().indexOf(hoverOver) + hoverOver.length() / 2; + public void assertNoHover(String hoverOver, int occurence) throws Exception { + int hoverPosition = getHoverPosition(hoverOver,occurence); Hover hover = harness.getHover(doc, doc.toPosition(hoverPosition)); List> contents = hover.getContents().getLeft(); assertTrue(contents.toString(), contents.isEmpty()); } + public void assertNoHover(String hoverOver) throws Exception { + assertNoHover(hoverOver, 1); + } + /** * Verifies an expected textSnippet is contained in the hover text that is * computed when hovering mouse at position at the end of first occurrence of diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java index aedb86d9e..2e7550575 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java @@ -20,6 +20,7 @@ import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; 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.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; @@ -40,6 +41,7 @@ import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IType; +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; @@ -53,7 +55,7 @@ public class AutowiredHoverProvider implements HoverProvider { final static Logger log = LoggerFactory.getLogger(AutowiredHoverProvider.class); - private static final int MAX_INLINE_BEANS_STRING_LENGTH = 50; + private static final int MAX_INLINE_BEANS_STRING_LENGTH = 60; private static final String INLINE_BEANS_STRING_SEPARATOR = " "; private BootJavaLanguageServerComponents server; @@ -63,34 +65,31 @@ public class AutowiredHoverProvider implements HoverProvider { } @Override - public Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { - try { - LiveBean definedBean = getDefinedBean(annotation); - if (definedBean != null) { - for (SpringBootApp app : runningApps) { - try { - List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList()); + public Collection getLiveHoverHints(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. + ASTNode declarationNode = annotation.getParent(); + try { + Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); + return getLiveHoverHints(project, declarationNode, hoverRange, runningApps, definedBean); + } catch (BadLocationException e) { + log.error("", e); + } + } + return null; + } - if (!relevantBeans.isEmpty()) { - for (LiveBean bean : relevantBeans) { - String[] dependencies = bean.getDependencies(); - if (dependencies != null && dependencies.length > 0) { - Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); - return ImmutableList.of(hoverRange); - } - } - } - } - catch (Exception e) { - log.error("", e); - } + private Collection getLiveHoverHints(IJavaProject project, ASTNode declarationNode, Range range, + SpringBootApp[] runningApps, LiveBean definedBean) { + if (declarationNode != null && definedBean != null) { + for (SpringBootApp app : runningApps) { + List relevantBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean); + if (!relevantBeans.isEmpty()) { + return ImmutableList.of(range); } } } - catch (Exception e) { - log.error("", e); - } - return null; } @@ -98,74 +97,94 @@ public class AutowiredHoverProvider implements HoverProvider { public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { if (runningApps.length > 0) { + 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); + } + return null; + } + + private Hover provideHover(LiveBean definedBean, ASTNode declarationNode, int offset, TextDocument doc, + IJavaProject project, SpringBootApp[] runningApps) { + if (definedBean != null) { StringBuilder hover = new StringBuilder(); - LiveBean definedBean = getDefinedBean(annotation); - if (definedBean != null) { + boolean hasContent = false; - boolean hasContent = false; + for (SpringBootApp app : runningApps) { - for (SpringBootApp app : runningApps) { - LiveBeansModel beans = app.getBeans(); - List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList()); + List autowiredBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean); - if (!relevantBeans.isEmpty()) { - List allDependencyBeans = relevantBeans.stream() - .flatMap(b -> Arrays.stream(b.getDependencies())) - .distinct() - .flatMap(d -> beans.getBeansOfName(d).stream()) - .collect(Collectors.toList()); - - if (!allDependencyBeans.isEmpty()) { - - // parent is marker node, grandparent is some field, method, variable declaration node. - ASTNode declarationNode = node.getParent().getParent(); - List autowiredBeans = findAutowiredBeans(project, declarationNode, allDependencyBeans); - if (autowiredBeans.isEmpty()) { - // Show all relevant dependency beans - autowiredBeans = allDependencyBeans; - } - - if (!autowiredBeans.isEmpty()) { - if (!hasContent) { - hasContent = true; - } else { - hover.append(" \n \n"); - } - hover.append("**Autowired → "); - if (LiveHoverUtils.doBeansFitInline(autowiredBeans, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR)) { - hover.append(autowiredBeans.stream().map(b -> LiveHoverUtils.showBeanInline(server, project, b)).collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR))); - hover.append("**\n"); - } else { - hover.append(autowiredBeans.size()); - hover.append(" beans**\n"); - } + if (!autowiredBeans.isEmpty()) { + if (!hasContent) { + hasContent = true; + } else { + hover.append(" \n \n"); + } + hover.append("**Autowired `"); + hover.append(definedBean.getId()); + hover.append("` → "); + if (LiveHoverUtils.doBeansFitInline(autowiredBeans, MAX_INLINE_BEANS_STRING_LENGTH - definedBean.getId().length(), + INLINE_BEANS_STRING_SEPARATOR)) { + hover.append(autowiredBeans.stream().map(b -> LiveHoverUtils.showBeanInline(server, project, b)) + .collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR))); + hover.append("**\n"); + } else { + hover.append(autowiredBeans.size()); + hover.append(" bean"); + if (autowiredBeans.size() > 1) { + hover.append('s'); + } + hover.append("**\n"); + } // if (autowiredBeans.size() == 1) { // hover.append(LiveHoverUtils.showBeanIdAndTypeInline(server, project, autowiredBeans.get(0))); // } else { // hover.append(autowiredBeans.size()); // hover.append(" beans**\n"); // } - hover.append(autowiredBeans.stream() - .map(b -> "- " + LiveHoverUtils.showBeanWithResource(server, b, " ", project)) - .collect(Collectors.joining("\n")) - ); - hover.append("\n \n"); - hover.append(LiveHoverUtils.niceAppName(app)); - } - } - } + hover.append(autowiredBeans.stream() + .map(b -> "- " + LiveHoverUtils.showBeanWithResource(server, b, " ", project)) + .collect(Collectors.joining("\n"))); + hover.append("\n \n"); + hover.append(LiveHoverUtils.niceAppName(app)); + } - } - if (hasContent) { - return new Hover(ImmutableList.of(Either.forLeft(hover.toString()))); - } + } + if (hasContent) { + return new Hover(ImmutableList.of(Either.forLeft(hover.toString()))); } } return null; } + private List getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, LiveBean definedBean) { + LiveBeansModel beans = app.getBeans(); + List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); + + if (!relevantBeans.isEmpty()) { + List allDependencyBeans = relevantBeans.stream() + .flatMap(b -> Arrays.stream(b.getDependencies())).distinct() + .flatMap(d -> beans.getBeansOfName(d).stream()).collect(Collectors.toList()); + + if (!allDependencyBeans.isEmpty()) { + + List autowiredBeans = findAutowiredBeans(project, declarationNode, + allDependencyBeans); + if (autowiredBeans.isEmpty()) { + // Show all relevant dependency beans + autowiredBeans = allDependencyBeans; + } else { + return autowiredBeans; + } + } + } + + return Collections.emptyList(); + } + @SuppressWarnings("unchecked") private List findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection beans) { if (declarationNode instanceof MethodDeclaration) { @@ -190,14 +209,17 @@ public class AutowiredHoverProvider implements HoverProvider { if (type != null) { String fqName = type.getQualifiedName(); if (fqName != null) { - relevant = matchBeans(project, beans, fqName); + relevant = matchBeans(project, beans, fqName, true); if (relevant.isEmpty()) { IType indexType = project.findType(fqName); if (indexType != null) { relevant = project.allSubtypesOf(indexType) - .map(subType -> matchBeans(project, beans, subType.getFullyQualifiedName())) + .map(subType -> matchBeans(project, beans, subType.getFullyQualifiedName(), false)) .filter(relevantBeans -> !relevantBeans.isEmpty()) .blockFirst(); + if (relevant == null) { + relevant = Collections.emptyList(); + } } } } @@ -205,48 +227,83 @@ public class AutowiredHoverProvider implements HoverProvider { return relevant; } - private List matchBeans(IJavaProject project, Collection beans, String fqName) { + private List matchBeans(IJavaProject project, Collection beans, String fqName, boolean allDots) { if (fqName != null) { - return beans.stream().filter(b -> fqName.equals(b.getType(true))).collect(Collectors.toList()); + if (allDots) { + return beans.stream().filter(b -> fqName.equals(b.getType(true).replace('$', '.'))).collect(Collectors.toList()); + } else { + return beans.stream().filter(b -> fqName.equals(b.getType(true))).collect(Collectors.toList()); + } } else { return Collections.emptyList(); } } - private LiveBean getDefinedBean(Annotation autowiredAnnotation) { - TypeDeclaration declaringType = ASTUtils.findDeclaringType(autowiredAnnotation); + private LiveBean getDefinedBeanForTypeDeclaration(TypeDeclaration declaringType) { if (declaringType != null) { for (Annotation annotation : ASTUtils.getAnnotations(declaringType)) { if (AnnotationHierarchies.isSubtypeOf(annotation, Annotations.COMPONENT)) { return ComponentInjectionsHoverProvider.getDefinedBeanForComponent(annotation); } } - //TODO: handler below is an attempt to do something that may work in many cases, but is probably - // missing logics for special cases where annotation attributes on the declaring type matter. + // TODO: handler below is an attempt to do something that may work in many + // cases, but is probably + // missing logics for special cases where annotation attributes on the declaring + // type matter. ITypeBinding beanType = declaringType.resolveBinding(); - if (beanType!=null) { + if (beanType != null) { String beanTypeName = beanType.getName(); if (StringUtil.hasText(beanTypeName)) { return LiveBean.builder() .id(Character.toLowerCase(beanTypeName.charAt(0)) + beanTypeName.substring(1)) - .type(beanTypeName) - .build(); + .type(beanTypeName).build(); } } - return null; } return null; } @Override - public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, - TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { - return null; + 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); } @Override - public Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHoverHints(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, + SpringBootApp[] runningApps) { + LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(methodDeclaration); + try { + Range hoverRange = doc.toRange(methodDeclaration.getName().getStartPosition(), methodDeclaration.getName().getLength()); + return getLiveHoverHints(project, methodDeclaration, hoverRange, runningApps, definedBean); + } catch (BadLocationException e) { + log.error("", e); + } return null; } + private LiveBean getDefinedBeanForImplicitAutowiredConstructor(MethodDeclaration methodDeclaration) { + if (methodDeclaration.isConstructor() && !methodDeclaration.parameters().isEmpty()) { + TypeDeclaration typeDeclaration = ASTUtils.findDeclaringType(methodDeclaration); + if (typeDeclaration != null && ASTUtils.hasExactlyOneConstructor(typeDeclaration) && !hasAutowiredAnnotation(methodDeclaration)) { + return getDefinedBeanForTypeDeclaration(typeDeclaration); + } + } + return null; + } + + private boolean hasAutowiredAnnotation(MethodDeclaration constructor) { + List modifiers = constructor.modifiers(); + for (Object modifier : modifiers) { + if (modifier instanceof MarkerAnnotation) { + ITypeBinding typeBinding = ((MarkerAnnotation) modifier).resolveTypeBinding(); + if (typeBinding != null) { + String fqName = typeBinding.getQualifiedName(); + return Annotations.AUTOWIRED.equals(fqName) || Annotations.INJECT.equals(fqName); + } + } + } + return false; + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java index 20775004b..fa6ea4256 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java @@ -28,7 +28,6 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; 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.LocalSpringBootApp; 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; @@ -50,7 +49,7 @@ public class ConditionalsLiveHoverProvider implements HoverProvider { } @Override - public Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { try { Optional> val = getMatchedLiveConditionals(annotation, runningApps); if (val.isPresent()) { @@ -156,16 +155,4 @@ public class ConditionalsLiveHoverProvider implements HoverProvider { return false; } - @Override - public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, - TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { - return null; - } - - @Override - public Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, - SpringBootApp[] runningApps) { - return null; - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index 0c6ebe255..1908b3036 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -19,6 +19,7 @@ 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.MarkerAnnotation; +import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.SimpleName; @@ -140,6 +141,19 @@ public class BootJavaHoverProvider implements HoverHandler { return super.visit(node); } + + @Override + public boolean visit(MethodDeclaration node) { + try { + extractLiveHintsForMethod(node, document, runningBootApps, result); + } catch (Exception e) { + Log.log(e); + } + + return super.visit(node); + } + + }); } } catch (Exception e) { @@ -149,13 +163,33 @@ public class BootJavaHoverProvider implements HoverHandler { }); } + protected void extractLiveHintsForMethod(MethodDeclaration methodDeclaration, TextDocument doc, + SpringBootApp[] runningApps, Collection result) { + Collection providers = this.hoverProviders.getAll(); + if (!providers.isEmpty()) { + for (HoverProvider provider : providers) { + getProject(doc).ifPresent(project -> { + if (hasActuatorDependency(project)) { + Collection hints = provider.getLiveHoverHints(project, methodDeclaration, doc, runningApps); + if (hints!=null) { + result.addAll(hints); + } + } else { + //Do nothing... we don't want a highlight for the 'no actuator warning' + //ASTUtils.nameRange(doc, annotation).ifPresent(result::add); + } + }); + } + } + } + protected void extractLiveHintsForType(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps, Collection result) { Collection providers = this.hoverProviders.getAll(); if (!providers.isEmpty()) { for (HoverProvider provider : providers) { getProject(doc).ifPresent(project -> { if (hasActuatorDependency(project)) { - Collection hints = provider.getLiveHoverHints(typeDeclaration, doc, runningApps); + Collection hints = provider.getLiveHoverHints(project, typeDeclaration, doc, runningApps); if (hints!=null) { result.addAll(hints); } @@ -175,7 +209,7 @@ public class BootJavaHoverProvider implements HoverHandler { for (HoverProvider provider : this.hoverProviders.get(type)) { getProject(doc).ifPresent(project -> { if (hasActuatorDependency(project)) { - Collection hints = provider.getLiveHoverHints(annotation, doc, runningApps); + Collection hints = provider.getLiveHoverHints(project, annotation, doc, runningApps); if (hints!=null) { result.addAll(hints); } @@ -215,10 +249,29 @@ public class BootJavaHoverProvider implements HoverHandler { } // then do additional AST node coverage - if (node instanceof SimpleName && node.getParent() instanceof TypeDeclaration) { - return provideHoverForTypeDeclaration(node, (TypeDeclaration) node.getParent(), offset, doc, project); + if (node instanceof SimpleName) { + ASTNode parent = node.getParent(); + if (parent instanceof TypeDeclaration) { + return provideHoverForTypeDeclaration(node, (TypeDeclaration) parent, offset, doc, project); + } else if (parent instanceof MethodDeclaration) { + return provideHoverForMethodDeclaration((MethodDeclaration) parent, offset, doc, project); + } } + return null; + } + private Hover provideHoverForMethodDeclaration(MethodDeclaration methodDeclaration, int offset, TextDocument doc, + IJavaProject project) { + SpringBootApp[] runningApps = getRunningSpringApps(project); + if (runningApps.length > 0) { + for (HoverProvider provider : this.hoverProviders.getAll()) { + Hover hover = provider.provideHover(methodDeclaration, offset, doc, project, runningApps); + if (hover!=null) { + //TODO: compose multiple hovers somehow instead of just returning the first one? + return hover; + } + } + } return null; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java index 4dcf3598a..948bed6a5 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java @@ -15,6 +15,7 @@ import java.util.Collection; 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.MethodDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Range; @@ -27,10 +28,24 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public interface HoverProvider { - Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps); - Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps); + default Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { + return null; + } + default Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { + return null; + } + default Hover provideHover(MethodDeclaration methodDeclaration, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { + return null; + } - Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps); - Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps); + default Collection getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + return null; + } + default Collection getLiveHoverHints(IJavaProject project,TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) { + return null; + } + default Collection getLiveHoverHints(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringBootApp[] runningApps) { + return null; + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RunningAppMatcher.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RunningAppMatcher.java index cb91bd6af..ecd74d1cc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RunningAppMatcher.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RunningAppMatcher.java @@ -33,19 +33,26 @@ public class RunningAppMatcher { return RunningAppMatcher.doesProjectMatch(app, project); }).collect(CollectorUtil.toImmutableList()); - if (matchedProjects.size() > 0) { - return matchedProjects; - } + return matchedProjects; } return apps; } private static boolean doesProjectMatch(SpringBootApp app, IJavaProject project) { - if (doesProjectNameMatch(app, project)) return true; - if (doesProjectThinJarWrapperMatch(app, project)) return true; - if (doesClasspathMatch(app, project)) return true; + if (hasProjectName(app, project)) { + return doesProjectNameMatch(app, project); + } + return true; + } - return false; + public static boolean hasProjectName(SpringBootApp app, IJavaProject project) { + try { + String projectName = app.getSystemProperty("spring.boot.project.name"); + return projectName != null && projectName.trim().length() > 0; + } + catch (Exception e) { + return false; + } } public static boolean doesProjectNameMatch(SpringBootApp app, IJavaProject project) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java index 59c746984..8e166424e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.boot.java.livehover; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -22,6 +23,8 @@ import org.eclipse.jdt.core.dom.ITypeBinding; 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.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.handlers.HoverProvider; import org.springframework.ide.vscode.boot.java.utils.ASTUtils; @@ -29,14 +32,17 @@ 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.boot.app.cli.livebean.LiveBeansModel; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; -import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider { + private static Logger LOG = LoggerFactory.getLogger(AbstractInjectedIntoHoverProvider.class); + + private static final int MAX_INLINE_BEANS_STRING_LENGTH = 60; + private static final String INLINE_BEANS_STRING_SEPARATOR = " "; + protected BootJavaLanguageServerComponents server; public AbstractInjectedIntoHoverProvider(BootJavaLanguageServerComponents server) { @@ -44,7 +50,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider } @Override - public Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHoverHints(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 +65,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider } } } catch (Exception e) { - Log.log(e); + LOG.error("", e); } return ImmutableList.of(); } @@ -71,59 +77,80 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider LiveBean definedBean = getDefinedBean(annotation); if (definedBean != null) { - StringBuilder hover = new StringBuilder(); - hover.append("**Injection report for " + LiveHoverUtils.showBean(definedBean) + "**\n\n"); - - boolean hasInterestingApp = false; - for (SpringBootApp app : runningApps) { - LiveBeansModel beans = app.getBeans(); - List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList()); - - if (!relevantBeans.isEmpty()) { - if (!hasInterestingApp) { - hasInterestingApp = true; - } else { - hover.append("\n\n"); - } - hover.append(LiveHoverUtils.niceAppName(app) + ":"); - - for (LiveBean bean : relevantBeans) { - addInjectedInto(definedBean, hover, beans, bean, project); - addAutomaticallyWiredContructor(hover, annotation, beans, bean, project); - } - } - } - if (hasInterestingApp) { - return new Hover(ImmutableList.of(Either.forLeft(hover.toString()))); - } + return assembleHover(project, runningApps, definedBean); } } return null; } + protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean) { + StringBuilder hover = new StringBuilder(); + + boolean hasContent = false; + + for (SpringBootApp app : runningApps) { + + List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); + + if (!relevantBeans.isEmpty()) { + List injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans); + + if (!hasContent) { + hasContent = true; + } else { + hover.append(" \n \n"); + } + + if (injectedBeans.isEmpty()) { + hover.append("**Injected `"); + hover.append(definedBean.getId()); + hover.append("` → _not injected anywhere_** \n"); + } else { + hover.append("**Injected `"); + hover.append(definedBean.getId()); + hover.append("` → "); + if (LiveHoverUtils.doBeansFitInline(injectedBeans, MAX_INLINE_BEANS_STRING_LENGTH - definedBean.getId().length(), + INLINE_BEANS_STRING_SEPARATOR)) { + hover.append(injectedBeans.stream().map(b -> LiveHoverUtils.showBeanInline(server, project, b)) + .collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR))); + hover.append("**\n"); + } else { + hover.append(injectedBeans.size()); + hover.append(" bean"); + if (injectedBeans.size() > 1) { + hover.append('s'); + } + hover.append("**\n"); + } + hover.append(injectedBeans.stream() + .map(b -> "- " + LiveHoverUtils.showBeanWithResource(server, b, " ", project)) + .collect(Collectors.joining("\n"))); + hover.append("\n \n"); + } + hover.append(LiveHoverUtils.niceAppName(app)); + } + + } + if (hasContent) { + return new Hover(ImmutableList.of(Either.forLeft(hover.toString()))); + } else { + return null; + } + + } + + protected List getRelevantInjectedIntoBeans(IJavaProject project, SpringBootApp app, LiveBean definedBean, List relevantBeans) { + LiveBeansModel beans = app.getBeans(); + if (relevantBeans != null) { + return relevantBeans.stream() + .flatMap(b -> beans.getBeansDependingOn(b.getId()).stream()) + .distinct() + .collect(Collectors.toList()); + + } + return Collections.emptyList(); + } + protected abstract LiveBean getDefinedBean(Annotation annotation); - protected void addAutomaticallyWiredContructor(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean, IJavaProject project) { - //This doesn't really belong here, but it accomodates Martin's additional logic to handle implicitly - //@Autowired constructor. - //This does nothing by default as its really only relevant to @Component annotation report. - } - - protected void addInjectedInto(LiveBean definedBean, StringBuilder hover, LiveBeansModel beans, LiveBean bean, IJavaProject project) { - hover.append("\n\n"); - List dependers = beans.getBeansDependingOn(bean.getId()); - if (dependers.isEmpty()) { - hover.append(LiveHoverUtils.showBean(bean) + " exists but is **Not injected anywhere**\n"); - } else { - hover.append(LiveHoverUtils.showBean(bean) + " injected into:\n\n"); - boolean firstDependency = true; - for (LiveBean dependingBean : dependers) { - if (!firstDependency) { - hover.append("\n"); - } - hover.append("- " + LiveHoverUtils.showBeanWithResource(server, dependingBean, " ", project)); - firstDependency = false; - } - } - } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java index a4f674ac3..aa65941a3 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java @@ -22,7 +22,6 @@ 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.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.messages.Either; @@ -81,7 +80,7 @@ public class ActiveProfilesProvider implements HoverProvider { } @Override - public Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { if (runningApps.length > 0) { Builder ranges = ImmutableList.builder(); nameRange(doc, annotation).ifPresent(ranges::add); @@ -130,16 +129,4 @@ public class ActiveProfilesProvider implements HoverProvider { } } - @Override - public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, - TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { - return null; - } - - @Override - public Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, - SpringBootApp[] runningApps) { - return null; - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java index 9786a7d33..c1ac6966d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java @@ -10,24 +10,14 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.livehover; -import java.util.Collection; import java.util.Optional; -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.MethodDeclaration; -import org.eclipse.jdt.core.dom.TypeDeclaration; -import org.eclipse.lsp4j.Hover; -import org.eclipse.lsp4j.Range; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; 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.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.util.Optionals; -import org.springframework.ide.vscode.commons.util.text.TextDocument; public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProvider { @@ -76,16 +66,4 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv ); } - @Override - public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, - TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { - return null; - } - - @Override - public Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, - SpringBootApp[] runningApps) { - return null; - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java index db255328b..65c4e473b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java @@ -14,27 +14,23 @@ import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; 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.MarkerAnnotation; -import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; 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.Annotations; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies; 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.boot.app.cli.livebean.LiveBeansModel; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -42,53 +38,12 @@ import com.google.common.collect.ImmutableList; public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverProvider { + private static Logger LOG = LoggerFactory.getLogger(ComponentInjectionsHoverProvider.class); + public ComponentInjectionsHoverProvider(BootJavaLanguageServerComponents server) { super(server); } - @Override - protected void addAutomaticallyWiredContructor(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean, IJavaProject project) { - TypeDeclaration typeDecl = ASTUtils.findDeclaringType(annotation); - if (typeDecl != null) { - MethodDeclaration[] constructors = ASTUtils.findConstructors(typeDecl); - - if (constructors != null && constructors.length == 1 && !hasAutowiredAnnotation(constructors[0])) { - String[] dependencies = bean.getDependencies(); - - if (dependencies != null && dependencies.length > 0) { - hover.append("\n\n"); - hover.append(LiveHoverUtils.showBean(bean) + " got autowired with:\n\n"); - - boolean firstDependency = true; - for (String injectedBean : dependencies) { - if (!firstDependency) { - hover.append("\n"); - } - List dependencyBeans = beans.getBeansOfName(injectedBean); - for (LiveBean dependencyBean : dependencyBeans) { - hover.append("- " + LiveHoverUtils.showBeanWithResource(server, dependencyBean, " ", project)); - } - firstDependency = false; - } - } - } - } - } - - private boolean hasAutowiredAnnotation(MethodDeclaration constructor) { - List modifiers = constructor.modifiers(); - for (Object modifier : modifiers) { - if (modifier instanceof MarkerAnnotation) { - ITypeBinding typeBinding = ((MarkerAnnotation) modifier).resolveTypeBinding(); - if (typeBinding != null) { - String fqName = typeBinding.getQualifiedName(); - return Annotations.AUTOWIRED.equals(fqName) || Annotations.INJECT.equals(fqName); - } - } - } - return false; - } - @Override protected LiveBean getDefinedBean(Annotation annotation) { return getDefinedBeanForComponent(annotation); @@ -106,7 +61,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP if (beanType != null) { String id = getBeanId(annotation, beanType); if (StringUtil.hasText(id)) { - return LiveBean.builder().id(id).type(beanType.getQualifiedName()).build(); + return LiveBean.builder().id(id).type(getBeanType(beanType).toString()).build(); } } } @@ -119,9 +74,8 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP String typeName = beanType.getName(); ITypeBinding declaringClass = beanType.getDeclaringClass(); - while (declaringClass != null) { - typeName = declaringClass.getName() + "." + typeName; - declaringClass = declaringClass.getDeclaringClass(); + if (declaringClass != null) { + return getBeanType(beanType).toString(); } if (StringUtil.hasText(typeName)) { @@ -131,8 +85,20 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP }); } + private static StringBuilder getBeanType(ITypeBinding beanType) { + ITypeBinding declaringClass = beanType.getDeclaringClass(); + if (declaringClass == null) { + return new StringBuilder(beanType.getQualifiedName()); + } else { + StringBuilder sb = getBeanType(declaringClass); + sb.append('$'); + sb.append(beanType.getName()); + return sb; + } + } + @Override - public Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, + public Collection getLiveHoverHints(IJavaProject project, TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) { if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) { try { @@ -146,7 +112,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP } } } catch (Exception e) { - Log.log(e); + LOG.error("", e); } } return ImmutableList.of(); @@ -160,30 +126,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null); if (definedBean != null) { - StringBuilder hover = new StringBuilder(); - hover.append("**Injection report for " + LiveHoverUtils.showBean(definedBean) + "**\n\n"); - - boolean hasInterestingApp = false; - for (SpringBootApp app : runningApps) { - LiveBeansModel beans = app.getBeans(); - List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList()); - - if (!relevantBeans.isEmpty()) { - if (!hasInterestingApp) { - hasInterestingApp = true; - } else { - hover.append("\n\n"); - } - hover.append(LiveHoverUtils.niceAppName(app) + ":"); - - for (LiveBean bean : relevantBeans) { - addInjectedInto(definedBean, hover, beans, bean, project); - } - } - } - if (hasInterestingApp) { - return new Hover(ImmutableList.of(Either.forLeft(hover.toString()))); - } + return assembleHover(project, runningApps, definedBean); } } return null; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java index 4851bb21c..338610b64 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java @@ -11,8 +11,10 @@ package org.springframework.ide.vscode.boot.java.livehover; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Optional; -import java.util.stream.Stream; +import java.util.stream.Collectors; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; @@ -131,20 +133,25 @@ public class LiveHoverUtils { } public static boolean hasRelevantBeans(SpringBootApp app, LiveBean definedBean) { - return findRelevantBeans(app, definedBean).findAny().isPresent(); + return findRelevantBeans(app, definedBean).stream().findAny().isPresent(); } - public static Stream findRelevantBeans(SpringBootApp app, LiveBean definedBean) { + public static List findRelevantBeans(SpringBootApp app, LiveBean definedBean) { LiveBeansModel beansModel = app.getBeans(); if (beansModel != null) { - Stream relevantBeans = beansModel.getBeansOfName(definedBean.getId()).stream(); + List relevantBeans = beansModel.getBeansOfName(definedBean.getId()); String type = definedBean.getType(); if (type != null) { - relevantBeans = relevantBeans.filter(bean -> type.equals(bean.getType(true))); + // TODO: check if we should check for bean type rather than id that we build ourselves based on type +// if (relevantBeans.isEmpty()) { +// relevantBeans = beansModel.getBeansOfType(type); +// } else { + relevantBeans = relevantBeans.stream().filter(bean -> type.equals(bean.getType(true))).collect(Collectors.toList()); +// } } return relevantBeans; } - return Stream.empty(); + return Collections.emptyList(); } public static String niceAppName(SpringBootApp app) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index 01e9370c5..2e763c506 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -22,7 +22,6 @@ 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.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.MarkedString; import org.eclipse.lsp4j.Range; @@ -55,7 +54,7 @@ public class RequestMappingHoverProvider implements HoverProvider { } @Override - public Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { try { if (runningApps.length > 0) { List> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); @@ -173,16 +172,4 @@ public class RequestMappingHoverProvider implements HoverProvider { } } - @Override - public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, - TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { - return null; - } - - @Override - public Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, - SpringBootApp[] runningApps) { - return null; - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java index c7394b8e2..567c8b87a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -128,8 +127,7 @@ public class ASTUtils { return Optional.empty(); } - public static TypeDeclaration findDeclaringType(Annotation annotation) { - ASTNode node = annotation; + public static TypeDeclaration findDeclaringType(ASTNode node) { while (node != null && !(node instanceof TypeDeclaration)) { node = node.getParent(); } @@ -137,20 +135,21 @@ public class ASTUtils { return node != null ? (TypeDeclaration) node : null; } - public static MethodDeclaration[] findConstructors(TypeDeclaration typeDecl) { - List constructors = new ArrayList<>(); - + public static boolean hasExactlyOneConstructor(TypeDeclaration typeDecl) { + boolean oneFound = false; MethodDeclaration[] methods = typeDecl.getMethods(); for (MethodDeclaration methodDeclaration : methods) { if (methodDeclaration.isConstructor()) { - constructors.add(methodDeclaration); + if (oneFound) { + return false; + } else { + oneFound = true; + } } } - - return constructors.toArray(new MethodDeclaration[constructors.size()]); + return oneFound; } - public static MethodDeclaration getAnnotatedMethod(Annotation annotation) { ASTNode parent = annotation.getParent(); if (parent instanceof MethodDeclaration) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java index 9f104c6b4..9a29cebfe 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.value; -import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -67,11 +66,6 @@ public class ValueHoverProvider implements HoverProvider { return null; } - @Override - public Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { - return null; - } - private Hover provideHover(String value, int offset, int nodeStartOffset, TextDocument doc, SpringBootApp[] runningApps) { try { @@ -206,10 +200,4 @@ public class ValueHoverProvider implements HoverProvider { return null; } - @Override - public Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, - SpringBootApp[] runningApps) { - return null; - } - } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java index 1deba0edf..fed26d0a7 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java @@ -14,6 +14,8 @@ import static org.junit.Assert.assertTrue; import java.nio.file.Paths; import java.time.Duration; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.Before; import org.junit.Test; @@ -84,6 +86,34 @@ public class AutowiredHoverProviderTest { "}\n" ); + p.createType("com.example.RuntimeBeanFactory", + Stream.of( + "package com.example;", + "public interface RuntimeBeanFactory {", + "void createRuntimeBean(String info);", + "}" + ).collect(Collectors.joining("\n")) + ); + + p.createType("com.example.SomeComponent", + Stream.of("package com.example;", + "", + "import org.springframework.context.annotation.Bean;", + "", +// "@Component", + "public class SomeComponent {", + "", + "@Bean", + "public RuntimeBeanFactory getBeanFactory() {", + "\treturn new RuntimeBeanFactory() {", + "\t\tpublic void createRuntimeBean(String info){}", + "\t};", + "}", + "", + "}" + ).collect(Collectors.joining("\n")) + ); + p.createType("com.example.FooImplementation", FOO_IMPL_CONTENTS); }; @@ -146,7 +176,7 @@ public class AutowiredHoverProviderTest { editor.assertHighlights("@Component", "@Inject"); editor.assertTrimmedHover("@Inject", - "**Autowired → `dependencyA`**\n" + + "**Autowired `autowiredClass` → `dependencyA`**\n" + "- Bean: `dependencyA` \n" + " Type: `com.example.DependencyA` \n" + " Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" + @@ -201,7 +231,7 @@ public class AutowiredHoverProviderTest { editor.assertHighlights("@Component", "@Autowired"); editor.assertTrimmedHover("@Autowired", - "**Autowired → `dependencyA` `dependencyB`**\n" + + "**Autowired `autowiredClass` → `dependencyA` `dependencyB`**\n" + "- Bean: `dependencyA` \n" + " Type: `com.example.DependencyA` \n" + " Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" + @@ -352,11 +382,11 @@ public class AutowiredHoverProviderTest { Editor editor = harness.newEditor(LanguageId.JAVA, FOO_IMPL_CONTENTS); editor.assertHighlights("@Component", "@Autowired", "@Autowired"); editor.assertHoverContains("@Autowired", 1, - "**Autowired → `superBean`**\n" + + "**Autowired `defaultFoo` → `superBean`**\n" + "- Bean: `superBean` \n" + " Type: `com.example.FooImplementation`"); editor.assertHoverContains("@Autowired", 2, - "**Autowired → `scheduler`**\n" + + "**Autowired `defaultFoo` → `scheduler`**\n" + "- Bean: `scheduler` \n" + " Type: `org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler`"); } @@ -406,13 +436,220 @@ public class AutowiredHoverProviderTest { ); editor.assertHighlights("@Controller", "@Autowired"); editor.assertHoverContains("@Autowired", - "**Autowired → `restTemplate`**\n" + + "**Autowired `myController` → `restTemplate`**\n" + "- Bean: `restTemplate` \n" + " Type: `org.springframework.web.client.RestTemplate`" ); editor.assertHoverContains("@Controller", - "**Injection report for Bean [id: myController, type: `com.example.MyController`]**" + "**Injected `myController` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]" ); } + @Test + public void implicitAutowiringSingleConstructor() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("someComponent") + .type("com.example.SomeComponent") + .dependencies("dependencyA", "dependencyB") + .build() + ) + .add(LiveBean.builder() + .id("dependencyA") + .type("com.example.DependencyA") + .build() + ) + .add(LiveBean.builder() + .id("dependencyB") + .type("com.example.DependencyB") + .build() + ) + .build(); + mockAppProvider.builder() + .isSpringBootApp(true) + .processId("111") + .processName("the-app") + .beans(beans) + .build(); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Component;\n" + + "\n" + + "@Component\n" + + "public class SomeComponent {\n" + + "\n" + + " private DepedencyA depA;\n" + + " private DepedencyB depB;\n" + + "\n" + + " public SomeComponent(DependencyA depA, DependencyB depB) {\n" + + " this.depA = depA;\n" + + " this.depB = depB;\n" + + " }\n" + + "}\n" + ); + + editor.assertHighlights("@Component", "SomeComponent"); + + editor.assertTrimmedHover("SomeComponent", 2, + "**Autowired `someComponent` → `dependencyA` `dependencyB`**\n" + + "- Bean: `dependencyA` \n" + + " Type: `com.example.DependencyA`\n" + + "- Bean: `dependencyB` \n" + + " Type: `com.example.DependencyB`\n" + + " \n" + + "Process [PID=111, name=`the-app`]\n" + ); + } + + @Test + public void noImplicitAutowiringForConstructorFromNonBean() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("someOtherComponent") + .type("com.example.SomeOtherComponent") + .dependencies("dependencyA", "dependencyB") + .build() + ) + .add(LiveBean.builder() + .id("dependencyA") + .type("com.example.DependencyA") + .build() + ) + .add(LiveBean.builder() + .id("dependencyB") + .type("com.example.DependencyB") + .build() + ) + .build(); + mockAppProvider.builder() + .isSpringBootApp(true) + .processId("111") + .processName("the-app") + .beans(beans) + .build(); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "public class SomeComponent {\n" + + "\n" + + " private DepedencyA depA;\n" + + " private DepedencyB depB;\n" + + "\n" + + " public SomeComponent(DependencyA depA, DependencyB depB) {\n" + + " this.depA = depA;\n" + + " this.depB = depB;\n" + + " }\n" + + "}\n" + ); + + editor.assertHighlights(); + + for (int i = 1; i < 2; i++) { + editor.assertNoHover("SomeComponent", i); + } + } + + @Test + public void noImplicitAutowiringForMultipleConstructors() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("someComponent") + .type("com.example.SomeComponent") + .dependencies("dependencyA", "dependencyB") + .build() + ) + .add(LiveBean.builder() + .id("dependencyA") + .type("com.example.DependencyA") + .build() + ) + .add(LiveBean.builder() + .id("dependencyB") + .type("com.example.DependencyB") + .build() + ) + .build(); + mockAppProvider.builder() + .isSpringBootApp(true) + .processId("111") + .processName("the-app") + .beans(beans) + .build(); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Component;\n" + + "\n" + + "@Component\n" + + "public class SomeComponent {\n" + + "\n" + + " private DepedencyA depA;\n" + + " private DepedencyB depB;\n" + + "\n" + + " public SomeComponent() {\n" + + " }\n" + + "\n" + + " public SomeComponent(DependencyA depA, DependencyB depB) {\n" + + " this.depA = depA;\n" + + " this.depB = depB;\n" + + " }\n" + + "}\n" + ); + + editor.assertHighlights("@Component"); + for (int i = 1; i < 3; i++) { + editor.assertNoHover("SomeComponent", i); + } + } + + @Test + public void anonymousInnerClassBeanWiring() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("anotherComponent") + .type("com.example.AnotherComponent") + .dependencies("anonymousBeanFactory") + .build() + ) + .add(LiveBean.builder() + .id("anonymousBeanFactory") + .type("com.example.SomeComponent$1") + .build() + ) + .build(); + mockAppProvider.builder() + .isSpringBootApp(true) + .processId("111") + .processName("the-app") + .beans(beans) + .build(); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.beans.factory.annotation.Autowired;\n" + + "import org.springframework.stereotype.Component;\n" + + "\n" + + "@Component\n" + + "public class AnotherComponent {\n" + + "\n" + + " @Autowired\n" + + " RuntimeBeanFactory beanFactory;\n" + + "}\n" + ); + + editor.assertHighlights("@Component", "@Autowired"); + editor.assertTrimmedHover("@Autowired", 1, + "**Autowired `anotherComponent` → `anonymousBeanFactory`**\n" + + "- Bean: `anonymousBeanFactory` \n" + + " Type: `com.example.SomeComponent$1`\n" + + " \n" + + "Process [PID=111, name=`the-app`]\n" + ); + } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java index a995fe702..e126497e9 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java @@ -95,11 +95,8 @@ public class BeanInjectedIntoHoverProviderTest { ); editor.assertHighlights("@Bean"); editor.assertTrimmedHover("@Bean", - "**Injection report for Bean [id: myFoo]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: myFoo, type: `hello.FooImplementation`] exists but is **Not injected anywhere**\n" + "**Injected `myFoo` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -154,11 +151,8 @@ public class BeanInjectedIntoHoverProviderTest { ); editor.assertHighlights("@Bean"); editor.assertTrimmedHover("@Bean", - "**Injection report for Bean [id: beanId]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: beanId, type: `hello.FooImplementation`] exists but is **Not injected anywhere**\n" + "**Injected `beanId` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]" ); } } @@ -209,14 +203,11 @@ public class BeanInjectedIntoHoverProviderTest { ); editor.assertHighlights("@Bean"); editor.assertTrimmedHover("@Bean", - "**Injection report for Bean [id: fooImplementation]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController`**\n" + "- Bean: `myController` \n" + - " Type: `hello.MyController`\n" + " Type: `hello.MyController`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -269,14 +260,11 @@ public class BeanInjectedIntoHoverProviderTest { ); editor.assertHighlights("@Bean"); editor.assertTrimmedHover("@Bean", - "**Injection report for Bean [id: fooImplementation]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController`**\n" + "- Bean: `myController` \n" + - " Type: `hello.MyController`\n" + " Type: `hello.MyController`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -323,15 +311,12 @@ public class BeanInjectedIntoHoverProviderTest { ); editor.assertHighlights("@Bean"); editor.assertTrimmedHover("@Bean", - "**Injection report for Bean [id: fooImplementation]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController`**\n" + "- Bean: `myController` \n" + " Type: `hello.MyController` \n" + - " Resource: `" + Paths.get("hello/MyController.class") + "`" + " Resource: `" + Paths.get("hello/MyController.class") + "`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -377,15 +362,12 @@ public class BeanInjectedIntoHoverProviderTest { ); editor.assertHighlights("@Bean"); editor.assertTrimmedHover("@Bean", - "**Injection report for Bean [id: fooImplementation]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController`**\n" + "- Bean: `myController` \n" + " Type: `hello.MyController` \n" + - " Resource: `hello/MyController.class`" + " Resource: `hello/MyController.class`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -435,16 +417,13 @@ public class BeanInjectedIntoHoverProviderTest { ); editor.assertHighlights("@Bean"); editor.assertTrimmedHover("@Bean", - "**Injection report for Bean [id: fooImplementation]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController` `otherBean`**\n" + "- Bean: `myController` \n" + " Type: `hello.MyController`\n" + "- Bean: `otherBean` \n" + - " Type: `hello.OtherBean`\n" + " Type: `hello.OtherBean`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java index cea6fc61f..e80574434 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java @@ -134,14 +134,11 @@ public class BeansByTypeHoverProviderTest { ); editor.assertHighlights("ScannedRandomClass"); editor.assertTrimmedHover("ScannedRandomClass", - "**Injection report for Bean [id: scannedRandomClass, type: `com.example.ScannedRandomClass`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: scannedRandomClass, type: `com.example.ScannedRandomClass`] injected into:\n" + - "\n" + + "**Injected `scannedRandomClass` → `randomOtherBean`**\n" + "- Bean: `randomOtherBean` \n" + - " Type: `randomOtherBeanType`" + " Type: `randomOtherBeanType`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -190,14 +187,12 @@ public class BeansByTypeHoverProviderTest { ); editor.assertHighlights("ScannedFunctionClass"); editor.assertTrimmedHover("ScannedFunctionClass", - "**Injection report for Bean [id: scannedFunctionClass, type: `com.example.ScannedFunctionClass`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: scannedFunctionClass, type: `com.example.ScannedFunctionClass`] injected into:\n" + - "\n" + + "**Injected `scannedFunctionClass` → 1 bean**\n" + "- Bean: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration` \n" + - " Type: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration`" + " Type: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" + ); } @@ -233,11 +228,8 @@ public class BeansByTypeHoverProviderTest { ); editor.assertHighlights("@Component"); editor.assertTrimmedHover("@Component", - "**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `com.example.FooImplementation`] exists but is **Not injected anywhere**\n" + "**Injected `fooImplementation` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]" ); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java index cef834e18..49e1e07e1 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java @@ -12,7 +12,6 @@ package org.springframework.ide.vscode.boot.java.livehover.test; import static org.junit.Assert.assertTrue; -import java.nio.file.Paths; import java.time.Duration; import org.junit.Before; @@ -107,11 +106,8 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@Component"); editor.assertTrimmedHover("@Component", - "**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `com.example.FooImplementation`] exists but is **Not injected anywhere**\n" + "**Injected `fooImplementation` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -159,14 +155,11 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@Component"); editor.assertTrimmedHover("@Component", - "**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController`**\n" + "- Bean: `myController` \n" + - " Type: `com.example.MyController`" + " Type: `com.example.MyController`\n" + + " \n" + + "Process [PID=111, name=`the-app`]\n" ); } @@ -214,16 +207,13 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@Component"); editor.assertTrimmedHover("@Component", - "**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController` `otherBean`**\n" + "- Bean: `myController` \n" + " Type: `com.example.MyController`\n" + "- Bean: `otherBean` \n" + - " Type: `com.example.OtherBean`" + " Type: `com.example.OtherBean`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -273,25 +263,21 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@Component"); editor.assertTrimmedHover("@Component", - "**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" + - "\n" + - "Process [PID=1001, name=`app-instance-1`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController` `otherBean`**\n" + "- Bean: `myController` \n" + " Type: `com.example.MyController`\n" + "- Bean: `otherBean` \n" + " Type: `com.example.OtherBean`\n" + - "\n" + - "Process [PID=1002, name=`app-instance-2`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" + - "\n" + + " \n" + + "Process [PID=1001, name=`app-instance-1`]" + + " \n \n" + + "**Injected `fooImplementation` → `myController` `otherBean`**\n" + "- Bean: `myController` \n" + " Type: `com.example.MyController`\n" + "- Bean: `otherBean` \n" + - " Type: `com.example.OtherBean`\n" + " Type: `com.example.OtherBean`\n" + + " \n" + + "Process [PID=1002, name=`app-instance-2`]" ); } @@ -344,14 +330,11 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@Component"); editor.assertHoverExactText("@Component", - "**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `fooImplementation` → `myController`**\n" + "- Bean: `myController` \n" + - " Type: `com.example.MyController`" + " Type: `com.example.MyController`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -404,14 +387,11 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@Component"); editor.assertTrimmedHover("@Component", - "**Injection report for Bean [id: alternateFooImplementation, type: `com.example.FooImplementation`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: alternateFooImplementation, type: `com.example.FooImplementation`] injected into:\n" + - "\n" + + "**Injected `alternateFooImplementation` → `otherBean`**\n" + "- Bean: `otherBean` \n" + - " Type: `com.example.OtherBean`\n" + " Type: `com.example.OtherBean`\n" + + " \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -451,7 +431,7 @@ public class ComponentInjectionsHoverProviderTest { " }\n" + "}\n" ); - editor.assertHighlights(/*MONE*/); + editor.assertHighlights(/*NONE*/); editor.assertNoHover("@Component"); } @@ -516,22 +496,10 @@ public class ComponentInjectionsHoverProviderTest { " }\n" + "}\n" ); - editor.assertHighlights("@Component"); + editor.assertHighlights("@Component", "AutowiredClass"); editor.assertTrimmedHover("@Component", - "**Injection report for Bean [id: autowiredClass, type: `com.example.AutowiredClass`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: autowiredClass, type: `com.example.AutowiredClass`] exists but is **Not injected anywhere**\n" + - "\n\n" + - "Bean [id: autowiredClass, type: `com.example.AutowiredClass`] got autowired with:\n" + - "\n" + - "- Bean: `dependencyA` \n" + - " Type: `com.example.DependencyA` \n" + - " Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" + - "- Bean: `dependencyB` \n" + - " Type: `com.example.DependencyB` \n" + - " Resource: `com/example/DependencyB.class`" + "**Injected `autowiredClass` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]\n" ); } @@ -578,11 +546,8 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@Component", "@Autowired"); editor.assertTrimmedHover("@Component", - "**Injection report for Bean [id: autowiredClass, type: `com.example.AutowiredClass`]**\n" + - "\n" + - "Process [PID=111, name=`the-app`]:\n" + - "\n" + - "Bean [id: autowiredClass, type: `com.example.AutowiredClass`] exists but is **Not injected anywhere**\n" + "**Injected `autowiredClass` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]\n" ); } @@ -621,7 +586,8 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@SpringBootApplication"); editor.assertHoverContains("@SpringBootApplication", - "**Injection report for Bean [id: demoApplication, type: `com.example.DemoApplication`]**" + "**Injected `demoApplication` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -629,7 +595,7 @@ public class ComponentInjectionsHoverProviderTest { public void componentFromInnerClass() throws Exception { LiveBeansModel beans = LiveBeansModel.builder() .add(LiveBean.builder() - .id("demoApplication.InnerClass") + .id("com.example.DemoApplication$InnerClass") .type("com.example.DemoApplication$InnerClass") .build() ) @@ -662,7 +628,8 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@SpringBootApplication"); editor.assertHoverContains("@SpringBootApplication", - "**Injection report for Bean [id: demoApplication.InnerClass, type: `com.example.DemoApplication.InnerClass`]**" + "**Injected `com.example.DemoApplication$InnerClass` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]" ); } @@ -670,7 +637,7 @@ public class ComponentInjectionsHoverProviderTest { public void componentFromInnerInnerClass() throws Exception { LiveBeansModel beans = LiveBeansModel.builder() .add(LiveBean.builder() - .id("demoApplication.InnerClass.InnerInnerClass") + .id("com.example.DemoApplication$InnerClass$InnerInnerClass") .type("com.example.DemoApplication$InnerClass$InnerInnerClass") .build() ) @@ -706,7 +673,8 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@SpringBootApplication"); editor.assertHoverContains("@SpringBootApplication", - "**Injection report for Bean [id: demoApplication.InnerClass.InnerInnerClass, type: `com.example.DemoApplication.InnerClass.InnerInnerClass`]**" + "**Injected `com.example.DemoApplication$InnerClass$InnerInnerClass` → _not injected anywhere_** \n" + + "Process [PID=111, name=`the-app`]" ); } } diff --git a/vscode-extensions/commons-vscode/icons/boot-12h.png b/vscode-extensions/commons-vscode/icons/boot-12h.png new file mode 100644 index 000000000..3e4d5295a Binary files /dev/null and b/vscode-extensions/commons-vscode/icons/boot-12h.png differ diff --git a/vscode-extensions/commons-vscode/src/highlight-service.ts b/vscode-extensions/commons-vscode/src/highlight-service.ts index cd7915166..e55545b4c 100644 --- a/vscode-extensions/commons-vscode/src/highlight-service.ts +++ b/vscode-extensions/commons-vscode/src/highlight-service.ts @@ -27,10 +27,15 @@ export class HighlightService { constructor() { this.DECORATION = VSCode.window.createTextEditorDecorationType({ - // textDecoration: "underline", - gutterIconPath: path.resolve(__dirname, "../icons/boot-icon.png"), - gutterIconSize: "contain", - outline: "#32BA56 dotted thin" + before: { + contentIconPath: path.resolve(__dirname, "../icons/boot-12h.png"), + margin: '2px 2px 0px 0px' + }, + backgroundColor: 'rgba(109,179,63,0.25)', + borderColor: 'rgba(109,179,63,0.25)', + borderSpacing: '4px', + borderRadius: '4px', + borderWidth: '4px' }); this.highlights = new Map(); } @@ -49,6 +54,7 @@ export class HighlightService { let highlights : Range[] = this.highlights.get(uri) || []; let decorations = highlights.map(hl => toDecoration(hl)); editor.setDecorations(this.DECORATION, decorations); + editor.setDecorations(this.DECORATION, decorations); } } }