From 0a47b7c1583b588aeea2df4a7508dd0c9ba04937 Mon Sep 17 00:00:00 2001 From: nsingh Date: Wed, 20 Sep 2017 13:27:28 -0700 Subject: [PATCH] PT 150711216 - Initial implementation Includes basic completion of a request mapping snippet --- .../boot/java/BootJavaLanguageServer.java | 23 +++++- .../handlers/BootJavaCompletionEngine.java | 11 ++- .../handlers/SimpleCompletionFactory.java | 80 ++++++++++++------- .../boot/java/snippets/JavaSnippet.java | 53 +++++++++++- ...etContext.java => JavaSnippetContext.java} | 5 +- .../java/snippets/JavaSnippetManager.java | 63 +++++++++++++++ 6 files changed, 203 insertions(+), 32 deletions(-) rename headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/{SnippetContext.java => JavaSnippetContext.java} (79%) create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetManager.java 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 70b6fdd42..485176690 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 @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java; import java.util.HashMap; import java.util.Map; +import org.eclipse.lsp4j.CompletionItemKind; 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; @@ -29,6 +30,9 @@ 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.snippets.JavaSnippet; +import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager; +import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetContext; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor; import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider; @@ -52,6 +56,8 @@ import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; import org.springframework.ide.vscode.commons.util.text.TextDocument; +import com.google.common.collect.ImmutableList; + /** * Language Server for Spring Boot Application Properties files * @@ -109,7 +115,22 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { 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); + JavaSnippetManager snippetManager = new JavaSnippetManager(this::createSnippetBuilder); + snippetManager.add(new JavaSnippet( + "RequestMapping method", + JavaSnippetContext.BOOT_MEMBERS, + CompletionItemKind.Method, + ImmutableList.of( + "org.springframework.web.bind.annotation.RequestMapping", + "org.springframework.web.bind.annotation.RequestMethod", + "org.springframework.web.bind.annotation.RequestParam" + ), + "@RequestMapping(value=\"${path}\", method=RequestMethod.${GET})\n" + + "public ${SomeData} ${requestMethodName}(@RequestParam ${String} ${param}) {\n" + + " return new ${SomeData}(${cursor});\n" + + "}\n" + )); + return new BootJavaCompletionEngine(javaProjectFinder, providers, snippetManager); } protected HoverHandler createHoverHandler(JavaProjectFinder javaProjectFinder) { diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java index d96daf91d..82295b53a 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.boot.java.handlers; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -24,6 +25,7 @@ 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.java.snippets.JavaSnippetManager; 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; @@ -38,10 +40,12 @@ public class BootJavaCompletionEngine implements ICompletionEngine { private JavaProjectFinder projectFinder; private Map completionProviders; + private JavaSnippetManager snippets; - public BootJavaCompletionEngine(JavaProjectFinder projectFinder, Map specificProviders) { + public BootJavaCompletionEngine(JavaProjectFinder projectFinder, Map specificProviders, JavaSnippetManager snippets) { this.projectFinder = projectFinder; this.completionProviders = specificProviders; + this.snippets = snippets; } @Override @@ -69,7 +73,10 @@ public class BootJavaCompletionEngine implements ICompletionEngine { if (node != null) { System.out.println("AST node found: " + node.getClass().getName()); - return collectCompletionsForAnnotations(node, offset, document); + Collection completions = new ArrayList(); + completions.addAll(collectCompletionsForAnnotations(node, offset, document)); + completions.addAll(snippets.getCompletions(document, offset, node)); + return completions; } return Collections.emptyList(); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SimpleCompletionFactory.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SimpleCompletionFactory.java index 256e6c167..018f68865 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SimpleCompletionFactory.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/SimpleCompletionFactory.java @@ -18,34 +18,60 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; public class SimpleCompletionFactory { - public ICompletionProposal simpleProposal(IDocument doc, int offset, String query, CompletionItemKind kind, String value, String detail, Renderable info) { + + public static class SimpleProposal implements ICompletionProposal{ + + private DocumentEdits edits; + private CompletionItemKind kind; + private Renderable info; + private String detail; + private String label; + + public SimpleProposal(DocumentEdits edits, CompletionItemKind kind, Renderable info, + String detail, String label) { + this.edits = edits; + this.kind = kind; + this.info = info; + this.detail = detail; + this.label = label; + } + + public SimpleProposal setLabel(String label) { + this.label = label; + return this; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public DocumentEdits getTextEdit() { + return edits; + } + + @Override + public CompletionItemKind getKind() { + return kind; + } + + @Override + public Renderable getDocumentation() { + return info; + } + + @Override + public String getDetail() { + return detail; + } + + + } + + public static SimpleProposal simpleProposal(IDocument doc, int offset, String query, CompletionItemKind kind, String value, String detail, Renderable info) { DocumentEdits edits = new DocumentEdits(doc); edits.replace(offset-query.length(), offset, value); - return new ICompletionProposal() { - @Override - public DocumentEdits getTextEdit() { - return edits; - } - - @Override - public String getLabel() { - return value; - } - - @Override - public CompletionItemKind getKind() { - return kind; - } - - @Override - public Renderable getDocumentation() { - return info; - } - - @Override - public String getDetail() { - return detail; - } - }; + return new SimpleProposal(edits, kind, info, detail, value); } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippet.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippet.java index d3610b6b5..0ca9fa08e 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippet.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippet.java @@ -10,8 +10,59 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.snippets; +import java.util.List; +import java.util.Optional; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.lsp4j.CompletionItemKind; +import org.springframework.ide.vscode.boot.java.handlers.SimpleCompletionFactory; +import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; +import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder; +import org.springframework.ide.vscode.commons.util.Renderables; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +import com.google.common.base.Supplier; + public class JavaSnippet { - private SnippetContext context; + private JavaSnippetContext context; + + private String name; + + private String template; + + private List imports; + + private CompletionItemKind kind; + + public JavaSnippet(String name, JavaSnippetContext context, CompletionItemKind kind, List imports, + String template) { + super(); + this.context = context; + this.name = name; + this.template = template; + this.imports = imports; + this.kind = kind; + } + + public Optional generateCompletion(Supplier snippetBuilderFactory, + IDocument doc, int offset, ASTNode node, String query) { + + if (context.appliesTo(node)) { + return Optional.of( + SimpleCompletionFactory.simpleProposal(doc, + offset, + query, + kind, template, "Snippet", Renderables.NO_DESCRIPTION + ).setLabel(name) + ); + } + + return Optional.empty(); + } + + public String getName() { + return this.name; + } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/SnippetContext.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetContext.java similarity index 79% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/SnippetContext.java rename to headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetContext.java index a37043c61..f67ffd6e1 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/SnippetContext.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetContext.java @@ -11,7 +11,10 @@ package org.springframework.ide.vscode.boot.java.snippets; import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.TypeDeclaration; + +public interface JavaSnippetContext { + JavaSnippetContext BOOT_MEMBERS = (node) -> node instanceof TypeDeclaration; -public interface SnippetContext { boolean appliesTo(ASTNode node); } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetManager.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetManager.java new file mode 100644 index 000000000..ce5fef47e --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetManager.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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.snippets; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; +import org.springframework.ide.vscode.commons.languageserver.util.PrefixFinder; +import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder; +import org.springframework.ide.vscode.commons.util.FuzzyMatcher; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +import com.google.common.base.Supplier; + +public class JavaSnippetManager { + + private List snippets = new ArrayList<>(); + private Supplier snippetBuilderFactory; + + private static PrefixFinder PREFIX_FINDER = new PrefixFinder() { + + @Override + protected boolean isPrefixChar(char c) { + return Character.isJavaIdentifierPart(c); + } + }; + + public JavaSnippetManager(Supplier snippetBuilderFactory) { + this.snippetBuilderFactory = snippetBuilderFactory; + } + + public void add(JavaSnippet javaSnippet) { + snippets.add(javaSnippet); + + } + + public Collection getCompletions(IDocument doc, int offset, ASTNode node) { + Collection completions = new ArrayList<>(); + + String query = PREFIX_FINDER.getPrefix(doc, offset); + + for (JavaSnippet javaSnippet : snippets) { + if (FuzzyMatcher.matchScore(query, javaSnippet.getName()) != 0) { + javaSnippet.generateCompletion(snippetBuilderFactory, doc, offset, node, query) + .ifPresent((completion) -> completions.add(completion)); + } + } + + return completions; + } + +}