Skip to content

Commit

Permalink
GH-758: additional steps towards improved request mapping code comple…
Browse files Browse the repository at this point in the history
…tions using snippets
  • Loading branch information
martinlippert committed Nov 21, 2023
1 parent 2c77f6d commit d925d12
Show file tree
Hide file tree
Showing 15 changed files with 612 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ private CompletionItem adaptItem(TextDocument doc, ICompletionProposal completio
item.setKind(completion.getKind());
item.setSortText(sortkeys.next());
item.setFilterText(completion.getFilterText());
item.setInsertTextMode(InsertTextMode.AsIs);
if (completion.isDeprecated()) {
item.setDeprecated(completion.isDeprecated());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,35 +47,39 @@ public class BootJavaCompletionEngineConfigurer {
// TODO: REMOVE this check once STS3 is no longer supported
if (LspClient.currentClient() != LspClient.Client.ECLIPSE) {
snippetManager.add(
new JavaSnippet("RequestMapping method", JavaSnippetContext.BOOT_MEMBERS, CompletionItemKind.Method,
new JavaSnippet("@RequestMapping(..) {..}", JavaSnippetContext.AT_ROOT_LEVEL, 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"
"@RequestMapping(\"${path}\", method=RequestMethod.${GET})\n"
+ "public ${SomeData} ${requestMethodName}(@RequestParam ${String} ${param}) {\n"
+ " return new ${SomeData}(${cursor});\n" + "}\n"));
snippetManager
.add(new JavaSnippet("GetMapping method", JavaSnippetContext.BOOT_MEMBERS, CompletionItemKind.Method,
+ " return new ${SomeData}(${cursor});\n" + "}\n",
"RequestMapping"));
snippetManager.add(
new JavaSnippet("@GetMapping(..) {..}", JavaSnippetContext.AT_ROOT_LEVEL, CompletionItemKind.Method,
ImmutableList.of("org.springframework.web.bind.annotation.GetMapping",
"org.springframework.web.bind.annotation.RequestParam"),
"@GetMapping(value=\"${path}\")\n"
"@GetMapping(\"${path}\")\n"
+ "public ${SomeData} ${getMethodName}(@RequestParam ${String} ${param}) {\n"
+ " return new ${SomeData}(${cursor});\n" + "}\n"));
snippetManager.add(new JavaSnippet("PostMapping method", JavaSnippetContext.BOOT_MEMBERS,
CompletionItemKind.Method,
ImmutableList.of("org.springframework.web.bind.annotation.PostMapping",
"org.springframework.web.bind.annotation.RequestBody"),
"@PostMapping(value=\"${path}\")\n"
+ "public ${SomeEnityData} ${postMethodName}(@RequestBody ${SomeEnityData} ${entity}) {\n"
+ " //TODO: process POST request\n" + " ${cursor}\n" + " return ${entity};\n" + "}\n"));
snippetManager.add(new JavaSnippet("PutMapping method", JavaSnippetContext.BOOT_MEMBERS,
CompletionItemKind.Method,
ImmutableList.of("org.springframework.web.bind.annotation.PutMapping",
"org.springframework.web.bind.annotation.RequestBody",
"org.springframework.web.bind.annotation.PathVariable"),
"@PutMapping(value=\"${path}/{${id}}\")\n"
+ "public ${SomeEnityData} ${putMethodName}(@PathVariable ${pvt:String} ${id}, @RequestBody ${SomeEnityData} ${entity}) {\n"
+ " //TODO: process PUT request\n" + " ${cursor}\n" + " return ${entity};\n" + "}"));
+ " return new ${SomeData}(${cursor});\n" + "}\n",
"GetMapping"));
snippetManager.add(
new JavaSnippet("@PostMapping(..) {..}", JavaSnippetContext.AT_ROOT_LEVEL, CompletionItemKind.Method,
ImmutableList.of("org.springframework.web.bind.annotation.PostMapping",
"org.springframework.web.bind.annotation.RequestBody"),
"@PostMapping(\"${path}\")\n"
+ "public ${SomeEnityData} ${postMethodName}(@RequestBody ${SomeEnityData} ${entity}) {\n"
+ " //TODO: process POST request\n" + " ${cursor}\n" + " return ${entity};\n" + "}\n",
"PostMapping"));
snippetManager.add(
new JavaSnippet("@PutMapping(..) {..}", JavaSnippetContext.AT_ROOT_LEVEL, CompletionItemKind.Method,
ImmutableList.of("org.springframework.web.bind.annotation.PutMapping",
"org.springframework.web.bind.annotation.RequestBody",
"org.springframework.web.bind.annotation.PathVariable"),
"@PutMapping(\"${path}/{${id}}\")\n"
+ "public ${SomeEnityData} ${putMethodName}(@PathVariable ${pvt:String} ${id}, @RequestBody ${SomeEnityData} ${entity}) {\n"
+ " //TODO: process PUT request\n" + " ${cursor}\n" + " return ${entity};\n" + "}",
"PutMapping"));
}

return snippetManager;
Expand All @@ -97,4 +101,5 @@ BootJavaCompletionEngine javaCompletionEngine(

return new BootJavaCompletionEngine(cuCache, providers, snippetManager);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2023 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
Expand All @@ -26,32 +26,33 @@ public class JavaSnippet {
private JavaSnippetContext context;

private String name;

private String template;

private List<String> imports;

private CompletionItemKind kind;
private String additionalTriggerPrefix;

public JavaSnippet(String name, JavaSnippetContext context, CompletionItemKind kind, List<String> imports,
String template) {
String template, String additionalTriggerPrefix) {
super();
this.context = context;
this.name = name;
this.template = template;
this.imports = imports;
this.kind = kind;
this.additionalTriggerPrefix = additionalTriggerPrefix;
}

public Optional<ICompletionProposal> generateCompletion(Supplier<SnippetBuilder> snippetBuilderFactory,
DocumentRegion query, ASTNode node, CompilationUnit cu) {
DocumentRegion query, ASTNode node, CompilationUnit cu, String filterText) {

if (context.appliesTo(node)) {
return Optional.of(
new JavaSnippetCompletion(snippetBuilderFactory,
query,
cu,
this
this,
filterText
)
);
}
Expand All @@ -75,4 +76,8 @@ public CompletionItemKind getKind() {
return kind;
}

public String getAdditionalTriggerPrefix() {
return additionalTriggerPrefix;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,20 @@
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;

public class JavaSnippetCompletion implements ICompletionProposal{
public class JavaSnippetCompletion implements ICompletionProposal {

private DocumentRegion query;
private JavaSnippet javaSnippet;
private Supplier<SnippetBuilder> snippetBuilderFactory;
private CompilationUnit cu;
private final DocumentRegion query;
private final JavaSnippet javaSnippet;
private final Supplier<SnippetBuilder> snippetBuilderFactory;
private final CompilationUnit cu;
private final String filterText;

public JavaSnippetCompletion(Supplier<SnippetBuilder> snippetBuilderFactory, DocumentRegion query, CompilationUnit cu, JavaSnippet javaSnippet) {
public JavaSnippetCompletion(Supplier<SnippetBuilder> snippetBuilderFactory, DocumentRegion query, CompilationUnit cu, JavaSnippet javaSnippet, String filterText) {
this.snippetBuilderFactory = snippetBuilderFactory;
this.query = query;
this.cu = cu;
this.javaSnippet = javaSnippet;
this.filterText = filterText;
}

@Override
Expand Down Expand Up @@ -66,4 +68,9 @@ public Renderable getDocumentation() {
public Optional<java.util.function.Supplier<DocumentEdits>> getAdditionalEdit() {
return javaSnippet.getImports().map(imports -> () -> ASTUtils.getImportsEdit(cu, imports, query.getDocument()).orElse(null));
}

@Override
public String getFilterText() {
return this.filterText;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2023 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
Expand All @@ -11,10 +11,16 @@
package org.springframework.ide.vscode.boot.java.snippets;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.TypeDeclaration;

public interface JavaSnippetContext {
JavaSnippetContext BOOT_MEMBERS = (node) -> node instanceof TypeDeclaration;
JavaSnippetContext BOOT_MEMBERS = (node) -> node instanceof TypeDeclaration || node instanceof SimpleName;

JavaSnippetContext AT_ROOT_LEVEL = (node) -> {
return (node instanceof TypeDeclaration) || (node instanceof SimpleName && node.getParent() != null && node.getParent() instanceof TypeDeclaration);
};


boolean appliesTo(ASTNode node);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2018 Pivotal, Inc.
* Copyright (c) 2017, 2023 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
Expand All @@ -21,7 +21,6 @@
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.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.IDocument;

Expand All @@ -34,7 +33,7 @@ public class JavaSnippetManager {

@Override
protected boolean isPrefixChar(char c) {
return Character.isJavaIdentifierPart(c);
return Character.isJavaIdentifierPart(c) || c == '@';
}
};

Expand All @@ -47,19 +46,23 @@ public void add(JavaSnippet javaSnippet) {

}

public void getCompletions(IDocument doc, int offset, ASTNode node, CompilationUnit cu, Collection<ICompletionProposal> completions) {
public void getCompletions(IDocument doc, int offset, ASTNode node, CompilationUnit cu,
Collection<ICompletionProposal> completions) {
// check if current offset is within the range of possible compiler problems
if (Arrays.stream(cu.getProblems()).anyMatch(p -> p.getSourceStart() <= offset && offset <= p.getSourceEnd())) {
return;
}

DocumentRegion query = PREFIX_FINDER.getPrefixRegion(doc, offset);

for (JavaSnippet javaSnippet : snippets) {
if (FuzzyMatcher.matchScore(query.toString(), javaSnippet.getName()) != 0
|| FuzzyMatcher.matchScore(query.toString(), javaSnippet.getTemplate()) != 0) {

javaSnippet.generateCompletion(snippetBuilderFactory, query, node, cu)
if (javaSnippet.getName().toLowerCase().startsWith(query.toString().toLowerCase())) {
javaSnippet.generateCompletion(snippetBuilderFactory, query, node, cu, javaSnippet.getName())
.ifPresent((completion) -> completions.add(completion));

} else if (javaSnippet.getAdditionalTriggerPrefix().toLowerCase().startsWith(query.toString().toLowerCase())) {
javaSnippet.generateCompletion(snippetBuilderFactory, query, node, cu, javaSnippet.getAdditionalTriggerPrefix())
.ifPresent((completion) -> completions.add(completion));
}
}
Expand Down
Loading

0 comments on commit d925d12

Please sign in to comment.