From 35f11033037ec15c4cf860f331499d891fcb1a01 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Tue, 29 Aug 2017 11:41:38 +0200 Subject: [PATCH] finished refactoring of boot annotation type centric packages and handler registration --- .../boot/java/BootJavaLanguageServer.java | 81 +++++++++++++++++-- .../BootJavaCompletionEngine.java | 59 +++++--------- .../BootJavaDocumentSymbolHandler.java | 8 +- .../java/handlers/BootJavaHoverProvider.java | 29 ++----- .../BootJavaReconcileEngine.java | 2 +- .../handlers/BootJavaReferencesHandler.java | 34 ++++---- .../BootJavaWorkspaceSymbolHandler.java | 9 +-- .../java/handlers/CompletionProvider.java | 28 +++++++ .../boot/java/handlers/ReferenceProvider.java | 30 +++++++ ...=> RequestMappingCompletionProcessor.java} | 2 +- .../RequestMappingHoverProvider.java | 11 --- .../RequestMappingSymbolProvider.java | 20 ----- .../ide/vscode/boot/java/scope/Constants.java | 20 +++++ .../ScopeCompletionProcessor.java | 30 ++++--- .../ScopeNameCompletion.java | 2 +- .../ScopeNameCompletionProposal.java | 2 +- .../ValueCompletionProcessor.java | 77 ++++++++++-------- .../ValuePropertyKeyProposal.java | 2 +- .../ValuePropertyReferencesProvider.java | 48 +++++------ .../completions/test/ScopeCompletionTest.java | 4 +- .../completions/test/ValueCompletionTest.java | 6 +- .../test/PropertyReferenceFinderTest.java | 2 +- 22 files changed, 298 insertions(+), 208 deletions(-) rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/{completions => handlers}/BootJavaCompletionEngine.java (66%) rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/{completions => handlers}/BootJavaReconcileEngine.java (93%) create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/CompletionProvider.java create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/{RequestMappingAssistProvider.java => RequestMappingCompletionProcessor.java} (92%) create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/Constants.java rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/{completions => scope}/ScopeCompletionProcessor.java (84%) rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/{completions => scope}/ScopeNameCompletion.java (95%) rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/{completions => scope}/ScopeNameCompletionProposal.java (97%) rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/{completions => value}/ValueCompletionProcessor.java (80%) rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/{completions => value}/ValuePropertyKeyProposal.java (96%) rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/{handlers => value}/ValuePropertyReferencesProvider.java (96%) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index c646fbae1..30801bb02 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -10,12 +10,25 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java; -import org.springframework.ide.vscode.boot.java.completions.BootJavaCompletionEngine; -import org.springframework.ide.vscode.boot.java.completions.BootJavaReconcileEngine; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine; import org.springframework.ide.vscode.boot.java.handlers.BootJavaDocumentSymbolHandler; import org.springframework.ide.vscode.boot.java.handlers.BootJavaHoverProvider; +import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine; import org.springframework.ide.vscode.boot.java.handlers.BootJavaReferencesHandler; import org.springframework.ide.vscode.boot.java.handlers.BootJavaWorkspaceSymbolHandler; +import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider; +import org.springframework.ide.vscode.boot.java.handlers.HoverProvider; +import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; +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.value.ValueCompletionProcessor; +import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider; +import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.gradle.GradleCore; import org.springframework.ide.vscode.commons.gradle.GradleProjectFinderStrategy; @@ -25,11 +38,13 @@ 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; @@ -61,24 +76,76 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { validateWith(doc.getId(), reconcileEngine); }); - ICompletionEngine bootCompletionEngine = new BootJavaCompletionEngine(javaProjectFinder, indexProvider); + ICompletionEngine bootCompletionEngine = createCompletionEngine(javaProjectFinder, indexProvider); completionEngine = createCompletionEngineAdapter(this, bootCompletionEngine); completionEngine.setMaxCompletions(100); documents.onCompletion(completionEngine::getCompletions); documents.onCompletionResolve(completionEngine::resolveCompletion); - HoverHandler hoverInfoProvider = new BootJavaHoverProvider(this, javaProjectFinder); + HoverHandler hoverInfoProvider = createHoverHandler(javaProjectFinder); documents.onHover(hoverInfoProvider); - ReferencesHandler referencesHandler = new BootJavaReferencesHandler(this, javaProjectFinder); + ReferencesHandler referencesHandler = createReferenceHandler(this, javaProjectFinder); documents.onReferences(referencesHandler); - documents.onDocumentSymbol(new BootJavaDocumentSymbolHandler(this, javaProjectFinder)); + documents.onDocumentSymbol(createDocumentSymbolHandler(this, javaProjectFinder)); - workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(this, javaProjectFinder)); + workspaceService.onWorkspaceSymbol(createWorkspaceSymbolHandler(this, javaProjectFinder)); } public void setMaxCompletionsNumber(int number) { completionEngine.setMaxCompletions(number); } + protected ICompletionEngine createCompletionEngine(JavaProjectFinder javaProjectFinder, SpringPropertyIndexProvider indexProvider) { + Map providers = new HashMap<>(); + providers.put(org.springframework.ide.vscode.boot.java.scope.Constants.SPRING_SCOPE, new ScopeCompletionProcessor()); + providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueCompletionProcessor(indexProvider)); + + return new BootJavaCompletionEngine(javaProjectFinder, providers); + } + + protected HoverHandler createHoverHandler(JavaProjectFinder javaProjectFinder) { + HashMap providers = new HashMap<>(); + providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueHoverProvider()); + providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_REQUEST_MAPPING, new RequestMappingHoverProvider()); + providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_GET_MAPPING, new RequestMappingHoverProvider()); + providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_POST_MAPPING, new RequestMappingHoverProvider()); + providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_PUT_MAPPING, new RequestMappingHoverProvider()); + providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_DELETE_MAPPING, new RequestMappingHoverProvider()); + providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_PATCH_MAPPING, new RequestMappingHoverProvider()); + + return new BootJavaHoverProvider(this, javaProjectFinder, providers); + } + + protected DocumentSymbolHandler createDocumentSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + HashMap 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 BootJavaDocumentSymbolHandler(server, projectFinder, providers); + } + + protected WorkspaceSymbolHandler createWorkspaceSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + HashMap 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); + } + + protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + Map providers = new HashMap<>(); + providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValuePropertyReferencesProvider(server)); + + return new BootJavaReferencesHandler(server, projectFinder, providers); + } + } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaCompletionEngine.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java similarity index 66% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaCompletionEngine.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java index 6411c5e39..f10dcf569 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaCompletionEngine.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java @@ -8,12 +8,11 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.completions; +package org.springframework.ide.vscode.boot.java.handlers; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; +import java.util.Collections; import java.util.Map; import java.util.stream.Stream; @@ -25,7 +24,6 @@ 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.NodeFinder; -import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; @@ -37,22 +35,17 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; * @author Martin Lippert */ public class BootJavaCompletionEngine implements ICompletionEngine { - - private static final String SPRING_SCOPE = "org.springframework.context.annotation.Scope"; - private static final String SPRING_VALUE = "org.springframework.beans.factory.annotation.Value"; - - private JavaProjectFinder projectFinder; - private SpringPropertyIndexProvider indexProvider; - public BootJavaCompletionEngine(JavaProjectFinder projectFinder, SpringPropertyIndexProvider indexProvider) { + private JavaProjectFinder projectFinder; + private Map completionProviders; + + public BootJavaCompletionEngine(JavaProjectFinder projectFinder, Map specificProviders) { this.projectFinder = projectFinder; - this.indexProvider = indexProvider; + this.completionProviders = specificProviders; } @Override public Collection getCompletions(IDocument document, int offset) throws Exception { - List completions = new ArrayList<>(); - ASTParser parser = ASTParser.newParser(AST.JLS8); Map options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options); @@ -61,7 +54,7 @@ public class BootJavaCompletionEngine implements ICompletionEngine { parser.setStatementsRecovery(true); parser.setBindingsRecovery(true); parser.setResolveBindings(true); - + String[] classpathEntries = getClasspathEntries(document); String[] sourceEntries = new String[] {}; parser.setEnvironment(classpathEntries, sourceEntries, null, true); @@ -73,45 +66,37 @@ public class BootJavaCompletionEngine implements ICompletionEngine { CompilationUnit cu = (CompilationUnit) parser.createAST(null); ASTNode node = NodeFinder.perform(cu, offset, 0); - + if (node != null) { - collectCompletionsForAnnotations(node, completions, offset, document); + System.out.println("AST node found: " + node.getClass().getName()); + return collectCompletionsForAnnotations(node, offset, document); } - System.out.println("AST node found: " + node.getClass().getName()); - - return completions; + return Collections.emptyList(); } - private void collectCompletionsForAnnotations(ASTNode node, List completions, int offset, IDocument doc) { + private Collection collectCompletionsForAnnotations(ASTNode node, int offset, IDocument doc) { Annotation annotation = null; - ASTNode exactNode = node; - + while (node != null && !(node instanceof Annotation)) { node = node.getParent(); } - + if (node != null) { annotation = (Annotation) node; ITypeBinding type = annotation.resolveTypeBinding(); if (type != null) { String qualifiedName = type.getQualifiedName(); - if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) { - collectCompletionsForSpringAnnotation(exactNode, annotation, type, completions, offset, doc); + if (qualifiedName != null) { + CompletionProvider provider = this.completionProviders.get(qualifiedName); + if (provider != null) { + return provider.provideCompletions(node, annotation, type, offset, doc); + } } } } - } - private void collectCompletionsForSpringAnnotation(ASTNode node, Annotation annotation, ITypeBinding type, - List completions, int offset, IDocument doc) { - - if (type.getQualifiedName().equals(SPRING_SCOPE)) { - new ScopeCompletionProcessor().collectCompletionsForScopeAnnotation(node, annotation, type, completions, offset, doc); - } - else if (type.getQualifiedName().equals(SPRING_VALUE)) { - new ValueCompletionProcessor(indexProvider.getIndex(doc)).collectCompletionsForValueAnnotation(node, annotation, type, completions, offset, doc); - } + return Collections.emptyList(); } private String[] getClasspathEntries(IDocument doc) throws Exception { @@ -120,7 +105,7 @@ public class BootJavaCompletionEngine implements ICompletionEngine { Stream classpathEntries = classpath.getClasspathEntries(); return classpathEntries .filter(path -> path.toFile().exists()) - .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); + .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaDocumentSymbolHandler.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaDocumentSymbolHandler.java index 5bd55cb88..ace21bb42 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaDocumentSymbolHandler.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaDocumentSymbolHandler.java @@ -12,7 +12,6 @@ package org.springframework.ide.vscode.boot.java.handlers; import java.nio.file.Path; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -32,7 +31,6 @@ 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.boot.java.requestmapping.RequestMappingSymbolProvider; 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; @@ -51,12 +49,10 @@ public class BootJavaDocumentSymbolHandler implements DocumentSymbolHandler { private JavaProjectFinder projectFinder; private Map symbolProviders; - public BootJavaDocumentSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + public BootJavaDocumentSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { this.server = server; this.projectFinder = projectFinder; - - this.symbolProviders = new HashMap<>(); - RequestMappingSymbolProvider.register(this.symbolProviders); + this.symbolProviders = specificProviders; } @Override diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index 5296895c0..f1af4f491 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -11,7 +11,6 @@ package org.springframework.ide.vscode.boot.java.handlers; import java.nio.file.Path; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; @@ -26,8 +25,6 @@ import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.TextDocumentPositionParams; -import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingHoverProvider; -import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider; 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; @@ -44,16 +41,12 @@ public class BootJavaHoverProvider implements HoverHandler { private JavaProjectFinder projectFinder; private SimpleLanguageServer server; - private Map hoverProviders; - public BootJavaHoverProvider(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + public BootJavaHoverProvider(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { this.server = server; this.projectFinder = projectFinder; - this.hoverProviders = new HashMap<>(); - - RequestMappingHoverProvider.register(this.hoverProviders); - ValueHoverProvider.register(this.hoverProviders); + this.hoverProviders = specificProviders; } @Override @@ -107,7 +100,6 @@ public class BootJavaHoverProvider implements HoverHandler { private CompletableFuture provideHoverForAnnotation(ASTNode node, int offset, TextDocument doc) { Annotation annotation = null; - ASTNode exactNode = node; while (node != null && !(node instanceof Annotation)) { node = node.getParent(); @@ -118,8 +110,11 @@ public class BootJavaHoverProvider implements HoverHandler { ITypeBinding type = annotation.resolveTypeBinding(); if (type != null) { String qualifiedName = type.getQualifiedName(); - if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) { - return provideHoverForSpringAnnotation(exactNode, annotation, type, offset, doc); + if (qualifiedName != null) { + HoverProvider provider = this.hoverProviders.get(qualifiedName); + if (provider != null) { + return provider.provideHover(node, annotation, type, offset, doc); + } } } } @@ -127,16 +122,6 @@ public class BootJavaHoverProvider implements HoverHandler { return null; } - private CompletableFuture provideHoverForSpringAnnotation(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc) { - String typeName = type.getQualifiedName(); - HoverProvider provider = this.hoverProviders.get(typeName); - if (provider != null) { - return provider.provideHover(node, annotation, type, offset, doc); - } - - return null; - } - private String[] getClasspathEntries(IDocument doc) throws Exception { IJavaProject project = this.projectFinder.find(doc); IClasspath classpath = project.getClasspath(); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaReconcileEngine.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java similarity index 93% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaReconcileEngine.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java index 5b8a39217..3220d1ef0 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaReconcileEngine.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.completions; +package org.springframework.ide.vscode.boot.java.handlers; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java index 078311047..05558835b 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java @@ -40,14 +40,14 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class BootJavaReferencesHandler implements ReferencesHandler { - private static final String SPRING_VALUE = "org.springframework.beans.factory.annotation.Value"; - private JavaProjectFinder projectFinder; private SimpleLanguageServer server; + private Map referenceProviders; - public BootJavaReferencesHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + public BootJavaReferencesHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { this.server = server; this.projectFinder = projectFinder; + this.referenceProviders = specificProviders; } @Override @@ -65,7 +65,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler { catch (Exception e) { } } - + return SimpleTextDocumentService.NO_REFERENCES; } @@ -78,7 +78,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler { parser.setStatementsRecovery(true); parser.setBindingsRecovery(true); parser.setResolveBindings(true); - + String[] classpathEntries = getClasspathEntries(document); String[] sourceEntries = new String[] {}; parser.setEnvironment(classpathEntries, sourceEntries, null, true); @@ -90,7 +90,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler { CompilationUnit cu = (CompilationUnit) parser.createAST(null); ASTNode node = NodeFinder.perform(cu, offset, 0); - + if (node != null) { System.out.println("AST node found: " + node.getClass().getName()); return provideReferencesForAnnotation(node, offset, document); @@ -101,31 +101,25 @@ public class BootJavaReferencesHandler implements ReferencesHandler { private CompletableFuture> provideReferencesForAnnotation(ASTNode node, int offset, TextDocument doc) { Annotation annotation = null; - ASTNode exactNode = node; - + while (node != null && !(node instanceof Annotation)) { node = node.getParent(); } - + if (node != null) { annotation = (Annotation) node; ITypeBinding type = annotation.resolveTypeBinding(); if (type != null) { String qualifiedName = type.getQualifiedName(); - if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) { - return provideReferencesForSpringAnnotation(exactNode, annotation, type, offset, doc); + if (qualifiedName != null) { + ReferenceProvider provider = this.referenceProviders.get(qualifiedName); + if (provider != null) { + return provider.provideReferences(node, annotation, type, offset, doc); + } } } } - - return null; - } - private CompletableFuture> provideReferencesForSpringAnnotation(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc) { - if (type.getQualifiedName().equals(SPRING_VALUE)) { - return new ValuePropertyReferencesProvider(server).provideReferencesForValueAnnotation(node, annotation, type, offset, doc); - } - return null; } @@ -135,7 +129,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler { Stream classpathEntries = classpath.getClasspathEntries(); return classpathEntries .filter(path -> path.toFile().exists()) - .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); + .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaWorkspaceSymbolHandler.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaWorkspaceSymbolHandler.java index 3afbcebcd..57fe8fd34 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaWorkspaceSymbolHandler.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaWorkspaceSymbolHandler.java @@ -14,7 +14,6 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -33,8 +32,6 @@ import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; import org.eclipse.lsp4j.WorkspaceSymbolParams; -import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; -import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider; 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; @@ -54,13 +51,11 @@ public class BootJavaWorkspaceSymbolHandler implements WorkspaceSymbolHandler { private List symbols; - public BootJavaWorkspaceSymbolHandler(BootJavaLanguageServer server, JavaProjectFinder projectFinder) { + public BootJavaWorkspaceSymbolHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { this.server = server; this.projectFinder = projectFinder; this.symbols = new ArrayList<>(); - - this.symbolProviders = new HashMap<>(); - RequestMappingSymbolProvider.register(this.symbolProviders); + this.symbolProviders = specificProviders; } @Override diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/CompletionProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/CompletionProvider.java new file mode 100644 index 000000000..ae1ec5148 --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/CompletionProvider.java @@ -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.handlers; + +import java.util.Collection; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +/** + * @author Martin Lippert + */ +public interface CompletionProvider { + + Collection provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type, int offset, IDocument doc); + +} diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java new file mode 100644 index 000000000..dd0c96f80 --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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.handlers; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.lsp4j.Location; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public interface ReferenceProvider { + + CompletableFuture> provideReferences(ASTNode node, Annotation annotation, + ITypeBinding type, int offset, TextDocument doc); + +} diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingAssistProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingCompletionProcessor.java similarity index 92% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingAssistProvider.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingCompletionProcessor.java index 2559a031b..94cae9318 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingAssistProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingCompletionProcessor.java @@ -13,6 +13,6 @@ package org.springframework.ide.vscode.boot.java.requestmapping; /** * @author Martin Lippert */ -public class RequestMappingAssistProvider { +public class RequestMappingCompletionProcessor { } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index 9b6ededa5..31c2d3de9 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -38,17 +38,6 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class RequestMappingHoverProvider implements HoverProvider { - public static void register(Map hoverProviders) { - RequestMappingHoverProvider provider = new RequestMappingHoverProvider(); - - hoverProviders.put(Constants.SPRING_REQUEST_MAPPING, provider); - hoverProviders.put(Constants.SPRING_GET_MAPPING, provider); - hoverProviders.put(Constants.SPRING_POST_MAPPING, provider); - hoverProviders.put(Constants.SPRING_PUT_MAPPING, provider); - hoverProviders.put(Constants.SPRING_DELETE_MAPPING, provider); - hoverProviders.put(Constants.SPRING_PATCH_MAPPING, provider); - } - @Override public CompletableFuture provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc) { diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java index 60cac2ce5..3ee272ca0 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingSymbolProvider.java @@ -10,8 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; -import java.util.Map; - import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.SymbolInformation; @@ -24,24 +22,6 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class RequestMappingSymbolProvider implements SymbolProvider { - private static RequestMappingSymbolProvider symbolProvider; - - public static void register(Map symbolProviders) { - - synchronized(RequestMappingSymbolProvider.class) { - if (symbolProvider == null) { - symbolProvider = new RequestMappingSymbolProvider(); - } - } - - symbolProviders.put(Constants.SPRING_REQUEST_MAPPING, symbolProvider); - symbolProviders.put(Constants.SPRING_GET_MAPPING, symbolProvider); - symbolProviders.put(Constants.SPRING_POST_MAPPING, symbolProvider); - symbolProviders.put(Constants.SPRING_PUT_MAPPING, symbolProvider); - symbolProviders.put(Constants.SPRING_DELETE_MAPPING, symbolProvider); - symbolProviders.put(Constants.SPRING_PATCH_MAPPING, symbolProvider); - } - @Override public SymbolInformation getSymbol(Annotation node, TextDocument doc) { try { diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/Constants.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/Constants.java new file mode 100644 index 000000000..a9b588083 --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/Constants.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * 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.scope; + +/** + * @author Martin Lippert + */ +public class Constants { + + public static final String SPRING_SCOPE = "org.springframework.context.annotation.Scope"; + +} diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeCompletionProcessor.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeCompletionProcessor.java similarity index 84% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeCompletionProcessor.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeCompletionProcessor.java index cea007c94..c997ef076 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeCompletionProcessor.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeCompletionProcessor.java @@ -8,8 +8,10 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.completions; +package org.springframework.ide.vscode.boot.java.scope; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.eclipse.jdt.core.dom.ASTNode; @@ -18,26 +20,30 @@ import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MemberValuePair; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StringLiteral; +import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; import org.springframework.ide.vscode.commons.util.text.IDocument; /** * @author Martin Lippert */ -public class ScopeCompletionProcessor { +public class ScopeCompletionProcessor implements CompletionProvider { + + @Override + public Collection provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type, + int offset, IDocument doc) { + + List result = new ArrayList<>(); - public void collectCompletionsForScopeAnnotation(ASTNode node, Annotation annotation, ITypeBinding type, - List completions, int offset, IDocument doc) { - try { if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair) { MemberValuePair memberPair = (MemberValuePair) node.getParent(); - + // case: @Scope(value=<*>) if ("value".equals(memberPair.getName().toString()) && memberPair.getValue().toString().equals("$missing$")) { for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) { ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, offset, offset, ""); - completions.add(proposal); + result.add(proposal); } } } @@ -45,7 +51,7 @@ public class ScopeCompletionProcessor { else if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) { for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) { ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, offset, offset, ""); - completions.add(proposal); + result.add(proposal); } } else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) { @@ -55,21 +61,21 @@ public class ScopeCompletionProcessor { for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) { if (completion.getValue().startsWith(prefix)) { ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, node.getStartPosition(), node.getStartPosition() + node.getLength(), prefix); - completions.add(proposal); + result.add(proposal); } } } } else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair) { MemberValuePair memberPair = (MemberValuePair) node.getParent(); - + // case: @Scope(value=<*>) if ("value".equals(memberPair.getName().toString()) && node.toString().startsWith("\"") && node.toString().endsWith("\"")) { String prefix = doc.get(node.getStartPosition(), offset - node.getStartPosition()); for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) { if (completion.getValue().startsWith(prefix)) { ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, node.getStartPosition(), node.getStartPosition() + node.getLength(), prefix); - completions.add(proposal); + result.add(proposal); } } } @@ -78,6 +84,8 @@ public class ScopeCompletionProcessor { catch (Exception e) { e.printStackTrace(); } + + return result; } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeNameCompletion.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeNameCompletion.java similarity index 95% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeNameCompletion.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeNameCompletion.java index 35ab00b9f..033020d06 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeNameCompletion.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeNameCompletion.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.completions; +package org.springframework.ide.vscode.boot.java.scope; import org.eclipse.lsp4j.CompletionItemKind; import org.springframework.ide.vscode.commons.util.Renderable; diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeNameCompletionProposal.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeNameCompletionProposal.java similarity index 97% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeNameCompletionProposal.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeNameCompletionProposal.java index cab7e72c8..02bcf6d85 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ScopeNameCompletionProposal.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeNameCompletionProposal.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.completions; +package org.springframework.ide.vscode.boot.java.scope; import org.eclipse.lsp4j.CompletionItemKind; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java similarity index 80% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java index f0bfa5194..81a41255a 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java @@ -8,10 +8,12 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.completions; +package org.springframework.ide.vscode.boot.java.value; import static org.springframework.ide.vscode.commons.util.StringUtil.camelCaseToHyphens; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.eclipse.jdt.core.dom.ASTNode; @@ -21,6 +23,8 @@ import org.eclipse.jdt.core.dom.MemberValuePair; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StringLiteral; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; +import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider; +import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; import org.springframework.ide.vscode.commons.util.BadLocationException; @@ -31,71 +35,78 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; /** * @author Martin Lippert */ -public class ValueCompletionProcessor { +public class ValueCompletionProcessor implements CompletionProvider { - private FuzzyMap index; - - public ValueCompletionProcessor(FuzzyMap index) { - this.index = index; + private final SpringPropertyIndexProvider indexProvider; + + public ValueCompletionProcessor(SpringPropertyIndexProvider indexProvider) { + this.indexProvider = indexProvider; } - public void collectCompletionsForValueAnnotation(ASTNode node, Annotation annotation, ITypeBinding type, - List completions, int offset, IDocument doc) { - + @Override + public Collection provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type, + int offset, IDocument doc) { + + List result = new ArrayList<>(); + try { + FuzzyMap index = indexProvider.getIndex(doc); + // case: @Value(<*>) if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) { - List> matches = findMatches(""); - + List> matches = findMatches("", index); + for (Match match : matches) { DocumentEdits edits = new DocumentEdits(doc); edits.replace(offset, offset, "\"${" + match.data.getId() + "}\""); - + ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null); - completions.add(proposal); + result.add(proposal); } } // case: @Value(prefix<*>) else if (node instanceof SimpleName && node.getParent() instanceof Annotation) { - computeProposalsForSimpleName(node, completions, offset, doc); + computeProposalsForSimpleName(node, result, offset, doc, index); } // case: @Value(value=<*>) else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { - computeProposalsForSimpleName(node, completions, offset, doc); + computeProposalsForSimpleName(node, result, offset, doc, index); } // case: @Value("prefix<*>") else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) { if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - computeProposalsForStringLiteral(node, completions, offset, doc); + computeProposalsForStringLiteral(node, result, offset, doc, index); } } // case: @Value(value="prefix<*>") else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - computeProposalsForStringLiteral(node, completions, offset, doc); + computeProposalsForStringLiteral(node, result, offset, doc, index); } } } catch (Exception e) { e.printStackTrace(); } + + return result; } private void computeProposalsForSimpleName(ASTNode node, List completions, int offset, - IDocument doc) { + IDocument doc, FuzzyMap index) { String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition()); - + int startOffset = node.getStartPosition(); int endOffset = node.getStartPosition() + node.getLength(); String proposalPrefix = "\""; String proposalPostfix = "\""; - List> matches = findMatches(prefix); - + List> matches = findMatches(prefix, index); + for (Match match : matches) { DocumentEdits edits = new DocumentEdits(doc); @@ -107,12 +118,12 @@ public class ValueCompletionProcessor { } private void computeProposalsForStringLiteral(ASTNode node, List completions, int offset, - IDocument doc) throws BadLocationException { + IDocument doc, FuzzyMap index) throws BadLocationException { String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, offset - (node.getStartPosition() + 1)), offset - (node.getStartPosition() + 1)); - + int startOffset = offset - prefix.length(); int endOffset = offset; - + String prePrefix = doc.get(node.getStartPosition() + 1, offset - prefix.length() - node.getStartPosition() - 1); String preCompletion; @@ -125,12 +136,12 @@ public class ValueCompletionProcessor { else { preCompletion = "${"; } - + String fullNodeContent = doc.get(node.getStartPosition(), node.getLength()); String postCompletion = isClosingBracketMissing(fullNodeContent + preCompletion) ? "}" : ""; - List> matches = findMatches(prefix); - + List> matches = findMatches(prefix, index); + for (Match match : matches) { DocumentEdits edits = new DocumentEdits(doc); @@ -140,10 +151,10 @@ public class ValueCompletionProcessor { completions.add(proposal); } } - + private boolean isClosingBracketMissing(String fullNodeContent) { int bracketOpens = 0; - + for (int i = 0; i < fullNodeContent.length(); i++) { if (fullNodeContent.charAt(i) == '{') { bracketOpens++; @@ -152,13 +163,13 @@ public class ValueCompletionProcessor { bracketOpens--; } } - + return bracketOpens > 0; } public String identifyPropertyPrefix(String nodeContent, int offset) { String result = nodeContent.substring(0, offset); - + int i = offset - 1; while (i >= 0) { char c = nodeContent.charAt(i); @@ -168,11 +179,11 @@ public class ValueCompletionProcessor { } i--; } - + return result; } - private List> findMatches(String prefix) { + private List> findMatches(String prefix, FuzzyMap index) { List> matches = index.find(camelCaseToHyphens(prefix)); return matches; } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValuePropertyKeyProposal.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyKeyProposal.java similarity index 96% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValuePropertyKeyProposal.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyKeyProposal.java index 296e40d3f..e07878d89 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValuePropertyKeyProposal.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyKeyProposal.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.completions; +package org.springframework.ide.vscode.boot.java.value; import org.eclipse.lsp4j.CompletionItemKind; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ValuePropertyReferencesProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java similarity index 96% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ValuePropertyReferencesProvider.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java index f01d0a9bd..8b015cad9 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ValuePropertyReferencesProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.handlers; +package org.springframework.ide.vscode.boot.java.value; import static org.springframework.ide.vscode.commons.yaml.ast.NodeUtil.asScalar; @@ -33,6 +33,7 @@ import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -52,15 +53,16 @@ import org.yaml.snakeyaml.nodes.NodeTuple; /** * @author Martin Lippert */ -public class ValuePropertyReferencesProvider { - +public class ValuePropertyReferencesProvider implements ReferenceProvider { + private SimpleLanguageServer languageServer; - public ValuePropertyReferencesProvider(SimpleLanguageServer languageServer) { - this.languageServer = languageServer; + public ValuePropertyReferencesProvider(SimpleLanguageServer server) { + this.languageServer = server; } - public CompletableFuture> provideReferencesForValueAnnotation(ASTNode node, Annotation annotation, + @Override + public CompletableFuture> provideReferences(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc) { try { @@ -102,10 +104,10 @@ public class ValuePropertyReferencesProvider { return null; } - + public CompletableFuture> findReferencesFromPropertyFiles(Path workspaceRoot, String propertyKey) { - + try (Stream walk = Files.walk(workspaceRoot)) { List locations = walk .filter(path -> isPropertiesFile(path)) @@ -113,19 +115,19 @@ public class ValuePropertyReferencesProvider { .map(path -> findReferences(path, propertyKey)) .flatMap(Collection::stream) .collect(Collectors.toList()); - + return CompletableFuture.completedFuture(locations); } catch (Exception e) { e.printStackTrace(); } - + return null; } private boolean isPropertiesFile(Path path) { Path fileName = path.getFileName(); - + if (fileName.toString().endsWith(".properties") || path.toString().endsWith(".yml")) { return fileName.toString().contains("application"); } @@ -147,24 +149,24 @@ public class ValuePropertyReferencesProvider { private List findReferencesInYMLFile(String filePath, String propertyKey) { List foundLocations = new ArrayList<>(); - + try { String fileContent = FileUtils.readFileToString(new File(filePath)); - + Yaml yaml = new Yaml(); YamlASTProvider parser = new YamlParser(yaml); - + URI docURI = Paths.get(filePath).toUri(); TextDocument doc = new TextDocument(docURI.toString(), null); doc.setText(fileContent); YamlFileAST ast = parser.getAST(doc); - + List nodes = ast.getNodes(); if (nodes != null && !nodes.isEmpty()) { for (Node node : nodes) { Node foundNode = findNode(node, "", propertyKey); if (foundNode != null) { - + Position start = new Position(); start.setLine(foundNode.getStartMark().getLine()); start.setCharacter(foundNode.getStartMark().getColumn()); @@ -190,15 +192,15 @@ public class ValuePropertyReferencesProvider { return foundLocations; } - + protected Node findNode(Node node, String prefix, String propertyKey) { if (node.getNodeId().equals(NodeId.mapping)) { for (NodeTuple entry : ((MappingNode)node).getValue()) { Node keyNode = entry.getKeyNode(); String key = asScalar(keyNode); - + String combinedKey = prefix.length() > 0 ? prefix + "." + key : key; - + if (combinedKey != null && combinedKey.equals(propertyKey)) { return keyNode; } @@ -210,19 +212,19 @@ public class ValuePropertyReferencesProvider { } } } - + return null; } private List findReferencesInPropertiesFile(String filePath, String propertyKey) { List foundLocations = new ArrayList<>(); - + try { String fileContent = FileUtils.readFileToString(new File(filePath)); Parser parser = new AntlrParser(); ParseResults parseResults = parser.parse(fileContent); - + if (parseResults != null && parseResults.ast != null) { parseResults.ast.getNodes(KeyValuePair.class).forEach(pair -> { if (pair.getKey() != null && pair.getKey().decode().equals(propertyKey)) { @@ -256,7 +258,7 @@ public class ValuePropertyReferencesProvider { } }); } - + } catch (Exception e) { e.printStackTrace(); } diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java index 6f83c9acf..20f501ee7 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java @@ -52,7 +52,7 @@ public class ScopeCompletionTest { } }; - private LanguageServerHarness harness; + private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; private IJavaProject testProject; @@ -64,7 +64,7 @@ public class ScopeCompletionTest { testProject = ProjectsHarness.INSTANCE.mavenProject("test-annotations"); indexHarness = new PropertyIndexHarness(); - harness = new LanguageServerHarness(new Callable() { + harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { BootJavaLanguageServer server = new BootJavaLanguageServer(javaProjectFinder, indexHarness.getIndexProvider()); diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java index a0102890f..5a312f1d2 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java @@ -23,7 +23,7 @@ import org.eclipse.lsp4j.CompletionItem; import org.junit.Before; import org.junit.Test; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; -import org.springframework.ide.vscode.boot.java.completions.ValueCompletionProcessor; +import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.util.text.IDocument; @@ -53,7 +53,7 @@ public class ValueCompletionTest { } }; - private LanguageServerHarness harness; + private LanguageServerHarness harness; private IJavaProject testProject; private Editor editor; @@ -65,7 +65,7 @@ public class ValueCompletionTest { testProject = ProjectsHarness.INSTANCE.mavenProject("test-annotations"); indexHarness = new PropertyIndexHarness(); - harness = new LanguageServerHarness(new Callable() { + harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { BootJavaLanguageServer server = new BootJavaLanguageServer(javaProjectFinder, indexHarness.getIndexProvider()); diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/references/test/PropertyReferenceFinderTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/references/test/PropertyReferenceFinderTest.java index fcd2c0c7c..3ffec6073 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/references/test/PropertyReferenceFinderTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/references/test/PropertyReferenceFinderTest.java @@ -21,7 +21,7 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.Location; import org.junit.Test; -import org.springframework.ide.vscode.boot.java.handlers.ValuePropertyReferencesProvider; +import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider; import org.springframework.ide.vscode.project.harness.ProjectsHarness; /**