diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index a4253d57c..68a4187fc 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -40,6 +40,7 @@ import org.springframework.ide.vscode.boot.java.livehover.ComponentInjectionsHov import org.springframework.ide.vscode.boot.java.requestmapping.LiveAppURLSymbolProvider; import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingHoverProvider; import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider; +import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider; import org.springframework.ide.vscode.boot.java.scope.ScopeCompletionProcessor; import org.springframework.ide.vscode.boot.java.snippets.JavaSnippet; import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetContext; @@ -281,6 +282,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent providers.put(Annotations.COMPONENT, new ComponentSymbolProvider()); providers.put(Annotations.REPOSITORY, new DataRepositorySymbolProvider()); + providers.put("", new WebfluxRouterSymbolProvider()); return new SpringIndexer(server, params, providers); } diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java index 3878448ad..2937171a2 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java @@ -165,5 +165,9 @@ public class BeansSymbolProvider implements SymbolProvider { return false; } + @Override + public Collection getSymbols(MethodDeclaration methodDeclaration, TextDocument doc) { + return null; + } } diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java index a6829ff6a..9f2b6191e 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java @@ -16,6 +16,7 @@ import java.util.stream.Collectors; 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.Location; import org.eclipse.lsp4j.SymbolInformation; @@ -116,4 +117,9 @@ public class ComponentSymbolProvider implements SymbolProvider { return null; } + @Override + public Collection getSymbols(MethodDeclaration methodDeclaration, TextDocument doc) { + return null; + } + } diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java index b50524866..3f895e1d3 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java @@ -14,6 +14,7 @@ import java.util.Collection; 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.Location; import org.eclipse.lsp4j.SymbolInformation; @@ -139,4 +140,9 @@ public class DataRepositorySymbolProvider implements SymbolProvider { } return beanName; } + + @Override + public Collection getSymbols(MethodDeclaration methodDeclaration, TextDocument doc) { + return null; + } } diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SymbolProvider.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SymbolProvider.java index ff4802e26..2f8fc8b7f 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SymbolProvider.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SymbolProvider.java @@ -14,6 +14,7 @@ import java.util.Collection; 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.SymbolInformation; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -26,5 +27,6 @@ public interface SymbolProvider { Collection getSymbols(Annotation node, ITypeBinding typeBinding, Collection metaAnnotations, TextDocument doc); Collection getSymbols(TypeDeclaration typeDeclaration, TextDocument doc); + Collection getSymbols(MethodDeclaration methodDeclaration, TextDocument doc); } diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java index ec9d4d7a0..6e0d0ee58 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java @@ -181,4 +181,9 @@ public class RequestMappingSymbolProvider implements SymbolProvider { return null; } + @Override + public Collection getSymbols(MethodDeclaration methodDeclaration, TextDocument doc) { + return null; + } + } diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java new file mode 100644 index 000000000..60db3b12c --- /dev/null +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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 java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.Block; +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.MethodInvocation; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.lsp4j.SymbolInformation; +import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public class WebfluxRouterSymbolProvider implements SymbolProvider { + + private static final String ROUTER_FUNCTION_TYPE = "org.springframework.web.reactive.function.server.RouterFunction"; + private static final String ROUTER_FUNCTIONS_TYPE = "org.springframework.web.reactive.function.server.RouterFunctions"; + + @Override + public Collection getSymbols(Annotation node, ITypeBinding typeBinding, + Collection metaAnnotations, TextDocument doc) { + return null; + } + + @Override + public Collection getSymbols(TypeDeclaration typeDeclaration, TextDocument doc) { + return null; + } + + @Override + public Collection getSymbols(MethodDeclaration methodDeclaration, TextDocument doc) { + Type returnType = methodDeclaration.getReturnType2(); + if (returnType != null) { + ITypeBinding resolvedBinding = returnType.resolveBinding(); + if (resolvedBinding != null) { + if (ROUTER_FUNCTION_TYPE.equals(resolvedBinding.getBinaryName())) { + return getSymbolsForRouterFunction(methodDeclaration, doc); + } + } + } + return null; + } + + private Collection getSymbolsForRouterFunction(MethodDeclaration methodDeclaration, + TextDocument doc) { + List result = new ArrayList<>(); + + Block body = methodDeclaration.getBody(); + body.accept(new ASTVisitor() { + + @Override + public boolean visit(MethodInvocation node) { + IMethodBinding methodBinding = node.resolveMethodBinding(); + + if (ROUTER_FUNCTIONS_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName()) + && "route".equals(node.getName().toString())) { + extractMappingSymbol(node, doc, result); + } + else if (ROUTER_FUNCTION_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName()) + && "andRoute".equals(node.getName().toString())) { + extractMappingSymbol(node, doc, result); + } + + return super.visit(node); + } + + }); + + return result; + } + + protected void extractMappingSymbol(MethodInvocation node, TextDocument doc, List result) { + List arguments = node.arguments(); + if (arguments != null) { + for (Object argument : arguments) { + System.out.println(argument); + } + } + } + +} diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index a9316a013..d97a73bbb 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -42,6 +42,7 @@ import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.FileASTRequestor; 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.NormalAnnotation; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.TypeDeclaration; @@ -346,7 +347,7 @@ public class SpringIndexer { parser.setKind(ASTParser.K_COMPILATION_UNIT); parser.setStatementsRecovery(true); parser.setBindingsRecovery(true); - parser.setResolveBindings(true); + parser.setResolveBindings(false); String[] sourceEntries = new String[] {}; parser.setEnvironment(classpathEntries, sourceEntries, null, true); @@ -377,7 +378,7 @@ public class SpringIndexer { parser.setStatementsRecovery(true); parser.setBindingsRecovery(true); parser.setResolveBindings(true); - parser.setIgnoreMethodBodies(true); + parser.setIgnoreMethodBodies(false); String[] sourceEntries = new String[] {}; parser.setEnvironment(classpathEntries, sourceEntries, null, true); @@ -408,6 +409,17 @@ public class SpringIndexer { return super.visit(node); } + @Override + public boolean visit(MethodDeclaration node) { + try { + extractSymbolInformation(node, docURI, docRef, content); + } + catch (Exception e) { + e.printStackTrace(); + } + return super.visit(node); + } + @Override public boolean visit(SingleMemberAnnotation node) { try { @@ -462,6 +474,22 @@ public class SpringIndexer { } } + private void extractSymbolInformation(MethodDeclaration methodDeclaration, String docURI, AtomicReference docRef, String content) throws Exception { + Collection providers = symbolProviders.getAll(); + if (!providers.isEmpty()) { + TextDocument doc = getTempTextDocument(docURI, docRef, content); + for (SymbolProvider provider : providers) { + Collection sbls = provider.getSymbols(methodDeclaration, doc); + if (sbls != null) { + sbls.forEach(symbol -> { + symbols.add(symbol); + symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList()).add(symbol); + }); + } + } + } + } + private void extractSymbolInformation(Annotation node, String docURI, AtomicReference docRef, String content) throws Exception { ITypeBinding typeBinding = node.resolveTypeBinding(); diff --git a/headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java b/headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java index 596971ee6..dfe7c6409 100644 --- a/headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java +++ b/headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java @@ -47,6 +47,18 @@ public class WebFluxMappingSymbolProviderTest { assertTrue(containsSymbol(symbols, "@/users/{username}", docUri, 24, 1, 24, 85)); } + @Test + public void testRoutesMappingSymbols() 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/QuoteRouter.java").toUri().toString(); + List symbols = getSymbols(docUri); + assertEquals(6, symbols.size()); + assertTrue(containsSymbol(symbols, "@/hello", docUri, 22, 23, 22, 81)); + assertTrue(containsSymbol(symbols, "@/echo", docUri, 23, 1, 23, 85)); + } + 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();