diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java index a2c34fb5a..868b2dafd 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java @@ -28,8 +28,6 @@ import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.SymbolInformation; -import org.eclipse.lsp4j.SymbolKind; import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; @@ -48,9 +46,11 @@ public class RequestMappingSymbolProvider implements SymbolProvider { Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())); String[] path = getPath(node); String[] parentPath = getParentPath(node); - String[] method = getMethod(node); + String[] methods = getMethod(node); + String[] contentTypes = new String[0]; + String[] acceptTypes = new String[0]; - String methodStr = method == null || method.length == 0 ? "" : String.join(",", method); +// String methodStr = method == null || method.length == 0 ? "" : String.join(",", method); return (parentPath == null ? Stream.of("") : Arrays.stream(parentPath)).filter(Objects::nonNull) .flatMap(parent -> (path == null ? Stream.empty() : Arrays.stream(path)) @@ -62,8 +62,7 @@ public class RequestMappingSymbolProvider implements SymbolProvider { } return resultPath.startsWith("/") ? resultPath : "/" + resultPath; })) - .map(p -> "@" + p + (methodStr.isEmpty() ? "" : " -- " + methodStr)) - .map(symbolLabel -> new EnhancedSymbolInformation(new SymbolInformation(symbolLabel, SymbolKind.Interface, location), null)) + .map(p -> RouteUtils.createRouteSymbol(location, p, methods, contentTypes, acceptTypes, null)) .collect(Collectors.toList()); } catch (Exception e) { e.printStackTrace(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RouteUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RouteUtils.java new file mode 100644 index 000000000..52ea11dcc --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RouteUtils.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.requestmapping; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.SymbolKind; +import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; + +/** + * @author Martin Lippert + */ +public class RouteUtils { + + public static EnhancedSymbolInformation createRouteSymbol(Location location, String path, + String[] httpMethods, String[] contentTypes, String[] acceptTypes, Object enhancedInformation) { + + if (path != null && path.length() > 0) { + String label = "@" + (path.startsWith("/") ? path : ("/" + path)); + label += (httpMethods == null || httpMethods.length == 0 ? "" : " -- " + WebfluxUtils.getStringRep(httpMethods, string -> string)); + + return new EnhancedSymbolInformation(new SymbolInformation(label, SymbolKind.Interface, location), enhancedInformation); + } + else { + return null; + } + + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java index 7dce4b632..9f0ea3d65 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java @@ -28,8 +28,6 @@ import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.SymbolInformation; -import org.eclipse.lsp4j.SymbolKind; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.commons.util.BadLocationException; @@ -100,13 +98,12 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { if (path != null && path.length() > 0) { try { - Location location = new Location(doc.getUri(), doc.toRange(methodNameStart, node.getLength() - (methodNameStart - invocationStart))); - String label = "@" + (path.startsWith("/") ? path : ("/" + path)); - label += (httpMethods == null || httpMethods.length == 0 ? "" : " -- " + WebfluxUtils.getStringRep(httpMethods, string -> string)); + Location location = new Location(doc.getUri(), doc.toRange(methodNameStart, node.getLength() - (methodNameStart - invocationStart))); WebfluxHandlerInformation handler = extractHandlerInformation(node, path, httpMethods, contentTypes, acceptTypes); - - result.add(new EnhancedSymbolInformation(new SymbolInformation(label, SymbolKind.Interface, location), handler)); + + result.add(RouteUtils.createRouteSymbol(location, path, httpMethods, contentTypes, acceptTypes, handler)); + } catch (BadLocationException e) { e.printStackTrace(); } @@ -159,7 +156,7 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { String methodName = methodBinding.getName(); if (WebfluxUtils.REQUEST_PREDICATE_METHOD_METHOD.equals(methodName)) { - return WebfluxUtils.extractStringLiteralArgument(methodInvocation); + return WebfluxUtils.extractQualifiedNameArgument(methodInvocation); } return null; }); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java index b8b11ba91..b51e4921e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java @@ -117,7 +117,7 @@ public class WebfluxUtils { StringBuilder result = new StringBuilder(valueConverter.apply(multipleTypes[0])); for (int i = 1; i < multipleTypes.length; i++) { - result.append(", "); + result.append(","); result.append(valueConverter.apply(multipleTypes[i])); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxCodeLensProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxCodeLensProviderTest.java index 27d91e82c..daf21949a 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxCodeLensProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxCodeLensProviderTest.java @@ -90,9 +90,28 @@ public class WebFluxCodeLensProviderTest { assertEquals(3, codeLenses.size()); + assertTrue(containsCodeLens(codeLenses, "GET /person/{id} - Accept: application/json", 9, 29, 9, 38)); + assertTrue(containsCodeLens(codeLenses, "POST / - Accept: application/json - Content-Type: application/json,application/pdf", 13, 29, 13, 41)); + assertTrue(containsCodeLens(codeLenses, "GET,HEAD /person - Accept: text/plain,application/json", 17, 29, 17, 39)); + } + + @Test + public void testRoutesCodeLensesNestedRoutes3() throws Exception { + harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-webflux-project/").toURI())); + File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-webflux-project/").toURI()); + + String docUri = directory.toPath().resolve("src/main/java/org/test/PersonHandler3.java").toUri().toString(); + TextDocumentInfo doc = harness.getOrReadFile(new File(new URI(docUri)), LanguageId.JAVA.toString()); + TextDocumentInfo openedDoc = harness.openDocument(doc); + + List codeLenses = harness.getCodeLenses(openedDoc); + + assertEquals(6, codeLenses.size()); +/* assertTrue(containsCodeLens(codeLenses, "GET /person/{id} - Accept: application/json", 9, 29, 9, 38)); assertTrue(containsCodeLens(codeLenses, "POST / - Accept: application/json - Content-Type: application/json, application/pdf", 13, 29, 13, 41)); assertTrue(containsCodeLens(codeLenses, "GET, HEAD /person - Accept: text/plain, application/json", 17, 29, 17, 39)); +*/ } private boolean containsCodeLens(List codeLenses, String commandTitle, int startLine, int startPosition, int endLine, int endPosition) { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java index 3acb1d4c5..1d7ae6bdc 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java @@ -153,7 +153,7 @@ public class WebFluxMappingSymbolProviderTest { assertEquals(5, symbols.size()); assertTrue(containsSymbol(symbols, "@/person/{id} -- GET", docUri, 29, 6, 29, 45)); assertTrue(containsSymbol(symbols, "@/ -- POST", docUri, 31, 6, 31, 117)); - assertTrue(containsSymbol(symbols, "@/person -- GET, HEAD", docUri, 30, 7, 30, 113)); + assertTrue(containsSymbol(symbols, "@/person -- GET,HEAD", docUri, 30, 7, 30, 113)); List addons = getAdditionalInformation(docUri); assertEquals(3, addons.size()); @@ -183,6 +183,51 @@ public class WebFluxMappingSymbolProviderTest { assertEquals("public Mono listPeople(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo3.getHandlerMethod()); } + @Test + public void testNestedRoutesMappingSymbols3() throws Exception { + harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-webflux-project/").toURI())); + File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-webflux-project/").toURI()); + + String docUri = directory.toPath().resolve("src/main/java/org/test/NestedRouter3.java").toUri().toString(); + List symbols = getSymbols(docUri); + assertEquals(8, symbols.size()); + + assertTrue(containsSymbol(symbols, "@/person/sub1/sub2/{id} -- GET", docUri, 29, 7, 29, 46)); + assertTrue(containsSymbol(symbols, "@/person/sub1/sub2 -- GET", docUri, 30, 8, 30, 61)); + assertTrue(containsSymbol(symbols, "@/person/sub1/sub2/nestedGet -- GET", docUri, 31, 9, 31, 56)); + assertTrue(containsSymbol(symbols, "@/person/sub1/andNestPath/andNestPathGET -- GET", docUri, 33, 5, 33, 54)); + assertTrue(containsSymbol(symbols, "@/person/ -- POST", docUri, 34, 5, 34, 82)); + assertTrue(containsSymbol(symbols, "@/nestedDelete -- DELETE", docUri, 35, 42, 35, 93)); +/* + List addons = getAdditionalInformation(docUri); + assertEquals(3, addons.size()); + + WebfluxHandlerInformation handlerInfo1 = getWebfluxHandler(addons, "/person/{id}", "GET").get(0); + assertEquals("/person/{id}", handlerInfo1.getPath()); + assertEquals("[GET]", Arrays.toString(handlerInfo1.getHttpMethods())); + assertEquals(0, handlerInfo1.getContentTypes().length); + assertEquals("[APPLICATION_JSON]", Arrays.toString(handlerInfo1.getAcceptTypes())); + assertEquals("org.test.PersonHandler2", handlerInfo1.getHandlerClass()); + assertEquals("public Mono getPerson(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo1.getHandlerMethod()); + + WebfluxHandlerInformation handlerInfo2 = getWebfluxHandler(addons, "/", "POST").get(0); + assertEquals("/", handlerInfo2.getPath()); + assertEquals("[POST]", Arrays.toString(handlerInfo2.getHttpMethods())); + assertEquals("[APPLICATION_JSON, APPLICATION_PDF]", Arrays.toString(handlerInfo2.getContentTypes())); + assertEquals("[APPLICATION_JSON]", Arrays.toString(handlerInfo2.getAcceptTypes())); + assertEquals("org.test.PersonHandler2", handlerInfo2.getHandlerClass()); + assertEquals("public Mono createPerson(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo2.getHandlerMethod()); + + WebfluxHandlerInformation handlerInfo3 = getWebfluxHandler(addons, "/person", "HEAD").get(0); + assertEquals("/person", handlerInfo3.getPath()); + assertEquals("[GET, HEAD]", Arrays.toString(handlerInfo3.getHttpMethods())); + assertEquals(0, handlerInfo3.getContentTypes().length); + assertEquals("[TEXT_PLAIN, APPLICATION_JSON]", Arrays.toString(handlerInfo3.getAcceptTypes())); + assertEquals("org.test.PersonHandler2", handlerInfo3.getHandlerClass()); + assertEquals("public Mono listPeople(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo3.getHandlerMethod()); +*/ + } + private boolean containsSymbol(List symbols, String name, String uri, int startLine, int startCHaracter, int endLine, int endCharacter) { for (Iterator iterator = symbols.iterator(); iterator.hasNext();) { SymbolInformation symbol = iterator.next(); diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/NestedRouter3.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/NestedRouter3.java index 3abe41fbe..fc6a567ee 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/NestedRouter3.java +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/NestedRouter3.java @@ -24,10 +24,16 @@ public class NestedRouter3 { PersonHandler3 handler = new PersonHandler3(); return nest(path("/person"), - nest(accept(APPLICATION_JSON), - route(GET("/{id}"), handler::getPerson) - .andRoute(method(HttpMethod.GET), handler::listPeople) - ).andRoute(POST("/").and(contentType(APPLICATION_JSON)), handler::createPerson)); + nest(path("/sub1"), + nest(path("/sub2"), + nest(accept(APPLICATION_JSON), + route(GET("/{id}"), handler::getPerson) + .andRoute(method(HttpMethod.GET), handler::listPeople)) + .andRoute(GET("/nestedGet"), handler::getPerson)) + .and(nest(path("/andNestPath"), + route(GET("/andNestPathGET"), handler::getPerson)))) + .andRoute(POST("/").and(contentType(APPLICATION_JSON)), handler::createPerson)) + .and(nest(method(HttpMethod.DELETE), route(path("/nestedDelete"), handler::deletePerson))); } } diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/PersonHandler3.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/PersonHandler3.java index b1ec76362..a1a48331e 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/PersonHandler3.java +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/PersonHandler3.java @@ -19,4 +19,8 @@ public class PersonHandler3 { return ServerResponse.notFound().build(); } + public Mono deletePerson(ServerRequest request) { + return ServerResponse.notFound().build(); + } + }