added completion proposal provider for constructur arg names in Spring XML config files
This commit is contained in:
@@ -58,6 +58,7 @@ import org.eclipse.lemminx.dom.parser.XMLScanner;
|
||||
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.xml.completions.BeanRefCompletionProposalProvider;
|
||||
import org.springframework.ide.vscode.boot.xml.completions.ConstructorArgNameCompletionProposalProvider;
|
||||
import org.springframework.ide.vscode.boot.xml.completions.PropertyNameCompletionProposalProvider;
|
||||
import org.springframework.ide.vscode.boot.xml.completions.TypeCompletionProposalProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
||||
@@ -95,6 +96,7 @@ public class SpringXMLCompletionEngine implements ICompletionEngine, LanguageSpe
|
||||
|
||||
BeanRefCompletionProposalProvider beanRefProvider = new BeanRefCompletionProposalProvider(projectFinder, symbolIndex);
|
||||
PropertyNameCompletionProposalProvider propertyNameProvider = new PropertyNameCompletionProposalProvider(projectFinder);
|
||||
ConstructorArgNameCompletionProposalProvider constructorArgNameProvider = new ConstructorArgNameCompletionProposalProvider(projectFinder);
|
||||
|
||||
this.completionProviders.put(new XMLElementKey(BEANS_NAMESPACE, null, BEAN_ELEMENT, CLASS_ATTRIBUTE), classesOnlyProvider);
|
||||
this.completionProviders.put(new XMLElementKey(BEANS_NAMESPACE, null, CONSTRUCTOR_ARG_ELEMENT, TYPE_ATTRIBUTE), classesAndInterfacesProvider);
|
||||
@@ -115,6 +117,7 @@ public class SpringXMLCompletionEngine implements ICompletionEngine, LanguageSpe
|
||||
this.completionProviders.put(new XMLElementKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, REF_ATTRIBUTE), beanRefProvider);
|
||||
|
||||
this.completionProviders.put(new XMLElementKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, NAME_ATTRIBUTE), propertyNameProvider);
|
||||
this.completionProviders.put(new XMLElementKey(BEANS_NAMESPACE, null, CONSTRUCTOR_ARG_ELEMENT, NAME_ATTRIBUTE), constructorArgNameProvider);
|
||||
|
||||
this.completionProviders.put(new XMLElementKey(UTIL_NAMESPACE, null, null, VALUE_TYPE_ATTRIBUTE), classesAndInterfacesProvider);
|
||||
this.completionProviders.put(new XMLElementKey(UTIL_NAMESPACE, null, null, KEY_TYPE_ATTRIBUTE), classesAndInterfacesProvider);
|
||||
|
||||
@@ -89,7 +89,7 @@ public class BeanRefCompletionProposalProvider implements XMLCompletionProvider
|
||||
|
||||
Renderable renderable = null;
|
||||
|
||||
return new TypeCompletionProposal(beanID, kind, edits, beanID, renderable, score);
|
||||
return new GenericXMLCompletionProposal(beanID, kind, edits, beanID, renderable, score);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 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.xml.completions;
|
||||
|
||||
import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.BEAN_ELEMENT;
|
||||
import static org.springframework.ide.vscode.boot.xml.XmlConfigConstants.CLASS_ATTRIBUTE;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.eclipse.lemminx.dom.DOMAttr;
|
||||
import org.eclipse.lemminx.dom.DOMNode;
|
||||
import org.eclipse.lemminx.dom.parser.Scanner;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.Type;
|
||||
import org.springframework.ide.vscode.boot.xml.XMLCompletionProvider;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaType;
|
||||
import org.springframework.ide.vscode.commons.java.IMethod;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
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.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ConstructorArgNameCompletionProposalProvider implements XMLCompletionProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ConstructorArgNameCompletionProposalProvider.class);
|
||||
|
||||
private final JavaProjectFinder projectFinder;
|
||||
|
||||
public ConstructorArgNameCompletionProposalProvider(JavaProjectFinder projectFinder) {
|
||||
this.projectFinder = projectFinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ICompletionProposal> getCompletions(TextDocument doc, String namespace, DOMNode node, DOMAttr attributeAt,
|
||||
Scanner scanner, int offset) {
|
||||
|
||||
int tokenOffset = scanner.getTokenOffset();
|
||||
int tokenEnd = scanner.getTokenEnd();
|
||||
String tokenText = scanner.getTokenText();
|
||||
|
||||
log.info("Stating calculating completions for {} at offset {}.", doc.getId().getUri(), offset);
|
||||
|
||||
|
||||
Optional<IJavaProject> foundProject = this.projectFinder.find(doc.getId());
|
||||
if (foundProject.isPresent()) {
|
||||
IJavaProject project = foundProject.get();
|
||||
log.info("Project found is {}", project.getElementName());
|
||||
|
||||
String prefix = tokenText.substring(0, offset - tokenOffset);
|
||||
if (prefix.startsWith("\"")) {
|
||||
prefix = prefix.substring(1);
|
||||
}
|
||||
|
||||
log.info("Prefix is '{}'", prefix);
|
||||
|
||||
String beanClass = identifyBeanClass(node);
|
||||
if (beanClass != null && beanClass.length() > 0) {
|
||||
|
||||
log.info("Bean class '{}'", beanClass);
|
||||
|
||||
final String searchPrefix = prefix;
|
||||
return constructorArgNameCandidates(project, beanClass)
|
||||
.filter(constructorArg -> constructorArg.getRight().startsWith(searchPrefix))
|
||||
.map(constructorArg -> createProposal(constructorArg, doc, offset, tokenOffset, tokenEnd))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
};
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static String identifyBeanClass(DOMNode node) {
|
||||
DOMNode parentNode = node.getParentNode();
|
||||
if (parentNode != null) {
|
||||
if (BEAN_ELEMENT.equals(parentNode.getLocalName())) {
|
||||
String beanClassAttribute = parentNode.getAttribute(CLASS_ATTRIBUTE);
|
||||
return beanClassAttribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ICompletionProposal createProposal(Pair<IJavaType, String> constructorArg, TextDocument doc, int offset, int tokenStart, int tokenEnd) {
|
||||
String label = constructorArg.getRight();
|
||||
CompletionItemKind kind = CompletionItemKind.Reference;
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||
|
||||
String replaceString = "\"" + label + "\"";
|
||||
int replaceStart = tokenStart;
|
||||
|
||||
if (tokenStart < offset) {
|
||||
replaceStart = offset;
|
||||
replaceString = replaceString.substring(offset - tokenStart);
|
||||
}
|
||||
edits.replace(replaceStart, tokenEnd, replaceString);
|
||||
|
||||
Renderable renderable = null;
|
||||
String detail = label;
|
||||
|
||||
IJavaType paramType = constructorArg.getLeft();
|
||||
if (paramType != null) {
|
||||
Type type = Type.fromJavaType(paramType);
|
||||
if (type != null) {
|
||||
detail = constructorArg.getRight() + " - " + type.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return new GenericXMLCompletionProposal(label, kind, edits, detail, renderable, 1d);
|
||||
}
|
||||
|
||||
public static Stream<Pair<IJavaType, String>> constructorArgNameCandidates(IJavaProject project, String beanClassFqName) {
|
||||
IType type = project.getIndex().findType(beanClassFqName);
|
||||
if (type != null) {
|
||||
return type.getMethods()
|
||||
.filter(method -> method.isConstructor())
|
||||
.flatMap(method -> getConstructorArgs(method));
|
||||
}
|
||||
else {
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<Pair<IJavaType, String>> getConstructorArgs(IMethod method) {
|
||||
List<String> parameterNames = method.getParameterNames();
|
||||
List<IJavaType> parameterTypes = method.parameters().collect(Collectors.toList());
|
||||
|
||||
if (parameterNames != null && parameterTypes != null && parameterNames.size() == parameterTypes.size()) {
|
||||
return IntStream.range(0, parameterNames.size())
|
||||
.mapToObj(i -> Pair.of(parameterTypes.get(i), parameterNames.get(i)));
|
||||
}
|
||||
else {
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 Pivotal, Inc.
|
||||
* Copyright (c) 2019, 2020 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
|
||||
@@ -18,7 +18,7 @@ import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class TypeCompletionProposal extends ScoreableProposal {
|
||||
public class GenericXMLCompletionProposal extends ScoreableProposal {
|
||||
|
||||
private final String label;
|
||||
private final CompletionItemKind kind;
|
||||
@@ -27,7 +27,7 @@ public class TypeCompletionProposal extends ScoreableProposal {
|
||||
private final Renderable documentation;
|
||||
private final double score;
|
||||
|
||||
public TypeCompletionProposal(String label, CompletionItemKind kind, DocumentEdits edits, String detail, Renderable documentation, double score) {
|
||||
public GenericXMLCompletionProposal(String label, CompletionItemKind kind, DocumentEdits edits, String detail, Renderable documentation, double score) {
|
||||
super();
|
||||
this.label = label;
|
||||
this.kind = kind;
|
||||
@@ -115,7 +115,7 @@ public class PropertyNameCompletionProposalProvider implements XMLCompletionProv
|
||||
|
||||
Renderable renderable = null;
|
||||
|
||||
return new TypeCompletionProposal(label, kind, edits, label, renderable, 1d);
|
||||
return new GenericXMLCompletionProposal(label, kind, edits, label, renderable, 1d);
|
||||
}
|
||||
|
||||
private static boolean isPropertyWriteMethod(IMethod method) {
|
||||
|
||||
@@ -124,7 +124,7 @@ public class TypeCompletionProposalProvider implements XMLCompletionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private TypeCompletionProposal createPackageProposal(JavaCodeCompleteData proposal, TextDocument doc, String prefix, int offset) {
|
||||
private GenericXMLCompletionProposal createPackageProposal(JavaCodeCompleteData proposal, TextDocument doc, String prefix, int offset) {
|
||||
String fqName = proposal.getFullyQualifiedName();
|
||||
String label = fqName;
|
||||
CompletionItemKind kind = CompletionItemKind.Module;
|
||||
@@ -138,10 +138,10 @@ public class TypeCompletionProposalProvider implements XMLCompletionProvider {
|
||||
|
||||
Renderable renderable = null;
|
||||
|
||||
return new TypeCompletionProposal(label, kind, edits, fqName, renderable, proposal.getRelevance());
|
||||
return new GenericXMLCompletionProposal(label, kind, edits, fqName, renderable, proposal.getRelevance());
|
||||
}
|
||||
|
||||
private TypeCompletionProposal createTypeProposal(JavaCodeCompleteData proposal, TextDocument doc, String prefix, int offset) {
|
||||
private GenericXMLCompletionProposal createTypeProposal(JavaCodeCompleteData proposal, TextDocument doc, String prefix, int offset) {
|
||||
String fqName = proposal.getFullyQualifiedName();
|
||||
String label = fqName;
|
||||
|
||||
@@ -168,7 +168,7 @@ public class TypeCompletionProposalProvider implements XMLCompletionProvider {
|
||||
|
||||
Renderable renderable = null;
|
||||
|
||||
return new TypeCompletionProposal(label, kind, edits, fqName, renderable, proposal.getRelevance());
|
||||
return new GenericXMLCompletionProposal(label, kind, edits, fqName, renderable, proposal.getRelevance());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.WorkspaceFolder;
|
||||
|
||||
Reference in New Issue
Block a user