first steps towards auto completion for bean class attribute in spring XML config files
This commit is contained in:
@@ -12,9 +12,19 @@ package org.springframework.ide.vscode.boot.xml;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.lsp4xml.dom.DOMAttr;
|
||||
import org.eclipse.lsp4xml.dom.DOMDocument;
|
||||
import org.eclipse.lsp4xml.dom.DOMNode;
|
||||
import org.eclipse.lsp4xml.dom.DOMParser;
|
||||
import org.eclipse.lsp4xml.dom.parser.Scanner;
|
||||
import org.eclipse.lsp4xml.dom.parser.TokenType;
|
||||
import org.eclipse.lsp4xml.dom.parser.XMLScanner;
|
||||
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.TextDocument;
|
||||
|
||||
/**
|
||||
@@ -22,13 +32,54 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
*/
|
||||
public class SpringXMLCompletionEngine implements ICompletionEngine {
|
||||
|
||||
public SpringXMLCompletionEngine(SpringXMLLanguageServerComponents springXMLLanguageServerComponents) {
|
||||
private static final String BEANS_NAMESPACE = "http://www.springframework.org/schema/beans";
|
||||
|
||||
private static final String BEAN_ELEMENT = "bean";
|
||||
private static final String BEAN_CLASS_ATTRIBUTE = "class";
|
||||
|
||||
|
||||
private final Map<XMLCompletionProviderKey, XMLCompletionProvider> completionProviders;
|
||||
|
||||
public SpringXMLCompletionEngine(SpringXMLLanguageServerComponents springXMLLanguageServerComponents, JavaProjectFinder projectFinder) {
|
||||
this.completionProviders = new HashMap<>();
|
||||
this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, BEAN_ELEMENT, BEAN_CLASS_ATTRIBUTE), new TypeCompletionProposalProvider(projectFinder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ICompletionProposal> getCompletions(TextDocument document, int offset) throws Exception {
|
||||
System.out.println(" SUPER COOL SPRING XML COMPLETION PROPOSALS COMING SOON... STAY TUNED...");
|
||||
public Collection<ICompletionProposal> getCompletions(TextDocument doc, int offset) throws Exception {
|
||||
String content = doc.get();
|
||||
|
||||
DOMParser parser = DOMParser.getInstance();
|
||||
DOMDocument dom = parser.parse(content, "", null);
|
||||
|
||||
DOMNode node = dom.findNodeBefore(offset);
|
||||
|
||||
if (node != null) {
|
||||
String namespace = node.getNamespaceURI();
|
||||
|
||||
Scanner scanner = XMLScanner.createScanner(content, node.getStart(), false);
|
||||
TokenType token = scanner.scan();
|
||||
while (token != TokenType.EOS && scanner.getTokenOffset() <= offset) {
|
||||
switch (token) {
|
||||
case AttributeValue:
|
||||
if (scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd()) {
|
||||
DOMAttr attributeAt = dom.findAttrAt(offset);
|
||||
|
||||
if (attributeAt != null) {
|
||||
XMLCompletionProviderKey key = new XMLCompletionProviderKey(namespace, node.getNodeName(), attributeAt.getNodeName());
|
||||
XMLCompletionProvider completionProvider = this.completionProviders.get(key);
|
||||
if (completionProvider != null) {
|
||||
return completionProvider.getCompletions(doc, namespace, node, attributeAt, scanner, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,9 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.app.BootLanguageServerParams;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -36,12 +35,14 @@ public class SpringXMLLanguageServerComponents implements LanguageServerComponen
|
||||
|
||||
private final SimpleLanguageServer server;
|
||||
private final BootLanguageServerParams serverParams;
|
||||
private final JavaProjectFinder projectFinder;
|
||||
|
||||
public SpringXMLLanguageServerComponents(
|
||||
SimpleLanguageServer server,
|
||||
BootLanguageServerParams serverParams) {
|
||||
this.server = server;
|
||||
this.serverParams = serverParams;
|
||||
this.projectFinder = serverParams.projectFinder;
|
||||
|
||||
server.doOnInitialized(this::initialized);
|
||||
server.onShutdown(this::shutdown);
|
||||
@@ -54,7 +55,7 @@ public class SpringXMLLanguageServerComponents implements LanguageServerComponen
|
||||
|
||||
@Override
|
||||
public ICompletionEngine getCompletionEngine() {
|
||||
return new SpringXMLCompletionEngine(this);
|
||||
return new SpringXMLCompletionEngine(this, projectFinder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 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;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class TypeCompletionProposal implements ICompletionProposal {
|
||||
|
||||
private final String label;
|
||||
private final CompletionItemKind kind;
|
||||
private final DocumentEdits edits;
|
||||
private final String detail;
|
||||
private final Renderable documentation;
|
||||
|
||||
public TypeCompletionProposal(String label, CompletionItemKind kind, DocumentEdits edits, String detail, Renderable documentation) {
|
||||
super();
|
||||
this.label = label;
|
||||
this.kind = kind;
|
||||
this.edits = edits;
|
||||
this.detail = detail;
|
||||
this.documentation = documentation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionItemKind getKind() {
|
||||
return this.kind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentEdits getTextEdit() {
|
||||
return this.edits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetail() {
|
||||
return this.detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable getDocumentation() {
|
||||
return this.documentation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.eclipse.lsp4xml.dom.DOMAttr;
|
||||
import org.eclipse.lsp4xml.dom.DOMNode;
|
||||
import org.eclipse.lsp4xml.dom.parser.Scanner;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
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;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class TypeCompletionProposalProvider implements XMLCompletionProvider {
|
||||
|
||||
private final JavaProjectFinder projectFinder;
|
||||
|
||||
public TypeCompletionProposalProvider(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();
|
||||
|
||||
Optional<IJavaProject> foundProject = this.projectFinder.find(doc.getId());
|
||||
if (foundProject.isPresent()) {
|
||||
IJavaProject project = foundProject.get();
|
||||
|
||||
String prefix = tokenText.substring(0, offset - tokenOffset);
|
||||
if (prefix.startsWith("\"")) {
|
||||
prefix = prefix.substring(1);
|
||||
}
|
||||
|
||||
Flux<Tuple2<IType, Double>> types = project.getIndex().fuzzySearchTypes(prefix, true, true);
|
||||
|
||||
return types.collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2()))
|
||||
.flatMapIterable(l -> l)
|
||||
.filter(result -> result.getT1() != null && result.getT1().getElementName() != null && result.getT1().getElementName().length() > 0)
|
||||
.map(t -> createProposal(t, doc, offset, tokenOffset, tokenEnd)).collectList().block();
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ICompletionProposal createProposal(Tuple2<IType, Double> t, TextDocument doc, int offset, int tokenStart, int tokenEnd) {
|
||||
IType type = t.getT1();
|
||||
|
||||
String label = type.getElementName();
|
||||
CompletionItemKind kind;
|
||||
|
||||
if (type.isClass()) {
|
||||
kind = CompletionItemKind.Class;
|
||||
}
|
||||
else if (type.isInterface()) {
|
||||
kind = CompletionItemKind.Interface;
|
||||
}
|
||||
else {
|
||||
kind = CompletionItemKind.Value;
|
||||
}
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(tokenStart, tokenEnd, "\"" + type.getFullyQualifiedName() + "\"");
|
||||
|
||||
Renderable renderable = null;
|
||||
|
||||
return new TypeCompletionProposal(label, kind, edits, type.getFullyQualifiedName(), renderable);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 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;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.lsp4xml.dom.DOMAttr;
|
||||
import org.eclipse.lsp4xml.dom.DOMNode;
|
||||
import org.eclipse.lsp4xml.dom.parser.Scanner;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public interface XMLCompletionProvider {
|
||||
|
||||
Collection<ICompletionProposal> getCompletions(TextDocument doc, String namespace, DOMNode node,
|
||||
DOMAttr attributeAt, Scanner scanner, int offset);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 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;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class XMLCompletionProviderKey {
|
||||
|
||||
private final String namespaceURI;
|
||||
private final String elementName;
|
||||
private final String attributeName;
|
||||
|
||||
public XMLCompletionProviderKey(String namespaceURI, String elementName, String attributeName) {
|
||||
super();
|
||||
this.namespaceURI = namespaceURI;
|
||||
this.elementName = elementName;
|
||||
this.attributeName = attributeName;
|
||||
}
|
||||
|
||||
public String getNamespaceURI() {
|
||||
return namespaceURI;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return elementName;
|
||||
}
|
||||
|
||||
public String getAttributeName() {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((attributeName == null) ? 0 : attributeName.hashCode());
|
||||
result = prime * result + ((elementName == null) ? 0 : elementName.hashCode());
|
||||
result = prime * result + ((namespaceURI == null) ? 0 : namespaceURI.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
XMLCompletionProviderKey other = (XMLCompletionProviderKey) obj;
|
||||
if (attributeName == null) {
|
||||
if (other.attributeName != null)
|
||||
return false;
|
||||
} else if (!attributeName.equals(other.attributeName))
|
||||
return false;
|
||||
if (elementName == null) {
|
||||
if (other.elementName != null)
|
||||
return false;
|
||||
} else if (!elementName.equals(other.elementName))
|
||||
return false;
|
||||
if (namespaceURI == null) {
|
||||
if (other.namespaceURI != null)
|
||||
return false;
|
||||
} else if (!namespaceURI.equals(other.namespaceURI))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user