Skip to content

Commit

Permalink
Manage completion without start tag (see #112)
Browse files Browse the repository at this point in the history
  • Loading branch information
angelozerr committed Sep 26, 2018
1 parent 492212a commit e27e304
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 48 deletions.
44 changes: 26 additions & 18 deletions org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/Attr.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,36 @@
* An attribute node.
*
*/
public class Attr {
public class Attr extends Node {

private final String name;

private final Node nodeName;
private final Node nodeAttrName;

private Node nodeValue;
private Node nodeAttrValue;

private String value;

public Attr(String name, Node nodeName) {
private final Node ownerElement;

public Attr(String name, Node nodeAttrName, Node ownerElement) {
super(-1, -1, ownerElement.getOwnerDocument());
this.name = name;
this.nodeName = nodeName;
this.nodeAttrName = nodeAttrName;
this.ownerElement = ownerElement;
}

public String getName() {
return name;
}

public Node getNodeName() {
return nodeName;
public Node getNodeAttrName() {
return nodeAttrName;
}

public void setValue(String value, Node nodeValue) {
this.value = getValue(value);
this.nodeValue = nodeValue;
this.nodeAttrValue = nodeValue;
}

private static String getValue(String value) {
Expand All @@ -54,24 +58,26 @@ private static String getValue(String value) {
return value.substring(start, end);
}

public Node getNodeValue() {
return nodeValue;
public Node getNodeAttrValue() {
return nodeAttrValue;
}

public void setNodeValue(Node nodeValue) {
this.nodeValue = nodeValue;
public void setNodeAttrValue(Node nodeAttrValue) {
this.nodeAttrValue = nodeAttrValue;
}

public boolean isIncluded(int offset) {
return Node.isIncluded(getStart(), getEnd(), offset);
}

@Override
public int getStart() {
return nodeName.start;
return nodeAttrName.start;
}

@Override
public int getEnd() {
return nodeValue != null ? nodeValue.end : nodeName.end;
return nodeAttrValue != null ? nodeAttrValue.end : nodeAttrName.end;
}

public String getValue() {
Expand Down Expand Up @@ -110,9 +116,11 @@ public boolean equals(Object obj) {
}

public Element getOwnerElement() {
if (nodeName != null && nodeName.getParent() != null && nodeName.getParent().isElement()) {
return (Element) nodeName.getParent();
}
return null;
return ownerElement.isElement() ? (Element) ownerElement : null;
}

@Override
public short getNodeType() {
return Node.ATTRIBUTE_NODE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ public boolean isInStartTag(int offset) {
}
return false;
}

public boolean isInEndTag(int offset) {
if (endTagOpenOffset == null) {
// case >|
return false;
}
if (offset > endTagOpenOffset && offset <= getEnd()) {
// case <\bean | >
return true;
}
return false;
}

public boolean hasStartTagClose() {
return startTagCloseOffset != null;
Expand Down Expand Up @@ -187,4 +199,5 @@ public boolean hasEndTag() {
return endTagOpenOffset != null;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public class Node {
*/
public static final short ELEMENT_NODE = 1;

/**
* The node is an <code>Attribute</code>.
*/
public static final short ATTRIBUTE_NODE = 2;

/**
* The node is a <code>Text</code> node.
*/
Expand Down Expand Up @@ -59,7 +64,7 @@ public class Node {

private List<Attr> attributeNodes;
private List<Node> children;

final int start;
int end;

Expand Down Expand Up @@ -191,6 +196,10 @@ public static boolean isIncluded(int start, int end, int offset) {

public Attr findAttrAt(int offset) {
Node node = findNodeAt(offset);
return findAttrAt(node, offset);
}

public Attr findAttrAt(Node node, int offset) {
if (node != null && node.hasAttributes()) {
for (Attr attr : node.getAttributeNodes()) {
if (attr.isIncluded(offset)) {
Expand Down Expand Up @@ -273,7 +282,7 @@ public boolean hasAttributes() {
public void setAttribute(String name, String value) {
Attr attr = getAttributeNode(name);
if (attr == null) {
attr = new Attr(name, null);
attr = new Attr(name, null, this);
setAttributeNode(attr);
}
attr.setValue(value, null);
Expand Down Expand Up @@ -376,6 +385,10 @@ public boolean isDoctype() {
public boolean isElement() {
return getNodeType() == Node.ELEMENT_NODE;
}

public boolean isAttribute() {
return getNodeType() == Node.ATTRIBUTE_NODE;
}

public boolean isText() {
return getNodeType() == Node.TEXT_NODE;
Expand All @@ -392,11 +405,11 @@ public short getNodeType() {
public String getNodeName() {
return null;
}

public int getStart() {
return start;
}

public int getEnd() {
return end;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public XMLDocument parse(TextDocument document) {
} else {
// element open tag not found (ex: <root>) add a fake elementg which have just
// end tag (no start tag).
Element element = xmlDocument.createElement(scanner.getTokenOffset() -2, text.length());
Element element = xmlDocument.createElement(scanner.getTokenOffset() - 2, text.length());
element.endTagOpenOffset = endTagOpenOffset;
element.tag = closeTag;
current.addChild(element);
Expand Down Expand Up @@ -141,7 +141,7 @@ public XMLDocument parse(TextDocument document) {
case AttributeName: {
pendingAttribute = scanner.getTokenText();
attr = new Attr(pendingAttribute, new Node(scanner.getTokenOffset(),
scanner.getTokenOffset() + pendingAttribute.length(), null, curr, xmlDocument));
scanner.getTokenOffset() + pendingAttribute.length(), null, curr, xmlDocument), curr);
curr.setAttributeNode(attr);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ public Node getNode() {
@Override
public Element getParentElement() {
Node currentNode = getNode();
if (!currentNode.isElement()) {
if (!currentNode.isElement() || currentNode.getEnd() < offset) {
// Node is not an element, search parent element.
return currentNode.getParentElement();
}
Element element = (Element) currentNode;
// node is an element, there are 2 cases
// case 1: <| or <bean | > --> in this case we must search parent of bean
// element
if (element.isInStartTag(offset)) {
if (element.isInStartTag(offset) || element.isInEndTag(offset)) {
return element.getParentElement();
}
// case 2: <bean> | --> in this case, parent element is the bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ public CompletionList doComplete(XMLDocument xmlDocument, Position position, Com
return completionResponse;
default:
}

}
break;
case EndTagOpen:
Expand Down Expand Up @@ -172,12 +171,21 @@ public CompletionList doComplete(XMLDocument xmlDocument, Position position, Com
case StartTagClose:
if (offset <= scanner.getTokenEnd()) {
if (currentTag != null && currentTag.length() > 0) {
collectInsideContent(completionRequest, completionResponse);
collectAutoCloseTagSuggestion(scanner.getTokenEnd(), currentTag, completionRequest,
completionResponse);
return completionResponse;
}
}
break;
case StartTagSelfClose:
if (offset <= scanner.getTokenEnd()) {
if (currentTag != null && currentTag.length() > 0) {
collectInsideContent(completionRequest, completionResponse);
return completionResponse;
}
}
break;
case EndTagClose:
if (offset <= scanner.getTokenEnd()) {
if (currentTag != null && currentTag.length() > 0) {
Expand Down Expand Up @@ -315,7 +323,7 @@ private void collectOpenTagSuggestions(boolean hasOpenBracket, Range replaceRang
if (completionSettings.isCompletionSnippetsSupported()) {
xml.append("$0");
}
if(completionSettings.isAutoCloseTags()) {
if (completionSettings.isAutoCloseTags()) {
xml.append("</").append(tag).append(">");
}
}
Expand Down Expand Up @@ -420,16 +428,6 @@ private void collectInsideContent(CompletionRequest request, CompletionResponse
collectCharacterEntityProposals(request, response);
}

/**
* Collect completion inside comments.
*
* @param request
* @param response
*/
private void collectInsideComment(ICompletionRequest request, ICompletionResponse response) {
collectionRegionProposals(request, response);
}

private void collectionRegionProposals(ICompletionRequest request, ICompletionResponse response) {
// Completion for #region
try {
Expand All @@ -440,7 +438,7 @@ private void collectionRegionProposals(ICompletionRequest request, ICompletionRe
String lineUntilPos = lineText.substring(0, pos.getCharacter());
Matcher match = regionCompletionRegExpr.matcher(lineUntilPos);
if (match.find()) {
Range range = new Range(new Position(pos.getLine(), match.regionStart()), pos);
Range range = new Range(new Position(pos.getLine(), pos.getCharacter() + match.regionStart()), pos);

CompletionItem beginProposal = new CompletionItem("#region");
beginProposal.setTextEdit(new TextEdit(range, "<!-- #region $1-->"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public static Range selectAttributeNameAt(int offset, XMLDocument document) {
offset = adjustOffsetForAttribute(offset, document);
Attr attr = document.findAttrAt(offset);
if (attr != null) {
int startOffset = attr.getNodeName().getStart();
int endOffset = attr.getNodeName().getEnd();
int startOffset = attr.getNodeAttrName().getStart();
int endOffset = attr.getNodeAttrName().getEnd();
return createRange(startOffset, endOffset, document);
}
return null;
Expand Down Expand Up @@ -85,8 +85,8 @@ public static Range selectAttributeValueFromGivenValue(String attrValue, int off
}

private static Range createAttrValueRange(Attr attr, XMLDocument document) {
int startOffset = attr.getNodeValue().getStart();
int endOffset = attr.getNodeValue().getEnd();
int startOffset = attr.getNodeAttrValue().getStart();
int endOffset = attr.getNodeAttrValue().getEnd();
return createRange(startOffset, endOffset, document);
}

Expand Down Expand Up @@ -115,8 +115,8 @@ public static Range selectAttributeNameFromGivenNameAt(String attrName, int offs
}

private static Range createAttrNameRange(Attr attr, XMLDocument document) {
int startOffset = attr.getNodeName().getStart();
int endOffset = attr.getNodeName().getEnd();
int startOffset = attr.getNodeAttrName().getStart();
int endOffset = attr.getNodeAttrName().getEnd();
return createRange(startOffset, endOffset, document);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,58 @@ public void completionOnAttributeValue2() throws BadLocationException {
c("debit", "debit"), c("cash", "cash"));
}

@Test
public void completionWithoutStartBracket() throws BadLocationException {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
"<beans xmlns=\"http://www.springframework.org/schema/beans\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ //
" |";
XMLAssert.testCompletionFor(xml, c("bean", "<bean></bean>"));

xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
"<beans xmlns=\"http://www.springframework.org/schema/beans\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ //
" |" + "</beans>";
XMLAssert.testCompletionFor(xml, c("bean", "<bean></bean>"));

xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
"<beans xmlns=\"http://www.springframework.org/schema/beans\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ //
" <bean />|" + "</beans>";
XMLAssert.testCompletionFor(xml, c("bean", "<bean></bean>"));

xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
"<beans xmlns=\"http://www.springframework.org/schema/beans\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ //
" <bean />|";
XMLAssert.testCompletionFor(xml, c("bean", "<bean></bean>"));

xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
"<beans xmlns=\"http://www.springframework.org/schema/beans\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ //
" <bean ></bean>|" + "</beans>";
XMLAssert.testCompletionFor(xml, c("bean", "<bean></bean>"));

xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
"<beans xmlns=\"http://www.springframework.org/schema/beans\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ //
" <bean ></bean>|";
XMLAssert.testCompletionFor(xml, c("bean", "<bean></bean>"));

xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
"<beans xmlns=\"http://www.springframework.org/schema/beans\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ //
" <bean>|</bean>";
XMLAssert.testCompletionFor(xml, c("constructor-arg", "<constructor-arg></constructor-arg>"));

xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
"<beans xmlns=\"http://www.springframework.org/schema/beans\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ //
" <bean>\r\n | \r\n</bean>";
XMLAssert.testCompletionFor(xml, c("constructor-arg", "<constructor-arg></constructor-arg>"));

}

private void testCompletionFor(String xml, CompletionItem... expectedItems) throws BadLocationException {
XMLAssert.testCompletionFor(xml, "src/test/resources/catalogs/catalog.xml", expectedItems);
}
Expand Down
Loading

0 comments on commit e27e304

Please sign in to comment.