diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java index fb1fbc27f..52f706527 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java @@ -30,10 +30,10 @@ public class RequestMappingsParser20 { .getJSONObject(contextId) .getJSONObject("mappings"); JSONArray rmArray = null; - if (mappings.has("dispatcherServlets")) { + if (mappings.has("dispatcherServlets") && mappings.getJSONObject("dispatcherServlets").has("dispatcherServlet")) { // Regular Web starter endpoints RMs JMX beans format rmArray = mappings.getJSONObject("dispatcherServlets").getJSONArray("dispatcherServlet"); - } else if (mappings.has("dispatcherHandlers")) { + } else if (mappings.has("dispatcherHandlers") && mappings.getJSONObject("dispatcherHandlers").has("webHandler")) { // WebFlux endpoints RMs JMX bean format rmArray = mappings.getJSONObject("dispatcherHandlers").getJSONArray("webHandler"); } diff --git a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java index 5d9c4e29d..424d47285 100644 --- a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java @@ -89,4 +89,45 @@ public class Boot2xRequestMappingsTest { .collect(Collectors.toSet()) ); } + + @Test + public void testWebEurekaRms() throws Exception { + String json = IOUtils.toString(Boot2xRequestMappingsTest.class.getResourceAsStream("/live-rm-beans/rms-boot2-web-eureka.json")); + Collection rms = RequestMappingsParser20.parse(new JSONObject(json)); + assertEquals(9, rms.size()); + + ImmutableSet expected = ImmutableSet.of( + "/error", + "/actuator", + "/actuator/health", + "/actuator/info", + "/welcome" + ); + assertEquals(expected, + rms.stream() + .flatMap(rm -> Arrays.stream(rm.getSplitPath())) + .collect(Collectors.toSet()) + ); + + } + + @Test + public void testWebFluxEurekaRms() throws Exception { + String json = IOUtils.toString(Boot2xRequestMappingsTest.class.getResourceAsStream("/live-rm-beans/rms-boot2-webflux-eureka.json")); + Collection rms = RequestMappingsParser20.parse(new JSONObject(json)); + assertEquals(6, rms.size()); + + ImmutableSet expected = ImmutableSet.of( + "/actuator", + "/actuator/health", + "/actuator/info", + "/welcome" + ); + assertEquals(expected, + rms.stream() + .flatMap(rm -> Arrays.stream(rm.getSplitPath())) + .collect(Collectors.toSet()) + ); + + } } diff --git a/headless-services/commons/commons-boot-app-cli/src/test/resources/live-rm-beans/rms-boot2-web-eureka.json b/headless-services/commons/commons-boot-app-cli/src/test/resources/live-rm-beans/rms-boot2-web-eureka.json new file mode 100644 index 000000000..a69ce71c4 --- /dev/null +++ b/headless-services/commons/commons-boot-app-cli/src/test/resources/live-rm-beans/rms-boot2-web-eureka.json @@ -0,0 +1,270 @@ +{ + "contexts": { + "welcome-messages-1": { + "mappings": { + "dispatcherServlets": { + "dispatcherServlet": [ + { + "handler": "ResourceHttpRequestHandler [locations=[class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/], class path resource []], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@22fd98a9]]", + "predicate": "/**/favicon.ico" + }, + { + "handler": "public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map)", + "predicate": "{[/actuator/health],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [ + "GET" + ], + "patterns": [ + "/actuator/health" + ], + "produces": [ + { + "negated": false, + "mediaType": "application/vnd.spring-boot.actuator.v2+json" + }, + { + "negated": false, + "mediaType": "application/json" + } + ], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "handle", + "className": "org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler", + "descriptor": "(Ljavax/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;" + } + } + }, + { + "handler": "public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map)", + "predicate": "{[/actuator/info],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [ + "GET" + ], + "patterns": [ + "/actuator/info" + ], + "produces": [ + { + "negated": false, + "mediaType": "application/vnd.spring-boot.actuator.v2+json" + }, + { + "negated": false, + "mediaType": "application/json" + } + ], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "handle", + "className": "org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler", + "descriptor": "(Ljavax/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;" + } + } + }, + { + "handler": "protected java.util.Map> org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.links(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)", + "predicate": "{[/actuator],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [ + "GET" + ], + "patterns": [ + "/actuator" + ], + "produces": [ + { + "negated": false, + "mediaType": "application/vnd.spring-boot.actuator.v2+json" + }, + { + "negated": false, + "mediaType": "application/json" + } + ], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "links", + "className": "org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping", + "descriptor": "(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Ljava/util/Map;" + } + } + }, + { + "handler": "public com.example.demo.Greeting com.example.demo.WelcomeMessageServiceApplication.greeting()", + "predicate": "{[/welcome],methods=[GET]}", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [ + "GET" + ], + "patterns": [ + "/welcome" + ], + "produces": [], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "greeting", + "className": "com.example.demo.WelcomeMessageServiceApplication", + "descriptor": "()Lcom/example/demo/Greeting;" + } + } + }, + { + "handler": "public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)", + "predicate": "{[/error]}", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [], + "patterns": [ + "/error" + ], + "produces": [], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "error", + "className": "org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController", + "descriptor": "(Ljavax/servlet/http/HttpServletRequest;)Lorg/springframework/http/ResponseEntity;" + } + } + }, + { + "handler": "public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)", + "predicate": "{[/error],produces=[text/html]}", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [], + "patterns": [ + "/error" + ], + "produces": [ + { + "negated": false, + "mediaType": "text/html" + } + ], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "errorHtml", + "className": "org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController", + "descriptor": "(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lorg/springframework/web/servlet/ModelAndView;" + } + } + }, + { + "handler": "ResourceHttpRequestHandler [locations=[class path resource [META-INF/resources/webjars/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@474c937f]]", + "predicate": "/webjars/**" + }, + { + "handler": "ResourceHttpRequestHandler [locations=[class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@6a7b18f7]]", + "predicate": "/**" + } + ] + }, + "servletFilters": [ + { + "name": "webMvcMetricsFilter", + "className": "org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter", + "urlPatternMappings": [ + "/*" + ], + "servletNameMappings": [] + }, + { + "name": "requestContextFilter", + "className": "org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter", + "urlPatternMappings": [ + "/*" + ], + "servletNameMappings": [] + }, + { + "name": "Tomcat WebSocket (JSR356) Filter", + "className": "org.apache.tomcat.websocket.server.WsFilter", + "urlPatternMappings": [ + "/*" + ], + "servletNameMappings": [] + }, + { + "name": "httpPutFormContentFilter", + "className": "org.springframework.boot.web.servlet.filter.OrderedHttpPutFormContentFilter", + "urlPatternMappings": [ + "/*" + ], + "servletNameMappings": [] + }, + { + "name": "hiddenHttpMethodFilter", + "className": "org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter", + "urlPatternMappings": [ + "/*" + ], + "servletNameMappings": [] + }, + { + "name": "characterEncodingFilter", + "className": "org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter", + "urlPatternMappings": [ + "/*" + ], + "servletNameMappings": [] + }, + { + "name": "httpTraceFilter", + "className": "org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter", + "urlPatternMappings": [ + "/*" + ], + "servletNameMappings": [] + } + ], + "servlets": [ + { + "mappings": [], + "name": "default", + "className": "org.apache.catalina.servlets.DefaultServlet" + }, + { + "mappings": [ + "/" + ], + "name": "dispatcherServlet", + "className": "org.springframework.web.servlet.DispatcherServlet" + } + ] + }, + "parentId": "welcome-messages-1" + }, + "bootstrap": { + "mappings": { + "dispatcherServlets": {}, + "servletFilters": [], + "servlets": [] + } + } + } +} \ No newline at end of file diff --git a/headless-services/commons/commons-boot-app-cli/src/test/resources/live-rm-beans/rms-boot2-webflux-eureka.json b/headless-services/commons/commons-boot-app-cli/src/test/resources/live-rm-beans/rms-boot2-webflux-eureka.json new file mode 100644 index 000000000..50419fe7f --- /dev/null +++ b/headless-services/commons/commons-boot-app-cli/src/test/resources/live-rm-beans/rms-boot2-webflux-eureka.json @@ -0,0 +1,145 @@ +{ + "contexts": { + "welcome-messages-1": { + "mappings": { + "dispatcherHandlers": { + "webHandler": [ + { + "predicate": "{[/actuator/health],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}", + "handler": "public org.reactivestreams.Publisher> org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping$ReadOperationHandler.handle(org.springframework.web.server.ServerWebExchange)", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [ + "GET" + ], + "patterns": [ + "/actuator/health" + ], + "produces": [ + { + "negated": false, + "mediaType": "application/vnd.spring-boot.actuator.v2+json" + }, + { + "negated": false, + "mediaType": "application/json" + } + ], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "handle", + "className": "org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping.ReadOperationHandler", + "descriptor": "(Lorg/springframework/web/server/ServerWebExchange;)Lorg/reactivestreams/Publisher;" + } + } + }, + { + "predicate": "{[/actuator/info],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}", + "handler": "public org.reactivestreams.Publisher> org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping$ReadOperationHandler.handle(org.springframework.web.server.ServerWebExchange)", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [ + "GET" + ], + "patterns": [ + "/actuator/info" + ], + "produces": [ + { + "negated": false, + "mediaType": "application/vnd.spring-boot.actuator.v2+json" + }, + { + "negated": false, + "mediaType": "application/json" + } + ], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "handle", + "className": "org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping.ReadOperationHandler", + "descriptor": "(Lorg/springframework/web/server/ServerWebExchange;)Lorg/reactivestreams/Publisher;" + } + } + }, + { + "predicate": "{[/actuator],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}", + "handler": "protected java.util.Map> org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping.links(org.springframework.web.server.ServerWebExchange)", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [ + "GET" + ], + "patterns": [ + "/actuator" + ], + "produces": [ + { + "negated": false, + "mediaType": "application/vnd.spring-boot.actuator.v2+json" + }, + { + "negated": false, + "mediaType": "application/json" + } + ], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "links", + "className": "org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping", + "descriptor": "(Lorg/springframework/web/server/ServerWebExchange;)Ljava/util/Map;" + } + } + }, + { + "predicate": "{[/welcome],methods=[GET]}", + "handler": "public com.example.demo.Greeting com.example.demo.WelcomeMessageServiceApplication.greeting()", + "details": { + "requestMappingConditions": { + "headers": [], + "methods": [ + "GET" + ], + "patterns": [ + "/welcome" + ], + "produces": [], + "params": [], + "consumes": [] + }, + "handlerMethod": { + "name": "greeting", + "className": "com.example.demo.WelcomeMessageServiceApplication", + "descriptor": "()Lcom/example/demo/Greeting;" + } + } + }, + { + "predicate": "/webjars/**", + "handler": "ResourceWebHandler [locations=[class path resource [META-INF/resources/webjars/]], resolvers=[org.springframework.web.reactive.resource.PathResourceResolver@7030aee8]]" + }, + { + "predicate": "/**", + "handler": "ResourceWebHandler [locations=[class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/]], resolvers=[org.springframework.web.reactive.resource.PathResourceResolver@2c2efafe]]" + } + ] + } + }, + "parentId": "welcome-messages-1" + }, + "bootstrap": { + "mappings": { + "dispatcherHandlers": {} + } + } + } +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguagServerBootApp.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguagServerBootApp.java index ff163c725..fda7a6c49 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguagServerBootApp.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguagServerBootApp.java @@ -83,7 +83,7 @@ public class BootLanguagServerBootApp { @Bean InitializingBean initializeValueProviders(ValueProviderRegistry r, @Qualifier("adHocProperties") ProjectBasedPropertyIndexProvider adHocProperties) { return () -> { - r.def("logger-name", new LoggerNameProvider(adHocProperties).FACTORY); + r.def("logger-name", LoggerNameProvider.factory(adHocProperties)); r.def("class-reference", ClassReferenceProvider.FACTORY); }; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/configurationmetadata/ConfigurationMetadataItem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/configurationmetadata/ConfigurationMetadataItem.java index 6167202a3..8ca55d5af 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/configurationmetadata/ConfigurationMetadataItem.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/configurationmetadata/ConfigurationMetadataItem.java @@ -56,5 +56,4 @@ class ConfigurationMetadataItem extends ConfigurationMetadataProperty { public void setSourceMethod(String sourceMethod) { this.sourceMethod = sourceMethod; } - } 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 3f293b3fc..8bb2b3757 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 @@ -71,32 +71,30 @@ public class AutowiredHoverProvider implements HoverProvider { } @Override - public Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, + SpringBootApp[] runningApps) { + ImmutableList.Builder builder = ImmutableList.builder(); if (runningApps.length > 0) { LiveBean definedBean = getDefinedBeanForTypeDeclaration(ASTUtils.findDeclaringType(annotation)); - // Annotation is MarkerNode, parent is some field, method, variable declaration node. + // 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); + for (SpringBootApp app : runningApps) { + List relevantBeans = getRelevantAutowiredBeans(project, declarationNode, app, + definedBean); + if (!relevantBeans.isEmpty()) { + builder.addAll(LiveHoverUtils.createCodeLensesForBeans(hoverRange, relevantBeans, + BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, + INLINE_BEANS_STRING_SEPARATOR)); + } + } } catch (BadLocationException e) { log.error("", e); } } - return null; - } - - 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 LiveHoverUtils.createCodeLensesForBeans(range, relevantBeans, BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR); - } - } - } - return null; + return builder.build(); } @Override @@ -125,16 +123,12 @@ public class AutowiredHoverProvider implements HoverProvider { StringBuilder hover = new StringBuilder(); - boolean hasContent = false; - for (SpringBootApp app : runningApps) { List autowiredBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean); if (!autowiredBeans.isEmpty()) { - if (!hasContent) { - hasContent = true; - } else { + if (hover.length() > 0) { hover.append(" \n \n"); } createHoverContentForBeans(sourceLinks, project, hover, autowiredBeans); @@ -145,7 +139,7 @@ public class AutowiredHoverProvider implements HoverProvider { } } - if (hasContent) { + if (hover.length() > 0) { return new Hover(ImmutableList.of(Either.forLeft(hover.toString()))); } } @@ -189,7 +183,7 @@ public class AutowiredHoverProvider implements HoverProvider { } @SuppressWarnings("unchecked") - private static List findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection beans) { + public static List findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection beans) { if (declarationNode instanceof MethodDeclaration) { MethodDeclaration methodDeclaration = (MethodDeclaration)declarationNode; return ((List)methodDeclaration.parameters()).stream() @@ -325,16 +319,54 @@ public class AutowiredHoverProvider implements HoverProvider { } @Override - public Collection getLiveHintCodeLenses(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); + public Hover provideMethodParameterHover(SingleVariableDeclaration parameter, int offset, TextDocument doc, + IJavaProject project, SpringBootApp[] runningApps) { + MethodDeclaration method = (MethodDeclaration) parameter.getParent(); + LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(method); + Hover hover = provideHover(definedBean, parameter, offset, doc, project, runningApps); + if (hover != null) { + SimpleName name = parameter.getName(); + try { + hover.setRange(doc.toRange(name.getStartPosition(), name.getLength())); + } catch (BadLocationException e) { + log.error("", e); + } } - return null; + return hover; + } + + @Override + public Collection getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, + TextDocument doc, SpringBootApp[] runningApps) { + ImmutableList.Builder builder = ImmutableList.builder(); + LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(methodDeclaration); + if (definedBean != null) { + try { + Range hoverRange = doc.toRange(methodDeclaration.getName().getStartPosition(), + methodDeclaration.getName().getLength()); + + for (SpringBootApp app : runningApps) { + List relevantBeans = getRelevantAutowiredBeans(project, methodDeclaration, app, + definedBean); + if (!relevantBeans.isEmpty()) { + + // CodeLens for the method + builder.addAll(LiveHoverUtils.createCodeLensesForBeans(hoverRange, relevantBeans, + BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, + INLINE_BEANS_STRING_SEPARATOR)); + + // CodeLenses for the method parameters. Only ranges just to provide a highlight + // for the hover + builder.addAll(LiveHoverUtils.createCodeLensForMethodParameters(app, project, methodDeclaration, doc, relevantBeans)); + + } + } + } catch (BadLocationException e) { + log.error("", e); + } + + } + return builder.build(); } private LiveBean getDefinedBeanForImplicitAutowiredConstructor(MethodDeclaration methodDeclaration) { 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 584c451bc..8acf4c699 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 @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.handlers; +import java.net.URI; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Optional; @@ -24,6 +25,7 @@ import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Hover; @@ -101,7 +103,7 @@ public class BootJavaHoverProvider implements HoverHandler { if (!project.isPresent()) return new CodeLens[0]; if (!hasActuatorDependency(project.get())) return new CodeLens[0]; - return server.getCompilationUnitCache().withCompilationUnit(document, cu -> { + return server.getCompilationUnitCache().withCompilationUnit(project.get(), URI.create(document.getUri()), cu -> { Collection result = new LinkedHashSet<>(); try { if (cu != null) { @@ -210,7 +212,7 @@ public class BootJavaHoverProvider implements HoverHandler { private Hover provideHover(TextDocument document, int offset) throws Exception { IJavaProject project = getProject(document).orElse(null); if (project != null) { - return server.getCompilationUnitCache().withCompilationUnit(document, cu -> { + return server.getCompilationUnitCache().withCompilationUnit(project, URI.create(document.getUri()), cu -> { ASTNode node = NodeFinder.perform(cu, offset, 0); if (node != null) { return provideHover(node, offset, document, project); @@ -239,6 +241,23 @@ public class BootJavaHoverProvider implements HoverHandler { return provideHoverForTypeDeclaration(node, (TypeDeclaration) parent, offset, doc, project); } else if (parent instanceof MethodDeclaration) { return provideHoverForMethodDeclaration((MethodDeclaration) parent, offset, doc, project); + } else if (parent instanceof SingleVariableDeclaration && parent.getParent() instanceof MethodDeclaration) { + return provideHoverForMethodParameter((SingleVariableDeclaration) parent, offset, doc, project); + } + } + return null; + } + + private Hover provideHoverForMethodParameter(SingleVariableDeclaration parameter, int offset, TextDocument doc, + IJavaProject project) { + SpringBootApp[] runningApps = getRunningSpringApps(project); + + if (runningApps.length > 0) { + for (HoverProvider provider : this.hoverProviders.getAll()) { + Hover hover = provider.provideMethodParameterHover(parameter, offset, doc, project, runningApps); + if (hover != null) { + 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 2bbd04600..5b9b9b25c 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 @@ -16,6 +16,7 @@ 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.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Hover; @@ -31,13 +32,19 @@ public interface HoverProvider { 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; } + default Hover provideMethodParameterHover(SingleVariableDeclaration parameter, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { + return null; + } + default Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, 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/AbstractInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java index a5bab7e44..f272b7e11 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 @@ -10,7 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.livehover; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -65,7 +64,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) { Optional nameRange = ASTUtils.nameRange(doc, annotation); if (nameRange.isPresent()) { - List codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get(), annotation); + List codeLenses = assembleCodeLenses(project, runningApps, definedBean, doc, nameRange.get(), annotation); return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses; } } @@ -97,31 +96,38 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider return null; } - protected List assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, Range range, ASTNode astNode) { - List codeLensList = new ArrayList<>(); - for (SpringBootApp app : runningApps) { + protected List assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, + TextDocument doc, Range range, ASTNode node) { + for (SpringBootApp app : runningApps) { - List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); + List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); - if (!relevantBeans.isEmpty()) { - List injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans); - ImmutableList.Builder builder = ImmutableList.builder(); - if (!injectedBeans.isEmpty()) { - // Break out of the loop. Just look for the first app with injected into beans - List injectedCodeLenses = LiveHoverUtils.createCodeLensesForBeans(range, injectedBeans, BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR); - builder.addAll(injectedCodeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : injectedCodeLenses); - } - - // Wired beans code lenses - List wiredBeans = findWiredBeans(project, app, relevantBeans, astNode); - builder.addAll(LiveHoverUtils.createCodeLensesForBeans(range, wiredBeans, - AutowiredHoverProvider.BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, - INLINE_BEANS_STRING_SEPARATOR)); - - return builder.build(); + if (!relevantBeans.isEmpty()) { + List injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans); + ImmutableList.Builder builder = ImmutableList.builder(); + if (!injectedBeans.isEmpty()) { + // Break out of the loop. Just look for the first app with injected into beans + List injectedCodeLenses = LiveHoverUtils.createCodeLensesForBeans(range, injectedBeans, + BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR); + builder.addAll( + injectedCodeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : injectedCodeLenses); } + + // Wired beans code lenses + List wiredBeans = findWiredBeans(project, app, relevantBeans, node); + builder.addAll(assembleCodeLenseForAutowired(wiredBeans, project, app, doc, range, node)); + + List codeLenses = builder.build(); + return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : codeLenses; + } } - return codeLensList; + return ImmutableList.of(); + } + + protected List assembleCodeLenseForAutowired(List wiredBeans, IJavaProject project, SpringBootApp app, TextDocument doc, Range nameRange, ASTNode astNode) { + return LiveHoverUtils.createCodeLensesForBeans(nameRange, wiredBeans, + AutowiredHoverProvider.BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, + INLINE_BEANS_STRING_SEPARATOR); } protected List findWiredBeans(IJavaProject project, SpringBootApp app, List relevantBeans, ASTNode astNode) { @@ -131,8 +137,6 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, ASTNode astNode) { StringBuilder hover = new StringBuilder(); - boolean hasContent = false; - for (SpringBootApp app : runningApps) { List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); @@ -140,9 +144,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider if (!relevantBeans.isEmpty()) { List injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans); - if (!hasContent) { - hasContent = true; - } else { + if (hover.length() > 0) { hover.append(" \n \n"); } @@ -167,7 +169,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider } } - if (hasContent) { + if (hover.length() > 0) { return new Hover(ImmutableList.of(Either.forLeft(hover.toString()))); } else { 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 c13ab5b42..6bfece4cd 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 @@ -16,7 +16,16 @@ 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.SingleVariableDeclaration; +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.Range; +import org.gradle.internal.impldep.com.google.common.collect.ImmutableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider; import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.java.utils.ASTUtils; @@ -24,9 +33,12 @@ import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.Optionals; +import org.springframework.ide.vscode.commons.util.text.TextDocument; public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProvider { + private static final Logger log = LoggerFactory.getLogger(BeanInjectedIntoHoverProvider.class); + public BeanInjectedIntoHoverProvider(SourceLinks sourceLinks) { super(sourceLinks); } @@ -75,12 +87,79 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv @Override protected List findWiredBeans(IJavaProject project, SpringBootApp app, List relevantBeans, ASTNode astNode) { if (astNode instanceof Annotation) { + // @Bean annotation case MethodDeclaration beanMethod = ASTUtils.getAnnotatedMethod((Annotation) astNode); if (beanMethod != null) { return AutowiredHoverProvider.getRelevantAutowiredBeans(project, beanMethod, app, relevantBeans); } + } else if (astNode instanceof SingleVariableDeclaration) { + // Bean method parameter case + return AutowiredHoverProvider.getRelevantAutowiredBeans(project, astNode, app, relevantBeans); } return Collections.emptyList(); } + @Override + protected List assembleCodeLenseForAutowired(List wiredBeans, IJavaProject project, + SpringBootApp app, TextDocument doc, Range nameRange, ASTNode astNode) { + ImmutableList.Builder builder = ImmutableList.builder(); + + // Code lens for the @Bean annotation + builder.addAll(super.assembleCodeLenseForAutowired(wiredBeans, project, app, doc, nameRange, astNode)); + + if (astNode instanceof Annotation) { + // Add code lenses for method parameters + MethodDeclaration beanMethod = ASTUtils.getAnnotatedMethod((Annotation) astNode); + if (beanMethod != null) { + builder.addAll(LiveHoverUtils.createCodeLensForMethodParameters(app, project, beanMethod, doc, wiredBeans)); + } + } + + return builder.build(); + } + + @Override + public Hover provideMethodParameterHover(SingleVariableDeclaration parameter, int offset, TextDocument doc, + IJavaProject project, SpringBootApp[] runningApps) { + try { + if (runningApps.length > 0) { + Range range = ASTUtils.nodeRegion(doc, parameter.getName()).asRange(); + MethodDeclaration method = (MethodDeclaration) parameter.getParent(); + Annotation beanAnnotation = getBeanAnnotation(method); + if (beanAnnotation != null) { + LiveBean definedBean = getDefinedBean(beanAnnotation); + if (definedBean != null) { + Hover hover = assembleHover(project, runningApps, definedBean, parameter); + if (hover != null) { + hover.setRange(range); + } + return hover; + } + + } + } + } catch (Exception e) { + log.error("", e); + } + return null; + } + + private static Annotation getBeanAnnotation(MethodDeclaration method) { + List modifiers = method.modifiers(); + for (Object modifier : modifiers) { + if (modifier instanceof Annotation) { + Annotation annotation = (Annotation) modifier; + ITypeBinding typeBinding = annotation.resolveTypeBinding(); + if (typeBinding != null) { + String fqName = typeBinding.getQualifiedName(); + if (Annotations.BEAN.equals(fqName)) { + return annotation; + } + } + } + } + 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 76d555d63..55d041e38 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 @@ -111,7 +111,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) { Optional nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange()); if (nameRange.isPresent()) { - List codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get(), typeDeclaration); + List codeLenses = assembleCodeLenses(project, runningApps, definedBean, doc, nameRange.get(), typeDeclaration); return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses; } } @@ -146,8 +146,6 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP return null; } - - @Override protected List findWiredBeans(IJavaProject project, SpringBootApp app, List relevantBeans, ASTNode astNode) { 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 75c0ba6e3..9873ee7de 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 @@ -17,22 +17,32 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Range; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider; import org.springframework.ide.vscode.boot.java.links.SourceLinks; +import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.boot.java.utils.SpringResource; 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.BadLocationException; import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.util.StringUtil; +import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; public class LiveHoverUtils { + private static final Logger log = LoggerFactory.getLogger(LiveHoverUtils.class); + public static final LiveBean CANT_MATCH_PROPER_BEAN = LiveBean.builder().id("UNKNOWN").build(); public static String showBean(LiveBean bean) { @@ -161,7 +171,7 @@ public class LiveHoverUtils { try { return niceAppName(app.getProcessID(), app.getProcessName()); } catch (Exception e) { - e.printStackTrace(); + log.error("", e); return app.toString(); } } @@ -189,6 +199,27 @@ public class LiveHoverUtils { } + @SuppressWarnings("unchecked") + public static List createCodeLensForMethodParameters(SpringBootApp app, IJavaProject project, MethodDeclaration method, TextDocument doc, List wiredBeans) { + ImmutableList.Builder builder = ImmutableList.builder(); + method.parameters().forEach(p -> { + if (p instanceof SingleVariableDeclaration) { + SingleVariableDeclaration parameter = (SingleVariableDeclaration) p; + List parameterMatchingBean = AutowiredHoverProvider.findAutowiredBeans(project, parameter, wiredBeans); + if (parameterMatchingBean.size() == 0) { + log.warn("No Live Bean matching parameter `" + parameter.getName().getIdentifier() + " for method " + method); + } else { + try { + builder.add(new CodeLens(ASTUtils.nodeRegion(doc, parameter.getName()).asRange())); + } catch (BadLocationException e) { + // ignore + } + } + } + }); + return builder.build(); + } + public static StringBuilder createBeansTitlePlainText(Collection beans, String prefix, int maxInlineBeansStringLength, String beansSeparator) { StringBuilder sb = new StringBuilder(prefix); if (LiveHoverUtils.doBeansFitInline(beans, maxInlineBeansStringLength, beansSeparator)) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java index 80241e29b..fb8bfb194 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java @@ -39,18 +39,23 @@ import reactor.util.function.Tuples; public class LoggerNameProvider extends CachingValueProvider { private static final String LOGGING_GROUPS_PREFIX = "logging.group."; - private final ProjectBasedPropertyIndexProvider adhocProperties; + private final boolean includeGroups; - public LoggerNameProvider(ProjectBasedPropertyIndexProvider adhocProperties) { + public LoggerNameProvider(ProjectBasedPropertyIndexProvider adhocProperties, boolean includeGroups) { this.adhocProperties = adhocProperties; + this.includeGroups = includeGroups; } - public final Function, ValueProviderStrategy> FACTORY = (params) -> this; + public static final Function, ValueProviderStrategy> factory(ProjectBasedPropertyIndexProvider adhocProperties) { + return (params) -> { + return new LoggerNameProvider(adhocProperties, (boolean) params.getOrDefault("group", true)); + }; + } Collection loggerGroupNames(IJavaProject jp) { Builder builder = ImmutableSet.builder(); - if (adhocProperties!=null) { + if (adhocProperties!=null && includeGroups) { SortedMap index = adhocProperties.getIndex(jp).getTreeMap(); index = index.subMap(LOGGING_GROUPS_PREFIX, LOGGING_GROUPS_PREFIX+Character.MAX_VALUE); for (String prop : index.keySet()) { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java index ae7c4b27c..a8fab984d 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java @@ -21,6 +21,7 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.TextDocumentIdentifier; @@ -136,6 +137,14 @@ public abstract class AbstractPropsEditorTest { assertCompletions(text /*NONE*/); } + public void assertNoCompletionWithLabel(String textBefore, String expectLabel) throws Exception { + Editor editor = newEditor(textBefore); + List completions = editor.getCompletions().stream().filter(c -> c.getLabel().equals(expectLabel)).collect(Collectors.toList()); + if (!completions.isEmpty()) { + fail("Expecting no completions with label '"+expectLabel+"', but found some"); + } + } + /** * Checks that completions contains a completion with a given display string (and check that * it applies as expected). 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 8f5d18559..bff49b62b 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 @@ -507,7 +507,7 @@ public class AutowiredHoverProviderTest { "}\n" ); - editor.assertHighlights("@Component", "SomeComponent"); + editor.assertHighlights("@Component", "SomeComponent", "depA", "depB"); editor.assertTrimmedHover("SomeComponent", 2, "**← `DependencyA` `DependencyB`**\n" + @@ -519,6 +519,25 @@ public class AutowiredHoverProviderTest { "Bean id: `someComponent` \n" + "Process [PID=111, name=`the-app`]\n" ); + + editor.assertTrimmedHover("depA", 2, + "**← `DependencyA`**\n" + + "- Bean: `dependencyA` \n" + + " Type: `com.example.DependencyA`\n" + + " \n" + + "Bean id: `someComponent` \n" + + "Process [PID=111, name=`the-app`]\n" + ); + + editor.assertTrimmedHover("depB", 2, + "**← `DependencyB`**\n" + + "- Bean: `dependencyB` \n" + + " Type: `com.example.DependencyB`\n" + + " \n" + + "Bean id: `someComponent` \n" + + "Process [PID=111, name=`the-app`]\n" + ); + } @Test @@ -718,7 +737,7 @@ public class AutowiredHoverProviderTest { " private IDependency a;\n" + " private IDependency b;\n" + "\n" + - " public SomeComponent(@Qualifier(\"dependencyA\") IDependency a, @Qualifier(\"dependencyB\") IDependency b) {\n" + + " public SomeComponent(@Qualifier(\"dependencyA\") IDependency depA, @Qualifier(\"dependencyB\") IDependency depB) {\n" + " this.a = a;\n" + " this.b = b;\n" + " }\n" + @@ -726,7 +745,7 @@ public class AutowiredHoverProviderTest { "}\n" ); - editor.assertHighlights("@Component", "SomeComponent"); + editor.assertHighlights("@Component", "SomeComponent", "depA", "depB"); editor.assertTrimmedHover("SomeComponent", 2, "**← `DependencyA` `DependencyB`**\n" + "- Bean: `dependencyA` \n" + @@ -737,6 +756,25 @@ public class AutowiredHoverProviderTest { "Bean id: `someComponent` \n" + "Process [PID=111, name=`the-app`]\n" ); + + editor.assertTrimmedHover("depA", + "**← `DependencyA`**\n" + + "- Bean: `dependencyA` \n" + + " Type: `com.example.DependencyA`\n" + + " \n" + + "Bean id: `someComponent` \n" + + "Process [PID=111, name=`the-app`]\n" + ); + + editor.assertTrimmedHover("depB", + "**← `DependencyB`**\n" + + "- Bean: `dependencyB` \n" + + " Type: `com.example.DependencyB`\n" + + " \n" + + "Bean id: `someComponent` \n" + + "Process [PID=111, name=`the-app`]\n" + ); + } @Test 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 60da3571c..15bbfb07c 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 @@ -549,12 +549,12 @@ public class BeanInjectedIntoHoverProviderTest { "public class LocalConfig {\n" + " \n" + " @Bean(\"fooImplementation\")\n" + - " Foo someFoo(DependencyA a) {\n" + + " Foo someFoo(DependencyA depA) {\n" + " return new FooImplementation();\n" + " }\n" + "}" ); - editor.assertHighlights("@Bean"); + editor.assertHighlights("@Bean", "depA"); editor.assertTrimmedHover("@Bean", "**← `DependencyA`**\n" + "- Bean: `depA` \n" + @@ -563,6 +563,16 @@ public class BeanInjectedIntoHoverProviderTest { "Bean id: `fooImplementation` \n" + "Process [PID=111, name=`the-app`]" ); + + editor.assertTrimmedHover("depA", + "**← `DependencyA`**\n" + + "- Bean: `depA` \n" + + " Type: `hello.DependencyA`\n" + + " \n" + + "Bean id: `fooImplementation` \n" + + "Process [PID=111, name=`the-app`]" + ); + } @Test @@ -603,12 +613,12 @@ public class BeanInjectedIntoHoverProviderTest { "public class LocalConfig {\n" + " \n" + " @Bean(\"fooImplementation\")\n" + - " Foo someFoo(DependencyA a, DependencyB b) {\n" + + " Foo someFoo(DependencyA depA, DependencyB depB) {\n" + " return new FooImplementation();\n" + " }\n" + "}" ); - editor.assertHighlights("@Bean"); + editor.assertHighlights("@Bean", "depA", "depB"); editor.assertTrimmedHover("@Bean", "**← `DependencyA` `DependencyB`**\n" + "- Bean: `depA` \n" + @@ -619,6 +629,25 @@ public class BeanInjectedIntoHoverProviderTest { "Bean id: `fooImplementation` \n" + "Process [PID=111, name=`the-app`]" ); + + editor.assertTrimmedHover("depA", + "**← `DependencyA`**\n" + + "- Bean: `depA` \n" + + " Type: `hello.DependencyA`\n" + + " \n" + + "Bean id: `fooImplementation` \n" + + "Process [PID=111, name=`the-app`]" + ); + + editor.assertTrimmedHover("depB", + "**← `DependencyB`**\n" + + "- Bean: `depB` \n" + + " Type: `hello.DependencyB`\n" + + " \n" + + "Bean id: `fooImplementation` \n" + + "Process [PID=111, name=`the-app`]" + ); + } @Test @@ -664,7 +693,7 @@ public class BeanInjectedIntoHoverProviderTest { " }\n" + "}" ); - editor.assertHighlights("@Bean"); + editor.assertHighlights("@Bean", "deps"); editor.assertTrimmedHover("@Bean", "**← `DependencyA` `DependencyB`**\n" + "- Bean: `depA` \n" + @@ -675,7 +704,18 @@ public class BeanInjectedIntoHoverProviderTest { "Bean id: `fooImplementation` \n" + "Process [PID=111, name=`the-app`]" ); - } + + editor.assertTrimmedHover("deps", + "**← `DependencyA` `DependencyB`**\n" + + "- Bean: `depA` \n" + + " Type: `hello.DependencyA`\n" + + "- Bean: `depB` \n" + + " Type: `hello.DependencyB`\n" + + " \n" + + "Bean id: `fooImplementation` \n" + + "Process [PID=111, name=`the-app`]" + ); +} @Test public void beanWithQualifierWiring() throws Exception { @@ -715,7 +755,7 @@ public class BeanInjectedIntoHoverProviderTest { " }\n" + "}" ); - editor.assertHighlights("@Bean"); + editor.assertHighlights("@Bean", "deps"); editor.assertTrimmedHover("@Bean", "**← `DependencyB`**\n" + "- Bean: `depB` \n" + @@ -724,5 +764,15 @@ public class BeanInjectedIntoHoverProviderTest { "Bean id: `fooImplementation` \n" + "Process [PID=111, name=`the-app`]" ); + + editor.assertTrimmedHover("deps", + "**← `DependencyB`**\n" + + "- Bean: `depB` \n" + + " Type: `hello.DependencyB`\n" + + " \n" + + "Bean id: `fooImplementation` \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 8243defbb..6feea840e 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 @@ -501,7 +501,7 @@ public class ComponentInjectionsHoverProviderTest { " }\n" + "}\n" ); - editor.assertHighlights("@Component", "AutowiredClass"); + editor.assertHighlights("@Component", "AutowiredClass", "depA", "depB"); editor.assertTrimmedHover("@Component", "**← `DependencyA` `DependencyB`**\n" + "- Bean: `dependencyA` \n" + diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProviderTest.java index f6b2c4a54..1d1219708 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProviderTest.java @@ -30,6 +30,7 @@ import org.springframework.ide.vscode.boot.metadata.LoggerNameProvider; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; /** @@ -69,7 +70,7 @@ public class LoggerNameProviderTest { @Test public void directResults() throws Exception { - LoggerNameProvider p = new LoggerNameProvider(null); + LoggerNameProvider p = create(); String query = "jboss"; List directQueryResults = getResults(p, query); @@ -84,7 +85,7 @@ public class LoggerNameProviderTest { @Test public void cachedResults() throws Exception { - LoggerNameProvider p = new LoggerNameProvider(null); + LoggerNameProvider p = create(); for (int i = 0; i < 10; i++) { long startTime = System.currentTimeMillis(); String query = "jboss"; @@ -105,7 +106,7 @@ public class LoggerNameProviderTest { public void incrementalResults() throws Exception { String fullQuery = "jboss"; - CachingValueProvider p = new LoggerNameProvider(null); + CachingValueProvider p = create(); for (int i = 0; i <= fullQuery.length(); i++) { String query = fullQuery.substring(0, i); List results = getResults(p, query); @@ -120,6 +121,10 @@ public class LoggerNameProviderTest { } } + private LoggerNameProvider create() { + return (LoggerNameProvider) LoggerNameProvider.factory(null).apply(ImmutableMap.of()); + } + private void assertElementsAtLeast(List results, String[] expecteds) { Set actuals = ImmutableSet.copyOf(results); StringBuilder missing = new StringBuilder(); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java index c66cae6fb..940a853d3 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java @@ -1183,6 +1183,10 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { "logging.level.indexed=<*>" ); + //Check whether the added parameter to disable group name hints is obeyed: + assertNoCompletionWithLabel("logging.group.whatever=<*>", "foobar"); + assertNoCompletionWithLabel("logging.group.whatever=<*>", "user-defined"); + assertNoCompletionWithLabel("logging.group.whatever=<*>", "indexed"); } @Test public void userDefinedLoggingGroupsValueCompletions() throws Exception { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index c0aba935f..bbba03c60 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -296,6 +296,16 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { " group:\n"+ " whatever: stuff,com.example.demo<*>" ); + + //Check whether the added parameter to disable group name hints is obeyed: + assertNoCompletionWithLabel( + "logging:\n" + + " group:\n"+ + " whatever: <*>" + , + "foobar" + ); + } ///////////////////// ported tests from old STS code base ////////////////////////////////////////////////