Snippets and Spring Data completions
This commit is contained in:
@@ -15,13 +15,15 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.openrewrite.internal.lang.Nullable;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.Annotation;
|
||||
import org.openrewrite.java.tree.J.ClassDeclaration;
|
||||
import org.openrewrite.java.tree.JavaType;
|
||||
import org.openrewrite.java.tree.JavaType.FullyQualified;
|
||||
import org.openrewrite.java.tree.JavaType.Parameterized;
|
||||
import org.openrewrite.java.tree.TypeUtils;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
@@ -104,42 +106,32 @@ public class DataRepositoryCompletionProcessor implements CompletionProvider {
|
||||
}
|
||||
|
||||
private DataRepositoryDefinition getDataRepositoryDefinition(ClassDeclaration declaration, FullyQualified type) {
|
||||
if (type != null) {
|
||||
|
||||
// interface analysis
|
||||
List<FullyQualified> interfaces = type.getInterfaces();
|
||||
for (FullyQualified resolvedInterface : interfaces) {
|
||||
String simplifiedType = resolvedInterface.getFullyQualifiedName();
|
||||
|
||||
if (Constants.REPOSITORY_TYPE.equals(simplifiedType)) {
|
||||
DomainType domainType = null;
|
||||
// TODO Fix for OR AST
|
||||
// if (resolvedInterface.isParameterizedType()) {
|
||||
// ITypeBinding[] typeParameters = resolvedInterface.getTypeArguments();
|
||||
// if (typeParameters != null && typeParameters.length > 0) {
|
||||
// domainType = new DomainType(typeParameters[0]);
|
||||
// }
|
||||
// }
|
||||
return createDataRepositoryDefinitionFromType(domainType);
|
||||
}
|
||||
else {
|
||||
DataRepositoryDefinition repo = getDataRepositoryDefinition(declaration, resolvedInterface);
|
||||
if (repo != null) {
|
||||
return repo;
|
||||
}
|
||||
}
|
||||
if (type != null && TypeUtils.isAssignableTo(Constants.REPOSITORY_TYPE, type)) {
|
||||
JavaType domainType = getFirstParameterType(type);
|
||||
return new DataRepositoryDefinition(domainType instanceof FullyQualified ? new DomainType((FullyQualified) domainType) : null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private JavaType getFirstParameterType(FullyQualified type) {
|
||||
if (type instanceof Parameterized) {
|
||||
List<JavaType> params = ((Parameterized) type).getTypeParameters();
|
||||
if (!params.isEmpty()) {
|
||||
return params.get(0);
|
||||
}
|
||||
|
||||
// super type analysis
|
||||
FullyQualified superclass = type.getSupertype();
|
||||
if (superclass != null) {
|
||||
return getDataRepositoryDefinition(declaration, superclass);
|
||||
}
|
||||
for (FullyQualified i : type.getInterfaces()) {
|
||||
JavaType parameType = getFirstParameterType(i);
|
||||
if (parameType != null) {
|
||||
return parameType;
|
||||
}
|
||||
}
|
||||
@Nullable
|
||||
FullyQualified superclass = type.getSupertype();
|
||||
if (superclass != null) {
|
||||
return getFirstParameterType(superclass);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private DataRepositoryDefinition createDataRepositoryDefinitionFromType(DomainType domainType) {
|
||||
return new DataRepositoryDefinition(domainType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Pivotal, Inc.
|
||||
* Copyright (c) 2018, 2022 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
|
||||
@@ -13,8 +13,9 @@ package org.springframework.ide.vscode.boot.java.data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.IMethodBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.openrewrite.java.tree.JavaType;
|
||||
import org.openrewrite.java.tree.JavaType.FullyQualified;
|
||||
import org.openrewrite.java.tree.JavaType.Method;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
@@ -35,22 +36,25 @@ public class DomainType {
|
||||
this.simpleName = simpleName;
|
||||
}
|
||||
|
||||
public DomainType(ITypeBinding typeBinding) {
|
||||
this.packageName = typeBinding.getPackage().getName();
|
||||
this.fullName = typeBinding.getQualifiedName();
|
||||
this.simpleName = typeBinding.getName();
|
||||
public DomainType(FullyQualified type) {
|
||||
this.packageName = type.getPackageName();
|
||||
this.fullName = type.getFullyQualifiedName();
|
||||
this.simpleName = type.getClassName();
|
||||
|
||||
this.properties = Suppliers.memoize(() -> {
|
||||
if (!this.packageName.startsWith("java")) {
|
||||
IMethodBinding[] methods = typeBinding.getDeclaredMethods();
|
||||
if (methods != null && methods.length > 0) {
|
||||
List<Method> methods = type.getMethods();
|
||||
if (methods != null && !methods.isEmpty()) {
|
||||
List<DomainProperty> properties = new ArrayList<>();
|
||||
|
||||
for (IMethodBinding method : methods) {
|
||||
for (Method method : methods) {
|
||||
String methodName = method.getName();
|
||||
if (methodName != null && methodName.startsWith("get")) {
|
||||
String propertyName = methodName.substring(3);
|
||||
properties.add(new DomainProperty(propertyName, new DomainType(method.getReturnType())));
|
||||
JavaType returnType = method.getReturnType();
|
||||
if (returnType instanceof FullyQualified) {
|
||||
properties.add(new DomainProperty(propertyName, new DomainType((FullyQualified) returnType)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return (DomainProperty[]) properties.toArray(new DomainProperty[properties.size()]);
|
||||
|
||||
@@ -139,24 +139,22 @@ public abstract class AbstractSourceLinks implements SourceLinks {
|
||||
if (cu == null) {
|
||||
return null;
|
||||
}
|
||||
int[] values = new int[] {0, -1};
|
||||
AtomicReference<Range> range = new AtomicReference<>();
|
||||
int lastDotIndex = fqName.lastIndexOf('.');
|
||||
String packageName = fqName.substring(0, lastDotIndex);
|
||||
String typeName = fqName.substring(lastDotIndex + 1);
|
||||
if (packageName.equals(TypeUtils.asFullyQualified(cu.getPackageDeclaration().getExpression().getType()).getFullyQualifiedName())) {
|
||||
if (packageName.equals(cu.getPackageDeclaration().getExpression().printTrimmed())) {
|
||||
Stack<String> visitedType = new Stack<>();
|
||||
new JavaIsoVisitor<>() {
|
||||
|
||||
public org.openrewrite.java.tree.J.ClassDeclaration visitClassDeclaration(org.openrewrite.java.tree.J.ClassDeclaration classDecl, Object p) {
|
||||
String fqName = classDecl.getType().getFullyQualifiedName();
|
||||
visitedType.push(fqName);
|
||||
visitedType.push(classDecl.getSimpleName());
|
||||
if (range.get() == null) {
|
||||
if (String.join("$", visitedType.toArray(new String[visitedType.size()])).equals(typeName)) {
|
||||
range.set(classDecl.getName().getMarkers().findFirst(Range.class).orElseThrow());
|
||||
}
|
||||
}
|
||||
if (values[1] < 0) {
|
||||
if (range.get() == null) {
|
||||
return super.visitClassDeclaration(classDecl, visitedType);
|
||||
} else {
|
||||
return classDecl;
|
||||
|
||||
@@ -49,7 +49,7 @@ public class VSCodeSourceLinks extends AbstractSourceLinks {
|
||||
sb.append('#');
|
||||
sb.append(line);
|
||||
sb.append(',');
|
||||
sb.append(column + 1); // 1-based columns?
|
||||
sb.append(column);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* Copyright (c) 2017, 2022 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
|
||||
@@ -11,10 +11,12 @@
|
||||
package org.springframework.ide.vscode.boot.java.snippets;
|
||||
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.Block;
|
||||
import org.openrewrite.java.tree.J.ClassDeclaration;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
|
||||
|
||||
public interface JavaSnippetContext {
|
||||
JavaSnippetContext BOOT_MEMBERS = (node) -> node instanceof ClassDeclaration;
|
||||
JavaSnippetContext BOOT_MEMBERS = (node) -> node instanceof Block && ORAstUtils.getParent(node) instanceof ClassDeclaration;
|
||||
|
||||
boolean appliesTo(J node);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.ide.vscode.boot.bootiful.HoverTestConf;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.TestAsserts;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2022 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.HoverTestConf;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(HoverTestConf.class)
|
||||
public class RequestMappingSnippetTests {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
private Editor editor;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
IJavaProject testProject = ProjectsHarness.INSTANCE.mavenProject("test-request-mapping-live-hover");
|
||||
harness.useProject(testProject);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMapping() throws Exception {
|
||||
prepareCase("Get<*>");
|
||||
assertOneSnippet("package example;\n"
|
||||
+ "\n"
|
||||
+ "import org.springframework.stereotype.Controller;\n"
|
||||
+ "import org.springframework.web.bind.annotation.DeleteMapping;\n"
|
||||
+ "import org.springframework.web.bind.annotation.GetMapping;\n"
|
||||
+ "import org.springframework.web.bind.annotation.PathVariable;\n"
|
||||
+ "import org.springframework.web.bind.annotation.PostMapping;\n"
|
||||
+ "import org.springframework.web.bind.annotation.PutMapping;\n"
|
||||
+ "import org.springframework.web.bind.annotation.RequestBody;\n"
|
||||
+ "import org.springframework.web.bind.annotation.RequestMapping;\n"
|
||||
+ "import org.springframework.web.bind.annotation.ResponseBody;\n"
|
||||
+ "\n"
|
||||
+ "/** Boot Java - Test Completion */\n"
|
||||
+ "@Controller\n"
|
||||
+ "public class RestApi {\n"
|
||||
+ "\n"
|
||||
+ "@GetMapping(value=\"${1:path}\")\n"
|
||||
+ "public ${2:SomeData} ${3:getMethodName}(@RequestParam ${4:String} ${5:param}) {\n"
|
||||
+ " return new ${2:SomeData}($0);\n"
|
||||
+ "}\n"
|
||||
+ "<*>\n"
|
||||
+ "\n"
|
||||
+ "\n"
|
||||
+ " @RequestMapping(\"/hello\")\n"
|
||||
+ " @ResponseBody\n"
|
||||
+ " public String hello() {\n"
|
||||
+ " return \"Hello there!\";\n"
|
||||
+ " }\n"
|
||||
+ " \n"
|
||||
+ " \n"
|
||||
+ " @RequestMapping(\"/goodbye\")\n"
|
||||
+ " @ResponseBody\n"
|
||||
+ " public String goodbye() {\n"
|
||||
+ " return \"Good bye\";\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " @GetMapping(\"/person/{name}\")\n"
|
||||
+ " public String getMapping(@PathVariable String name) {\n"
|
||||
+ " return \"Hello \" + name;\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " @DeleteMapping(\"/delete/{id}\")\n"
|
||||
+ " public String removeMe(@PathVariable int id) {\n"
|
||||
+ " System.out.println(\"You are removed: \" + id);\n"
|
||||
+ " return \"Done\";\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " @PostMapping(\"/postHello\")\n"
|
||||
+ " public String postMethod(@RequestBody String name) {\n"
|
||||
+ " System.out.println(\"Posted hello: \" + name);\n"
|
||||
+ " return name;\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " @PutMapping(\"/put/{id}\")\n"
|
||||
+ " public String putMethod(@PathVariable int id, @RequestBody String name) {\n"
|
||||
+ " System.out.println(\"Added \" + name + \" with ID: \" + id);\n"
|
||||
+ " return name;\n"
|
||||
+ " }\n"
|
||||
+ "}\n"
|
||||
+ "");
|
||||
}
|
||||
|
||||
private void prepareCase(String prefix) throws Exception {
|
||||
InputStream resource = this.getClass().getResourceAsStream("/test-projects/test-request-mapping-live-hover/src/main/java/example/RestApi.java");
|
||||
String content = IOUtils.toString(resource);
|
||||
|
||||
content = content.replace("class RestApi {", "class RestApi {\n\n" + prefix);
|
||||
editor = new Editor(harness, content, LanguageId.JAVA);
|
||||
}
|
||||
|
||||
private void assertOneSnippet(String expected) throws Exception {
|
||||
List<CompletionItem> completions = editor.getCompletions();
|
||||
assertEquals(completions.size(), 1);
|
||||
Editor clonedEditor = editor.clone();
|
||||
clonedEditor.apply(completions.get(0));
|
||||
assertEquals(expected, clonedEditor.getText());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user