added symbol per document handling to annotation indexer mechanism

This commit is contained in:
Martin Lippert
2017-09-17 17:44:52 +02:00
parent c76d6c7496
commit 985b43adbe
8 changed files with 134 additions and 159 deletions

View File

@@ -13,6 +13,8 @@ package org.springframework.ide.vscode.boot.java;
import java.util.HashMap;
import java.util.Map;
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolProvider;
import org.springframework.ide.vscode.boot.java.beans.ComponentSymbolProvider;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeLensEngine;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaDocumentSymbolHandler;
@@ -27,6 +29,7 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
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.scope.ScopeCompletionProcessor;
import org.springframework.ide.vscode.boot.java.utils.AnnotationIndexer;
import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor;
import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider;
import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider;
@@ -39,13 +42,11 @@ import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaPro
import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
import org.springframework.ide.vscode.commons.languageserver.util.WorkspaceSymbolHandler;
import org.springframework.ide.vscode.commons.maven.JavaProjectWithClasspathFileFinderStrategy;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy;
@@ -88,13 +89,15 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
ReferencesHandler referencesHandler = createReferenceHandler(this, javaProjectFinder);
documents.onReferences(referencesHandler);
documents.onDocumentSymbol(createDocumentSymbolHandler(this, javaProjectFinder));
AnnotationIndexer indexer = createAnnotationIndexer(this, javaProjectFinder);
documents.onDocumentSymbol(new BootJavaDocumentSymbolHandler(this, indexer));
workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(this, indexer));
BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(this, javaProjectFinder);
documents.onCodeLens(codeLensHandler::createCodeLenses);
documents.onCodeLensResolve(codeLensHandler::resolveCodeLens);
workspaceService.onWorkspaceSymbol(createWorkspaceSymbolHandler(this, javaProjectFinder));
}
public void setMaxCompletionsNumber(int number) {
@@ -122,7 +125,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
return new BootJavaHoverProvider(this, javaProjectFinder, providers);
}
protected DocumentSymbolHandler createDocumentSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) {
protected AnnotationIndexer createAnnotationIndexer(SimpleLanguageServer server, JavaProjectFinder projectFinder) {
HashMap<String, SymbolProvider> providers = new HashMap<>();
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider());
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_GET_MAPPING, new RequestMappingSymbolProvider());
@@ -131,19 +134,10 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_DELETE_MAPPING, new RequestMappingSymbolProvider());
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_PATCH_MAPPING, new RequestMappingSymbolProvider());
return new BootJavaDocumentSymbolHandler(server, projectFinder, providers);
}
providers.put(org.springframework.ide.vscode.boot.java.beans.Constants.SPRING_BEAN, new BeansSymbolProvider());
providers.put(org.springframework.ide.vscode.boot.java.beans.Constants.SPRING_COMPONENT, new ComponentSymbolProvider());
protected WorkspaceSymbolHandler createWorkspaceSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) {
HashMap<String, SymbolProvider> providers = new HashMap<>();
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider());
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_GET_MAPPING, new RequestMappingSymbolProvider());
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_POST_MAPPING, new RequestMappingSymbolProvider());
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_PUT_MAPPING, new RequestMappingSymbolProvider());
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_DELETE_MAPPING, new RequestMappingSymbolProvider());
providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_PATCH_MAPPING, new RequestMappingSymbolProvider());
return new BootJavaWorkspaceSymbolHandler(server, projectFinder, providers);
return new AnnotationIndexer(projectFinder, providers);
}
protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) {

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2017 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.beans;
import org.eclipse.jdt.core.dom.Annotation;
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 BeansSymbolProvider implements SymbolProvider {
@Override
public SymbolInformation getSymbol(Annotation node, TextDocument doc) {
return null;
}
}

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2017 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.beans;
import org.eclipse.jdt.core.dom.Annotation;
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 ComponentSymbolProvider implements SymbolProvider {
@Override
public SymbolInformation getSymbol(Annotation node, TextDocument doc) {
return null;
}
}

View File

@@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2017 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.beans;
/**
* @author Martin Lippert
*/
public class Constants {
public static final String SPRING_COMPONENT = "org.springframework.stereotype.Component";
public static final String SPRING_BEAN = "org.springframework.context.annotation.Bean";
}

View File

@@ -11,34 +11,13 @@
package org.springframework.ide.vscode.boot.java.handlers;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.boot.java.utils.AnnotationIndexer;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
* @author Martin Lippert
@@ -46,129 +25,21 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
public class BootJavaDocumentSymbolHandler implements DocumentSymbolHandler {
private SimpleLanguageServer server;
private JavaProjectFinder projectFinder;
private Map<String, SymbolProvider> symbolProviders;
private AnnotationIndexer indexer;
public BootJavaDocumentSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map<String, SymbolProvider> specificProviders) {
public BootJavaDocumentSymbolHandler(SimpleLanguageServer server, AnnotationIndexer indexer) {
this.server = server;
this.projectFinder = projectFinder;
this.symbolProviders = specificProviders;
this.indexer = indexer;
}
@Override
public List<? extends SymbolInformation> handle(DocumentSymbolParams params) {
SimpleTextDocumentService documents = server.getTextDocumentService();
TextDocument doc = documents.get(params.getTextDocument().getUri()).copy();
if (doc != null) {
try {
return provideDocumentSymbols(doc);
} catch (Exception e) {
e.printStackTrace();
}
}
Path root = this.server.getWorkspaceRoot();
return SimpleTextDocumentService.NO_SYMBOLS;
}
indexer.reset();
indexer.scanFiles(root.toFile());
private List<? extends SymbolInformation> provideDocumentSymbols(TextDocument document) throws Exception {
ASTParser parser = ASTParser.newParser(AST.JLS8);
Map<String, String> options = JavaCore.getOptions();
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
parser.setCompilerOptions(options);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setStatementsRecovery(true);
parser.setBindingsRecovery(true);
parser.setResolveBindings(true);
String[] classpathEntries = getClasspathEntries(document);
String[] sourceEntries = new String[] {};
parser.setEnvironment(classpathEntries, sourceEntries, null, true);
String docURI = document.getUri();
String unitName = docURI.substring(docURI.lastIndexOf("/"));
parser.setUnitName(unitName);
parser.setSource(document.get(0, document.getLength()).toCharArray());
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
if (cu != null) {
System.out.println("AST node found: " + cu.getClass().getName());
return provideDocumentSymbolsForAnnotations(cu, document);
}
return null;
}
private List<? extends SymbolInformation> provideDocumentSymbolsForAnnotations(ASTNode node, TextDocument doc) {
List<SymbolInformation> result = new ArrayList<>();
ASTVisitor visitor = new ASTVisitor() {
@Override
public boolean visit(SingleMemberAnnotation node) {
extractSymbol(node, doc, result);
return super.visit(node);
}
@Override
public boolean visit(NormalAnnotation node) {
extractSymbol(node, doc, result);
return super.visit(node);
}
@Override
public boolean visit(MarkerAnnotation node) {
extractSymbol(node, doc, result);
return super.visit(node);
}
};
node.accept(visitor);
return result;
}
private void extractSymbol(Annotation node, TextDocument doc, List<SymbolInformation> result) {
System.out.println("annotation found: " + node.toString());
ITypeBinding typeBinding = node.resolveTypeBinding();
if (typeBinding != null) {
String qualifiedTypeName = typeBinding.getQualifiedName();
SymbolProvider provider = symbolProviders.get(qualifiedTypeName);
if (provider != null) {
SymbolInformation symbol = provider.getSymbol(node, doc);
if (symbol != null) {
result.add(symbol);
}
}
else {
provideDefaultSymbol(node, doc, result);
}
}
}
private void provideDefaultSymbol(Annotation node, TextDocument doc, List<SymbolInformation> result) {
try {
ITypeBinding type = node.resolveTypeBinding();
if (type != null) {
String qualifiedName = type.getQualifiedName();
if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) {
SymbolInformation symbol = new SymbolInformation(node.toString(), SymbolKind.Interface,
new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())));
result.add(symbol);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
private String[] getClasspathEntries(IDocument doc) throws Exception {
IJavaProject project = this.projectFinder.find(doc);
IClasspath classpath = project.getClasspath();
Stream<Path> classpathEntries = classpath.getClasspathEntries();
return classpathEntries
.filter(path -> path.toFile().exists())
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);
return indexer.getSymbols(params.getTextDocument().getUri());
}
}

View File

@@ -12,12 +12,10 @@ package org.springframework.ide.vscode.boot.java.handlers;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.WorkspaceSymbolParams;
import org.springframework.ide.vscode.boot.java.utils.AnnotationIndexer;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.WorkspaceSymbolHandler;
@@ -29,9 +27,9 @@ public class BootJavaWorkspaceSymbolHandler implements WorkspaceSymbolHandler {
private SimpleLanguageServer server;
private AnnotationIndexer indexer;
public BootJavaWorkspaceSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map<String, SymbolProvider> specificProviders) {
public BootJavaWorkspaceSymbolHandler(SimpleLanguageServer server, AnnotationIndexer indexer) {
this.server = server;
this.indexer = new AnnotationIndexer(projectFinder, specificProviders);
this.indexer = indexer;
}
@Override

View File

@@ -18,6 +18,8 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -51,12 +53,14 @@ public class AnnotationIndexer {
private Map<String, SymbolProvider> symbolProviders;
private List<SymbolInformation> symbols;
private ConcurrentMap<String, List<SymbolInformation>> symbolsByDoc;
public AnnotationIndexer(JavaProjectFinder projectFinder, Map<String, SymbolProvider> specificProviders) {
this.projectFinder = projectFinder;
this.symbolProviders = specificProviders;
this.symbols = new ArrayList<>();
this.symbolsByDoc = new ConcurrentHashMap<>();
}
public void reset() {
@@ -64,7 +68,11 @@ public class AnnotationIndexer {
}
public List<? extends SymbolInformation> getAllSymbols() {
return symbols;
return this.symbols;
}
public List<? extends SymbolInformation> getSymbols(String docURI) {
return this.symbolsByDoc.get(docURI);
}
public void scanFiles(File directory) {
@@ -172,12 +180,14 @@ public class AnnotationIndexer {
SymbolInformation symbol = provider.getSymbol(node, doc);
if (symbol != null) {
symbols.add(symbol);
symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList<SymbolInformation>()).add(symbol);
}
}
else {
SymbolInformation symbol = provideDefaultSymbol(node, doc);
if (symbol != null) {
symbols.add(symbol);
symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList<SymbolInformation>()).add(symbol);
}
}
}

View File

@@ -71,6 +71,31 @@ public class AnnotationIndexerTest {
assertTrue(containsSymbol(allSymbols, "@/mapping-subpackage -- (no method defined)", uriPrefix + "/src/main/java/org/test/sub/MappingClassSubpackage.java", 7, 1, 7, 38));
}
@Test
public void testRetrievingSymbolsPerDocument() throws Exception {
AnnotationIndexer indexer = new AnnotationIndexer(projectFinder, symbolProviders);
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
indexer.scanFiles(directory);
String uriPrefix = "file://" + directory.getAbsolutePath();
List<? extends SymbolInformation> symbols = indexer.getSymbols(uriPrefix + "/src/main/java/org/test/MainClass.java");
assertEquals(4, symbols.size());
assertTrue(containsSymbol(symbols, "@SpringBootApplication", uriPrefix + "/src/main/java/org/test/MainClass.java", 6, 0, 6, 22));
assertTrue(containsSymbol(symbols, "@/embedded-foo-mapping -- (no method defined)", uriPrefix + "/src/main/java/org/test/MainClass.java", 17, 1, 17, 41));
assertTrue(containsSymbol(symbols, "@/foo-root-mapping -- (no method defined)", uriPrefix + "/src/main/java/org/test/MainClass.java", 24, 0, 24, 36));
assertTrue(containsSymbol(symbols, "@/embedded-foo-mapping -- (no method defined)", uriPrefix + "/src/main/java/org/test/MainClass.java", 27, 1, 27, 41));
symbols = indexer.getSymbols(uriPrefix + "/src/main/java/org/test/SimpleMappingClass.java");
assertEquals(2, symbols.size());
assertTrue(containsSymbol(symbols, "@/mapping1 -- (no method defined)", uriPrefix + "/src/main/java/org/test/SimpleMappingClass.java", 6, 1, 6, 28));
assertTrue(containsSymbol(symbols, "@/mapping2 -- (no method defined)", uriPrefix + "/src/main/java/org/test/SimpleMappingClass.java", 11, 1, 11, 28));
symbols = indexer.getSymbols(uriPrefix + "/src/main/java/org/test/sub/MappingClassSubpackage.java");
assertEquals(2, symbols.size());
assertTrue(containsSymbol(symbols, "@/classlevel -- (no method defined)", uriPrefix + "/src/main/java/org/test/sub/MappingClassSubpackage.java", 4, 0, 4, 30));
assertTrue(containsSymbol(symbols, "@/mapping-subpackage -- (no method defined)", uriPrefix + "/src/main/java/org/test/sub/MappingClassSubpackage.java", 7, 1, 7, 38));
}
@Test
public void testScanningAllAnnotationsMultiModuleProjectUpfront() throws Exception {
AnnotationIndexer indexer = new AnnotationIndexer(projectFinder, symbolProviders);