From 6783ffdbe8421ec98883bd96526e610395866da4 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 21 Nov 2017 11:30:55 -0800 Subject: [PATCH] Make live hovers `AnnotationHierarchyAware` --- .../boot/java/BootJavaLanguageServer.java | 2 +- .../java/handlers/BootJavaHoverProvider.java | 66 +++++++++---------- .../livehover/ActiveProfilesProvider.java | 24 ++++--- .../test/AutowiredHoverProviderTest.java | 21 +++--- .../test/ActiveProfilesHoverTest.java | 5 +- 5 files changed, 62 insertions(+), 56 deletions(-) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index 1970ca546..6779af88e 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -250,7 +250,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { protected BootJavaHoverProvider createHoverHandler(JavaProjectFinder javaProjectFinder, RunningAppProvider runningAppProvider) { - HashMap providers = new HashMap<>(); + AnnotationHierarchyAwareLookup providers = new AnnotationHierarchyAwareLookup<>(); providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueHoverProvider()); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index f169e589f..3cf3416dd 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -12,6 +12,7 @@ package org.springframework.ide.vscode.boot.java.handlers; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -32,6 +33,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextDocumentPositionParams; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; +import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup; import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.java.IClasspath; @@ -53,10 +55,10 @@ public class BootJavaHoverProvider implements HoverHandler { private JavaProjectFinder projectFinder; private BootJavaLanguageServer server; - private Map hoverProviders; + private AnnotationHierarchyAwareLookup hoverProviders; private RunningAppProvider runningAppProvider; - public BootJavaHoverProvider(BootJavaLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders, RunningAppProvider runningAppProvider) { + public BootJavaHoverProvider(BootJavaLanguageServer server, JavaProjectFinder projectFinder, AnnotationHierarchyAwareLookup specificProviders, RunningAppProvider runningAppProvider) { this.server = server; this.projectFinder = projectFinder; this.hoverProviders = specificProviders; @@ -84,7 +86,7 @@ public class BootJavaHoverProvider implements HoverHandler { public Range[] getLiveHoverHints(final TextDocument document, final SpringBootApp[] runningBootApps) { return server.getCompilationUnitCache().withCompilationUnit(document, cu -> { - List result = new ArrayList<>(); + Collection result = new HashSet<>(); try { if (cu != null) { cu.accept(new ASTVisitor() { @@ -129,26 +131,22 @@ public class BootJavaHoverProvider implements HoverHandler { }); } - protected void extractLiveHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps, List result) { + protected void extractLiveHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps, Collection result) { ITypeBinding type = annotation.resolveTypeBinding(); if (type != null) { - String qualifiedName = type.getQualifiedName(); - if (qualifiedName != null) { - HoverProvider provider = this.hoverProviders.get(qualifiedName); - if (provider != null) { - if (runningApps.length>0) { - getProject(doc).ifPresent(project -> { - if (hasActuatorDependency(project)) { - Collection hints = provider.getLiveHoverHints(annotation, 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); + if (runningApps.length>0) { + for (HoverProvider provider : this.hoverProviders.get(type)) { + getProject(doc).ifPresent(project -> { + if (hasActuatorDependency(project)) { + Collection hints = provider.getLiveHoverHints(annotation, 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); + } + }); } } } @@ -179,20 +177,20 @@ public class BootJavaHoverProvider implements HoverHandler { annotation = (Annotation) node; ITypeBinding type = annotation.resolveTypeBinding(); if (type != null) { - String qualifiedName = type.getQualifiedName(); - if (qualifiedName != null) { - HoverProvider provider = this.hoverProviders.get(qualifiedName); - if (provider != null) { - SpringBootApp[] runningApps = getRunningSpringApps(project); - if (runningApps.length>0) { - if (hasActuatorDependency(project)) { - return provider.provideHover(node, annotation, type, offset, doc, project, runningApps); - } else { - DocumentRegion region = ASTUtils.nameRegion(doc, annotation); - if (region.containsOffset(offset)) { - return actuatorWarning(project); - } - } + SpringBootApp[] runningApps = getRunningSpringApps(project); + if (runningApps.length>0) { + for (HoverProvider provider : this.hoverProviders.get(type)) { + Hover hover = provider.provideHover(node, annotation, type, offset, doc, project, runningApps); + if (hover!=null) { + //TODO: compose multiple hovers somehow instead of just returning the first one? + return hover; + } + } + //Only reaching here if we didn't get a hover. + if (!hasActuatorDependency(project)) { + DocumentRegion region = ASTUtils.nameRegion(doc, annotation); + if (region.containsOffset(offset)) { + return actuatorWarning(project); } } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java index 9bb529613..2b06e46ab 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java @@ -52,23 +52,29 @@ public class ActiveProfilesProvider implements HoverProvider { if (runningApps.length>0) { StringBuilder markdown = new StringBuilder(); markdown.append("**Active Profiles**\n\n"); + boolean hasInterestingApp = false; for (SpringBootApp app : runningApps) { List profiles = app.getActiveProfiles(); if (profiles==null) { markdown.append(niceAppName(app)+" : _Unknown_\n\n"); - } else if (profiles.isEmpty()) { - markdown.append(niceAppName(app)+" : _None_\n\n"); } else { - markdown.append(niceAppName(app)+" :\n"); - for (String profile : profiles) { - markdown.append("- "+profile+"\n"); + hasInterestingApp = true; + if (profiles.isEmpty()) { + markdown.append(niceAppName(app)+" : _None_\n\n"); + } else { + markdown.append(niceAppName(app)+" :\n"); + for (String profile : profiles) { + markdown.append("- "+profile+"\n"); + } + markdown.append("\n"); } - markdown.append("\n"); } } - return new Hover( - ImmutableList.of(Either.forLeft(markdown.toString())) - ); + if (hasInterestingApp) { + return new Hover( + ImmutableList.of(Either.forLeft(markdown.toString())) + ); + } } return null; } diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java index a1039fa4d..1048b8da1 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java @@ -300,7 +300,7 @@ public class AutowiredHoverProviderTest { ); editor.assertHighlights("@Component", "@Autowired", "@Autowired"); for (int i = 1; i <= 2; i++) { - editor.assertHoverContains("@Autowired", 1, + editor.assertHoverContains("@Autowired", i, "Bean [id: defaultFoo, type: `com.example.FooImplementation`] got autowired with:\n" + "\n" + "- Bean: otherBean \n" + @@ -351,15 +351,16 @@ public class AutowiredHoverProviderTest { " \n" + "}" ); - editor.assertHighlights(/* not yet: "@Controller",*/ "@Autowired"); - for (int i = 1; i <= 2; i++) { - editor.assertHoverContains("@Autowired", 1, - "Bean [id: myController, type: `com.example.MyController`] got autowired with:\n" + - "\n" + - "- Bean: restTemplate \n" + - " Type: `org.springframework.web.client.RestTemplate`"); - } - + editor.assertHighlights("@Controller", "@Autowired"); + editor.assertHoverContains("@Autowired", + "Bean [id: myController, type: `com.example.MyController`] got autowired with:\n" + + "\n" + + "- Bean: restTemplate \n" + + " Type: `org.springframework.web.client.RestTemplate`" + ); + editor.assertHoverContains("@Controller", + "**Injection report for Bean [id: myController, type: `com.example.MyController`]**" + ); } } diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ActiveProfilesHoverTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ActiveProfilesHoverTest.java index ff6fbea69..f244d3dfe 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ActiveProfilesHoverTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ActiveProfilesHoverTest.java @@ -78,6 +78,7 @@ public class ActiveProfilesHoverTest { public void testActiveProfileHover_Unknown() throws Exception { //Sometimes its not possible to determine active profiles for an app (e.g. no actuator dependency). //Make sure we show something sensible + harness.useProject(projects.mavenProject("no-actuator-boot-15-web-app")); mockAppProvider.builder() .isSpringBootApp(true) @@ -98,8 +99,8 @@ public class ActiveProfilesHoverTest { "\n" + "}" ); - editor.assertHoverContains("@Profile", "Process [PID=22022, name=`foo.bar.RunningApp`] : _Unknown_"); - editor.assertHighlights("@Profile"); + editor.assertHoverContains("@Profile", "Consider adding `spring-boot-actuator` as a dependency"); + editor.assertHighlights(/*NONE*/); } @Test