first experiments towards spring-specific content-assist for spring annotations, starting with scope
This commit is contained in:
@@ -52,7 +52,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
|
||||
validateWith(doc, reconcileEngine);
|
||||
});
|
||||
|
||||
ICompletionEngine bootCompletionEngine = new BootJavaCompletionEngine();
|
||||
ICompletionEngine bootCompletionEngine = new BootJavaCompletionEngine(javaProjectFinder);
|
||||
completionEngine = new VscodeCompletionEngineAdapter(this, bootCompletionEngine);
|
||||
completionEngine.setMaxCompletionsNumber(100);
|
||||
documents.onCompletion(completionEngine::getCompletions);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 Pivotal, Inc.
|
||||
* 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
|
||||
@@ -8,32 +8,47 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.springframework.ide.vscode.boot.java.completions;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.dom.AST;
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
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.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
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 JavaProjectFinder projectFinder;
|
||||
|
||||
public BootJavaCompletionEngine(JavaProjectFinder projectFinder) {
|
||||
this.projectFinder = projectFinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ICompletionProposal> getCompletions(IDocument document, int offset) throws Exception {
|
||||
List<ICompletionProposal> completions = new ArrayList<>();
|
||||
|
||||
System.out.println("create parser");
|
||||
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS8);
|
||||
Map<String, String> options = JavaCore.getOptions();
|
||||
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
|
||||
@@ -41,22 +56,62 @@ public class BootJavaCompletionEngine implements ICompletionEngine {
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setStatementsRecovery(true);
|
||||
parser.setBindingsRecovery(true);
|
||||
parser.setResolveBindings(true);
|
||||
|
||||
String[] classpathEntries = getClasspathEntries(document);
|
||||
String[] sourceEntries = new String[] {};
|
||||
parser.setEnvironment(classpathEntries, sourceEntries, null, true);
|
||||
|
||||
System.out.println("set source");
|
||||
|
||||
String docURI = document.getUri();
|
||||
String unitName = docURI.substring(docURI.lastIndexOf("/"));
|
||||
parser.setUnitName(unitName);
|
||||
parser.setSource(document.get(0, document.getLength()).toCharArray());
|
||||
|
||||
System.out.println("start parse");
|
||||
|
||||
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
|
||||
|
||||
System.out.println("end parse");
|
||||
|
||||
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 completions;
|
||||
}
|
||||
|
||||
private void collectCompletionsForAnnotations(ASTNode node, List<ICompletionProposal> completions, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collectCompletionsForSpringAnnotation(ASTNode node, Annotation annotation, ITypeBinding type,
|
||||
List<ICompletionProposal> completions, int offset, IDocument doc) {
|
||||
|
||||
if (type.getQualifiedName().equals(SPRING_SCOPE)) {
|
||||
new ScopeCompletionProcessor().collectCompletionsForScopeAnnotation(node, annotation, type, completions, offset, doc);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getClasspathEntries(IDocument doc) throws Exception {
|
||||
IJavaProject project = this.projectFinder.find(doc);
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Stream<Path> classpathEntries = classpath.getClasspathEntries();
|
||||
return classpathEntries.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.springframework.ide.vscode.boot.java.completions;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaReconcileEngine implements IReconcileEngine {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* 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.completions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
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.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ScopeCompletionProcessor {
|
||||
|
||||
public void collectCompletionsForScopeAnnotation(ASTNode node, Annotation annotation, ITypeBinding type,
|
||||
List<ICompletionProposal> completions, int offset, IDocument doc) {
|
||||
|
||||
try {
|
||||
if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair) {
|
||||
MemberValuePair memberPair = (MemberValuePair) node.getParent();
|
||||
|
||||
// case: @Scope(value=<*>)
|
||||
if (memberPair.getName() != null && memberPair.getValue().toString().equals("$missing$")) {
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, offset, offset, "");
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
}
|
||||
// case: @Scope(<*>)
|
||||
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);
|
||||
}
|
||||
}
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
// case: @Scope(value="")
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*******************************************************************************
|
||||
* 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.completions;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ScopeNameCompletion {
|
||||
|
||||
private final String label;
|
||||
private final String detail;
|
||||
private final Renderable documentation;
|
||||
private final CompletionItemKind kind;
|
||||
private final String value;
|
||||
|
||||
public ScopeNameCompletion(String value, String label, String detail, Renderable documentation, CompletionItemKind kind) {
|
||||
super();
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
this.detail = detail;
|
||||
this.documentation = documentation;
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public CompletionItemKind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public Renderable getDocumentation() {
|
||||
return documentation;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*******************************************************************************
|
||||
* 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.completions;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
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.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ScopeNameCompletionProposal implements ICompletionProposal {
|
||||
|
||||
public static final ScopeNameCompletion[] COMPLETIONS = new ScopeNameCompletion[] {
|
||||
new ScopeNameCompletion("\"prototype\"", "prototype", "prototype scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"singleton\"", "singleton", "singleton scope (default)", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"request\"", "request", "request scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"session\"", "session", "session scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"globalSession\"", "globalSession", "globalSession scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"application\"", "application", "application scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"websocket\"", "websocket", "websocket scope", null, CompletionItemKind.Value)
|
||||
};
|
||||
|
||||
private final IDocument doc;
|
||||
private final int startOffset;
|
||||
private final int endOffset;
|
||||
private final ScopeNameCompletion completion;
|
||||
private final String prefix;
|
||||
|
||||
public ScopeNameCompletionProposal(ScopeNameCompletion completion, IDocument doc, int startOffset, int endOffset, String prefix) {
|
||||
this.completion = completion;
|
||||
this.doc = doc;
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICompletionProposal deemphasize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return completion.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionItemKind getKind() {
|
||||
return completion.getKind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentEdits getTextEdit() {
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(startOffset + prefix.length(), endOffset, completion.getValue().substring(prefix.length()));
|
||||
return edits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetail() {
|
||||
return completion.getDetail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable getDocumentation() {
|
||||
return completion.getDocumentation();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user