PT #151438408: annotation index is updated on document save event
This commit is contained in:
@@ -17,6 +17,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.eclipse.lsp4j.InitializeParams;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
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;
|
||||
@@ -109,7 +110,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
|
||||
liveHoverWatchdog = new SpringLiveHoverWatchdog(this, hoverInfoProvider);
|
||||
documents.onDidChangeContent(params -> {
|
||||
TextDocument doc = params.getDocument();
|
||||
if (testHightlighter!=null) {
|
||||
if (testHightlighter != null) {
|
||||
getClient().highlight(new HighlightParams(params.getDocument().getId(), testHightlighter.apply(doc)));
|
||||
} else {
|
||||
liveHoverWatchdog.watchDocument(doc.getUri());
|
||||
@@ -121,12 +122,19 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
|
||||
documents.onReferences(referencesHandler);
|
||||
|
||||
indexer = createAnnotationIndexer(this, javaProjectFinder);
|
||||
documents.onDidSave(params -> {
|
||||
String docURI = params.getDocument().getId().getUri();
|
||||
String content = params.getDocument().get();
|
||||
indexer.updateDocument(docURI, content);
|
||||
});
|
||||
|
||||
documents.onDocumentSymbol(new BootJavaDocumentSymbolHandler(indexer));
|
||||
workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(indexer));
|
||||
|
||||
BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(this, javaProjectFinder);
|
||||
documents.onCodeLens(codeLensHandler::createCodeLenses);
|
||||
documents.onCodeLensResolve(codeLensHandler::resolveCodeLens);
|
||||
|
||||
}
|
||||
|
||||
public void setMaxCompletionsNumber(int number) {
|
||||
|
||||
@@ -49,8 +49,6 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@@ -89,8 +87,19 @@ public class SpringIndexer {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDocument(String docURI) {
|
||||
// TODO: update information because of doc change
|
||||
public void updateDocument(String docURI, String content) {
|
||||
if (docURI.endsWith(".java")) {
|
||||
try {
|
||||
IJavaProject project = projectFinder.find(new File(new URI(docURI)));
|
||||
if (project != null) {
|
||||
String[] classpathEntries = getClasspathEntries(project);
|
||||
scanFile(docURI, content, classpathEntries);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<? extends SymbolInformation> getAllSymbols(String query) {
|
||||
@@ -184,6 +193,34 @@ public class SpringIndexer {
|
||||
}
|
||||
}
|
||||
|
||||
private void scanFile(String docURI, String content, String[] classpathEntries) 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[] sourceEntries = new String[] {};
|
||||
parser.setEnvironment(classpathEntries, sourceEntries, null, true);
|
||||
|
||||
String unitName = docURI.substring(docURI.lastIndexOf("/"));
|
||||
parser.setUnitName(unitName);
|
||||
parser.setSource(content.toCharArray());
|
||||
|
||||
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
|
||||
|
||||
if (cu != null) {
|
||||
List<SymbolInformation> oldSymbols = symbolsByDoc.remove(docURI);
|
||||
symbols.removeAll(oldSymbols);
|
||||
|
||||
AtomicReference<TextDocument> docRef = new AtomicReference<>();
|
||||
scanAST(cu, docURI, docRef, content);
|
||||
}
|
||||
}
|
||||
|
||||
private void scanFiles(ASTParser parser, String[] javaFiles, String[] classpathEntries) throws Exception {
|
||||
|
||||
Map<String, String> options = JavaCore.getOptions();
|
||||
@@ -203,20 +240,20 @@ public class SpringIndexer {
|
||||
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
|
||||
String docURI = "file://" + sourceFilePath;
|
||||
AtomicReference<TextDocument> docRef = new AtomicReference<>();
|
||||
scanAST(cu, docURI, docRef);
|
||||
scanAST(cu, docURI, docRef, null);
|
||||
}
|
||||
};
|
||||
|
||||
parser.createASTs(javaFiles, null, new String[0], requestor, null);
|
||||
}
|
||||
|
||||
private void scanAST(final CompilationUnit cu, final String docURI, AtomicReference<TextDocument> docRef) {
|
||||
private void scanAST(final CompilationUnit cu, final String docURI, AtomicReference<TextDocument> docRef, final String content) {
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, docURI, docRef);
|
||||
extractSymbolInformation(node, docURI, docRef, content);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -228,7 +265,7 @@ public class SpringIndexer {
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, docURI, docRef);
|
||||
extractSymbolInformation(node, docURI, docRef, content);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -240,7 +277,7 @@ public class SpringIndexer {
|
||||
@Override
|
||||
public boolean visit(MarkerAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, docURI, docRef);
|
||||
extractSymbolInformation(node, docURI, docRef, content);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -251,7 +288,7 @@ public class SpringIndexer {
|
||||
});
|
||||
}
|
||||
|
||||
private void extractSymbolInformation(Annotation node, String docURI, AtomicReference<TextDocument> docRef) throws Exception {
|
||||
private void extractSymbolInformation(Annotation node, String docURI, AtomicReference<TextDocument> docRef, String content) throws Exception {
|
||||
ITypeBinding typeBinding = node.resolveTypeBinding();
|
||||
|
||||
if (typeBinding != null) {
|
||||
@@ -259,7 +296,7 @@ public class SpringIndexer {
|
||||
|
||||
SymbolProvider provider = symbolProviders.get(qualifiedTypeName);
|
||||
if (provider != null) {
|
||||
TextDocument doc = getTempTextDocument(docURI, docRef);
|
||||
TextDocument doc = getTempTextDocument(docURI, docRef, content);
|
||||
SymbolInformation symbol = provider.getSymbol(node, doc);
|
||||
if (symbol != null) {
|
||||
symbols.add(symbol);
|
||||
@@ -267,7 +304,7 @@ public class SpringIndexer {
|
||||
}
|
||||
}
|
||||
else {
|
||||
SymbolInformation symbol = provideDefaultSymbol(node, docURI, docRef);
|
||||
SymbolInformation symbol = provideDefaultSymbol(node, docURI, docRef, content);
|
||||
if (symbol != null) {
|
||||
symbols.add(symbol);
|
||||
symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList<SymbolInformation>()).add(symbol);
|
||||
@@ -276,30 +313,32 @@ public class SpringIndexer {
|
||||
}
|
||||
}
|
||||
|
||||
private TextDocument getTempTextDocument(String docURI, AtomicReference<TextDocument> docRef) throws Exception {
|
||||
private TextDocument getTempTextDocument(String docURI, AtomicReference<TextDocument> docRef, String content) throws Exception {
|
||||
TextDocument doc = docRef.get();
|
||||
if (doc == null) {
|
||||
doc = createTempTextDocument(docURI);
|
||||
doc = createTempTextDocument(docURI, content);
|
||||
docRef.set(doc);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
private TextDocument createTempTextDocument(String docURI) throws Exception {
|
||||
Path path = Paths.get(new URI(docURI));
|
||||
String content = new String(Files.readAllBytes(path));
|
||||
private TextDocument createTempTextDocument(String docURI, String content) throws Exception {
|
||||
if (content == null) {
|
||||
Path path = Paths.get(new URI(docURI));
|
||||
content = new String(Files.readAllBytes(path));
|
||||
}
|
||||
|
||||
TextDocument doc = new TextDocument(docURI, LanguageId.PLAINTEXT, 0, content);
|
||||
return doc;
|
||||
}
|
||||
|
||||
private SymbolInformation provideDefaultSymbol(Annotation node, String docURI, AtomicReference<TextDocument> docRef) {
|
||||
private SymbolInformation provideDefaultSymbol(Annotation node, String docURI, AtomicReference<TextDocument> docRef, String content) {
|
||||
try {
|
||||
ITypeBinding type = node.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
String qualifiedName = type.getQualifiedName();
|
||||
if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) {
|
||||
TextDocument doc = getTempTextDocument(docURI, docRef);
|
||||
TextDocument doc = getTempTextDocument(docURI, docRef, content);
|
||||
SymbolInformation symbol = new SymbolInformation(node.toString(), SymbolKind.Interface,
|
||||
new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())));
|
||||
return symbol;
|
||||
|
||||
@@ -14,12 +14,14 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -144,6 +146,42 @@ public class SpringIndexerTest {
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage -- (no method defined)", uriPrefix + "/src/main/java/org/test/sub/MappingClassSubpackage.java", 7, 1, 7, 38));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateChangedDocument() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
// create initial index content
|
||||
SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
indexer.initialize(directory.toPath());
|
||||
|
||||
// update document and update index
|
||||
String changedDocURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/SimpleMappingClass.java";
|
||||
String newContent = FileUtils.readFileToString(new File(new URI(changedDocURI))).replace("mapping1", "mapping1-CHANGED");
|
||||
indexer.updateDocument(changedDocURI, newContent);
|
||||
|
||||
// check for updated index per document
|
||||
List<? extends SymbolInformation> symbols = indexer.getSymbols(changedDocURI);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/mapping1-CHANGED -- (no method defined)", changedDocURI, 6, 1, 6, 36));
|
||||
assertTrue(containsSymbol(symbols, "@/mapping2 -- (no method defined)", changedDocURI, 11, 1, 11, 28));
|
||||
|
||||
// check for updated index in all symbols
|
||||
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
|
||||
assertEquals(8, allSymbols.size());
|
||||
|
||||
String uriPrefix = "file://" + directory.getAbsolutePath();
|
||||
|
||||
assertTrue(containsSymbol(allSymbols, "@SpringBootApplication", uriPrefix + "/src/main/java/org/test/MainClass.java", 6, 0, 6, 22));
|
||||
assertTrue(containsSymbol(allSymbols, "@/embedded-foo-mapping -- (no method defined)", uriPrefix + "/src/main/java/org/test/MainClass.java", 17, 1, 17, 41));
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping -- (no method defined)", uriPrefix + "/src/main/java/org/test/MainClass.java", 24, 0, 24, 36));
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root -- (no method defined)", uriPrefix + "/src/main/java/org/test/MainClass.java", 27, 1, 27, 51));
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping1-CHANGED -- (no method defined)", uriPrefix + "/src/main/java/org/test/SimpleMappingClass.java", 6, 1, 6, 36));
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping2 -- (no method defined)", uriPrefix + "/src/main/java/org/test/SimpleMappingClass.java", 11, 1, 11, 28));
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel -- (no method defined)", uriPrefix + "/src/main/java/org/test/sub/MappingClassSubpackage.java", 4, 0, 4, 30));
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage -- (no method defined)", uriPrefix + "/src/main/java/org/test/sub/MappingClassSubpackage.java", 7, 1, 7, 38));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterSymbolsUsingQueryString() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
Reference in New Issue
Block a user