diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMElement.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMElement.java
index fb2396e321..d7e1513817 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMElement.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMElement.java
@@ -77,6 +77,17 @@ public String getTagName() {
return tag;
}
+ /**
+ * Returns true if the DOM element have a tag name and null otherwise (ex : '<'
+ * or '').
+ *
+ * @return true if the DOM element have a tag name and null otherwise (ex : '<'
+ * or '').
+ */
+ public boolean hasTagName() {
+ return tag != null;
+ }
+
/*
* (non-Javadoc)
*
@@ -397,12 +408,23 @@ public boolean isEndTagClosed() {
* Returns true if the given element is an orphan end tag (which has no start
* tag, eg: ) and false otherwise.
*
- * @param tagName the end tag name.
* @return true if the given element is an orphan end tag (which has no start
* tag, eg: ) and false otherwise.
*/
- public boolean isOrphanEndTag(String tagName) {
- return isSameTag(tagName) && hasEndTag() && !hasStartTag();
+ public boolean isOrphanEndTag() {
+ return hasEndTag() && !hasStartTag();
+ }
+
+ /**
+ * Returns true if the given element is an orphan end tag (which has no start
+ * tag, eg: ) of the given tag name and false otherwise.
+ *
+ * @param tagName the end tag name.
+ * @return true if the given element is an orphan end tag (which has no start
+ * tag, eg: ) of the given tag name and false otherwise.
+ */
+ public boolean isOrphanEndTagOf(String tagName) {
+ return isSameTag(tagName) && isOrphanEndTag();
}
@Override
@@ -423,7 +445,7 @@ public DOMElement getOrphanEndElement(int offset, String tagName) {
for (DOMNode child : children) {
if (child.isElement()) {
DOMElement childElement = (DOMElement) child;
- if (childElement.isOrphanEndTag(tagName)) {
+ if (childElement.isOrphanEndTagOf(tagName)) {
return childElement;
}
}
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMNode.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMNode.java
index 871c1a1f23..9f4ebca0db 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMNode.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMNode.java
@@ -746,7 +746,7 @@ public DOMElement getOrphanEndElement(int offset, String tagName) {
}
// emp|
DOMElement nextElement = (DOMElement) next;
- if (nextElement.isOrphanEndTag(tagName)) {
+ if (nextElement.isOrphanEndTagOf(tagName)) {
return nextElement;
}
return null;
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMParser.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMParser.java
index 47c84fe083..73f47a05ff 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMParser.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMParser.java
@@ -52,7 +52,8 @@ public DOMDocument parse(String text, String uri, URIResolverExtensionManager re
return parse(new TextDocument(text, uri), resolverExtensionManager);
}
- public DOMDocument parse(String text, String uri, URIResolverExtensionManager resolverExtensionManager, boolean ignoreWhitespaceContent) {
+ public DOMDocument parse(String text, String uri, URIResolverExtensionManager resolverExtensionManager,
+ boolean ignoreWhitespaceContent) {
return parse(new TextDocument(text, uri), resolverExtensionManager, ignoreWhitespaceContent);
}
@@ -60,25 +61,27 @@ public DOMDocument parse(TextDocument document, URIResolverExtensionManager reso
return parse(document, resolverExtensionManager, true);
}
- public DOMDocument parse(TextDocument document, URIResolverExtensionManager resolverExtensionManager, boolean ignoreWhitespaceContent) {
+ public DOMDocument parse(TextDocument document, URIResolverExtensionManager resolverExtensionManager,
+ boolean ignoreWhitespaceContent) {
return parse(document, resolverExtensionManager, ignoreWhitespaceContent, null);
}
- public DOMDocument parse(TextDocument document, URIResolverExtensionManager resolverExtensionManager, boolean ignoreWhitespaceContent, CancelChecker monitor) {
+ public DOMDocument parse(TextDocument document, URIResolverExtensionManager resolverExtensionManager,
+ boolean ignoreWhitespaceContent, CancelChecker monitor) {
boolean isDTD = DOMUtils.isDTD(document.getUri());
boolean inDTDInternalSubset = false;
String text = document.getText();
Scanner scanner = XMLScanner.createScanner(text, 0, isDTD);
DOMDocument xmlDocument = new DOMDocument(document, resolverExtensionManager);
xmlDocument.setCancelChecker(monitor);
-
+
DOMNode curr = isDTD ? new DOMDocumentType(0, text.length()) : xmlDocument;
if (isDTD) {
xmlDocument.addChild(curr);
// This DOMDocumentType object is hidden, and just represents the DTD file
// nothing should affect it's closed status
- curr.closed = true;
+ curr.closed = true;
}
DOMNode lastClosed = curr;
DOMAttr attr = null;
@@ -86,29 +89,39 @@ public DOMDocument parse(TextDocument document, URIResolverExtensionManager reso
String pendingAttribute = null;
DOMNode tempWhitespaceContent = null;
boolean isInitialDeclaration = true; // A declaration can have multiple internal declarations
+ boolean previousTokenWasEndTagOpen = false;
TokenType token = scanner.scan();
while (token != TokenType.EOS) {
if (monitor != null) {
monitor.checkCanceled();
}
- if(tempWhitespaceContent != null && token != TokenType.EndTagOpen) {
+ if (tempWhitespaceContent != null && token != TokenType.EndTagOpen) {
tempWhitespaceContent = null;
}
+ if (previousTokenWasEndTagOpen) {
+ previousTokenWasEndTagOpen = false;
+ if (token != TokenType.EndTag) {
+ // The excepted token is not an EndTag, create a fake end tag element
+ DOMElement element = xmlDocument.createElement(endTagOpenOffset, endTagOpenOffset + 2);
+ element.endTagOpenOffset = endTagOpenOffset;
+ curr.addChild(element);
+ }
+ }
switch (token) {
case StartTagOpen: {
- if(!curr.isClosed() && curr.parent != null) {
- //The next node's parent (curr) is not closed at this point
- //so the node's parent (curr) will have its end position updated
- //to a newer end position.
+ if (!curr.isClosed() && curr.parent != null) {
+ // The next node's parent (curr) is not closed at this point
+ // so the node's parent (curr) will have its end position updated
+ // to a newer end position.
curr.end = scanner.getTokenOffset();
}
- if((curr.isClosed()) || curr.isDoctype()) {
- //The next node being considered is a child of 'curr'
- //and if 'curr' is already closed then 'curr' was not updated properly.
- //Or if we get a Doctype node then we know it was not closed and 'curr'
- //wasn't updated properly.
+ if ((curr.isClosed()) || curr.isDoctype()) {
+ // The next node being considered is a child of 'curr'
+ // and if 'curr' is already closed then 'curr' was not updated properly.
+ // Or if we get a Doctype node then we know it was not closed and 'curr'
+ // wasn't updated properly.
curr = curr.parent;
- inDTDInternalSubset = false; //In case it was previously in the internal subset
+ inDTDInternalSubset = false; // In case it was previously in the internal subset
}
DOMElement child = xmlDocument.createElement(scanner.getTokenOffset(), scanner.getTokenEnd());
child.startTagOpenOffset = scanner.getTokenOffset();
@@ -124,15 +137,14 @@ public DOMDocument parse(TextDocument document, URIResolverExtensionManager reso
break;
}
-
case StartTagClose:
if (curr.isElement()) {
DOMElement element = (DOMElement) curr;
curr.end = scanner.getTokenEnd(); // might be later set to end tag position
element.startTagCloseOffset = scanner.getTokenOffset();
- //never enters isEmptyElement() is always false
- if (element.getTagName() != null && isEmptyElement(element.getTagName()) && curr.parent != null) {
+ // never enters isEmptyElement() is always false
+ if (element.hasTagName() && isEmptyElement(element.getTagName()) && curr.parent != null) {
curr.closed = true;
curr = curr.parent;
}
@@ -149,12 +161,13 @@ public DOMDocument parse(TextDocument document, URIResolverExtensionManager reso
break;
case EndTagOpen:
- if(tempWhitespaceContent != null) {
+ if (tempWhitespaceContent != null) {
curr.addChild(tempWhitespaceContent);
tempWhitespaceContent = null;
}
endTagOpenOffset = scanner.getTokenOffset();
curr.end = scanner.getTokenOffset();
+ previousTokenWasEndTagOpen = true;
break;
case EndTag:
@@ -163,8 +176,8 @@ public DOMDocument parse(TextDocument document, URIResolverExtensionManager reso
DOMNode current = curr;
/**
- eg: will set a,b,c end position to the start of |
- */
+ * eg: will set a,b,c end position to the start of |
+ */
while (!(curr.isElement() && ((DOMElement) curr).isSameTag(closeTag)) && curr.parent != null) {
curr.end = endTagOpenOffset;
curr = curr.parent;
@@ -202,14 +215,14 @@ public DOMDocument parse(TextDocument document, URIResolverExtensionManager reso
if (curr.parent != null) {
curr.end = scanner.getTokenEnd();
lastClosed = curr;
- if(lastClosed.isElement()) {
+ if (lastClosed.isElement()) {
((DOMElement) curr).endTagCloseOffset = scanner.getTokenOffset();
}
- if(curr.isDoctype()) {
+ if (curr.isDoctype()) {
curr.closed = true;
}
curr = curr.parent;
-
+
}
break;
@@ -223,8 +236,8 @@ public DOMDocument parse(TextDocument document, URIResolverExtensionManager reso
}
case DelimiterAssign: {
- if(attr != null) {
- //Sets the value to the '=' position in case there is no AttributeValue
+ if (attr != null) {
+ // Sets the value to the '=' position in case there is no AttributeValue
attr.setValue(null, scanner.getTokenOffset(), scanner.getTokenEnd());
attr.setDelimiter(true);
}
@@ -302,14 +315,13 @@ public DOMDocument parse(TextDocument document, URIResolverExtensionManager reso
}
case StartCommentTag: {
- //Incase the tag before the comment tag (curr) was not properly closed
- //curr should be set to the root node.
- if(xmlDocument.isDTD() || inDTDInternalSubset) {
- while(!curr.isDoctype()) {
+ // Incase the tag before the comment tag (curr) was not properly closed
+ // curr should be set to the root node.
+ if (xmlDocument.isDTD() || inDTDInternalSubset) {
+ while (!curr.isDoctype()) {
curr = curr.parent;
}
- }
- else if((curr.isClosed())) {
+ } else if ((curr.isClosed())) {
curr = curr.parent;
}
DOMComment comment = xmlDocument.createComment(scanner.getTokenOffset(), text.length());
@@ -343,13 +355,13 @@ else if((curr.isClosed())) {
case Content: {
// FIXME: don't use getTokenText (substring) to know if the content is only
- // spaces or line feed (scanner should know that).
+ // spaces or line feed (scanner should know that).
boolean currIsDeclNode = curr instanceof DTDDeclNode;
if (currIsDeclNode) {
curr.end = scanner.getTokenOffset() - 1;
- while(!curr.isDoctype()) {
+ while (!curr.isDoctype()) {
curr = curr.getParentNode();
- }
+ }
}
int start = scanner.getTokenOffset();
int end = scanner.getTokenEnd();
@@ -357,23 +369,21 @@ else if((curr.isClosed())) {
textNode.closed = true;
String content = scanner.getTokenText();
- if(StringUtils.isWhitespace(content)) {
- if(ignoreWhitespaceContent) {
- if(curr.hasChildNodes()) {
+ if (StringUtils.isWhitespace(content)) {
+ if (ignoreWhitespaceContent) {
+ if (curr.hasChildNodes()) {
break;
}
-
+
tempWhitespaceContent = textNode;
break;
-
- }
- else if(!currIsDeclNode) {
+
+ } else if (!currIsDeclNode) {
textNode.setWhitespace(true);
- }
- else {
+ } else {
break;
}
-
+
}
curr.addChild(textNode);
@@ -439,12 +449,12 @@ else if(!currIsDeclNode) {
}
case DTDStartElement: {
- //If previous 'curr' was an unclosed DTD Declaration
+ // If previous 'curr' was an unclosed DTD Declaration
while (!curr.isDoctype()) {
curr.end = scanner.getTokenOffset();
curr = curr.getParentNode();
}
-
+
DTDElementDecl child = new DTDElementDecl(scanner.getTokenOffset(), text.length());
curr.addChild(child);
curr = child;
@@ -487,7 +497,7 @@ else if(!currIsDeclNode) {
curr = curr.getParentNode();
}
DTDAttlistDecl child = new DTDAttlistDecl(scanner.getTokenOffset(), text.length());
-
+
isInitialDeclaration = true;
curr.addChild(child);
curr = child;
@@ -502,7 +512,7 @@ else if(!currIsDeclNode) {
case DTDAttlistAttributeName: {
DTDAttlistDecl attribute = (DTDAttlistDecl) curr;
- if(isInitialDeclaration == false) {
+ if (isInitialDeclaration == false) {
// All additional declarations are created as new DTDAttlistDecl's
DTDAttlistDecl child = new DTDAttlistDecl(attribute.getStart(), attribute.getEnd());
attribute.addAdditionalAttDecl(child);
@@ -525,15 +535,14 @@ else if(!currIsDeclNode) {
DTDAttlistDecl attribute = (DTDAttlistDecl) curr;
attribute.setAttributeValue(scanner.getTokenOffset(), scanner.getTokenEnd());
- if(attribute.parent.isDTDAttListDecl()) { // Is not the root/main ATTLIST node
+ if (attribute.parent.isDTDAttListDecl()) { // Is not the root/main ATTLIST node
curr = attribute.parent;
- }
- else {
+ } else {
isInitialDeclaration = false;
}
break;
}
-
+
case DTDStartEntity: {
while (!curr.isDoctype()) { // If previous DTD Decl was unclosed
curr.end = scanner.getTokenOffset();
@@ -551,13 +560,13 @@ else if(!currIsDeclNode) {
break;
}
- case DTDEntityName : {
+ case DTDEntityName: {
DTDEntityDecl entity = (DTDEntityDecl) curr;
entity.setName(scanner.getTokenOffset(), scanner.getTokenEnd());
break;
}
- case DTDEntityValue : {
+ case DTDEntityValue: {
DTDEntityDecl entity = (DTDEntityDecl) curr;
entity.setValue(scanner.getTokenOffset(), scanner.getTokenEnd());
break;
@@ -625,8 +634,9 @@ else if(!currIsDeclNode) {
}
case DTDEndTag: {
- if ((curr.isDTDElementDecl() || curr.isDTDAttListDecl() || curr.isDTDEntityDecl() || curr.isDTDNotationDecl()) ) {
- while(curr.parent != null && !curr.parent.isDoctype()) {
+ if ((curr.isDTDElementDecl() || curr.isDTDAttListDecl() || curr.isDTDEntityDecl()
+ || curr.isDTDNotationDecl())) {
+ while (curr.parent != null && !curr.parent.isDoctype()) {
curr = curr.parent;
}
curr.end = scanner.getTokenEnd();
@@ -635,7 +645,7 @@ else if(!currIsDeclNode) {
}
break;
}
-
+
case DTDEndDoctypeTag: {
((DOMDocumentType) curr).end = scanner.getTokenEnd();
curr.closed = true;
@@ -645,7 +655,7 @@ else if(!currIsDeclNode) {
case DTDUnrecognizedParameters: {
DTDDeclNode node = (DTDDeclNode) curr;
- node.setUnrecognized(scanner.getTokenOffset(), ((XMLScanner)scanner).getLastNonWhitespaceOffset());
+ node.setUnrecognized(scanner.getTokenOffset(), ((XMLScanner) scanner).getLastNonWhitespaceOffset());
break;
}
@@ -653,7 +663,16 @@ else if(!currIsDeclNode) {
}
token = scanner.scan();
}
- while (curr.parent != null ) {
+ if (previousTokenWasEndTagOpen) {
+ previousTokenWasEndTagOpen = false;
+ if (token != TokenType.EndTag) {
+ // The excepted token is not an EndTag, create a fake end tag element
+ DOMElement element = xmlDocument.createElement(endTagOpenOffset, endTagOpenOffset + 2);
+ element.endTagOpenOffset = endTagOpenOffset;
+ curr.addChild(element);
+ }
+ }
+ while (curr.parent != null) {
curr.end = text.length();
curr = curr.parent;
}
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCompletionParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCompletionParticipant.java
index 91e93e387c..a1c14ed502 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCompletionParticipant.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCompletionParticipant.java
@@ -211,15 +211,17 @@ private static void addTagName(NodeList list, Set tags, ICompletionReque
Node node = list.item(i);
if (Node.ELEMENT_NODE == node.getNodeType()) {
DOMElement elt = (DOMElement) node;
- String tagName = elt.getTagName();
- if (!StringUtils.isEmpty(tagName) && !tags.contains(tagName)) {
- CompletionItem item = new CompletionItem(tagName);
- item.setKind(CompletionItemKind.Property);
- item.setFilterText(request.getFilterForStartTagName(tagName));
- String xml = elt.getOwnerDocument().getText().substring(elt.getStart(), elt.getEnd());
- item.setTextEdit(new TextEdit(request.getReplaceRange(), xml));
- response.addCompletionItem(item);
- tags.add(item.getLabel());
+ if (elt.hasTagName()) {
+ String tagName = elt.getTagName();
+ if (!tags.contains(tagName)) {
+ CompletionItem item = new CompletionItem(tagName);
+ item.setKind(CompletionItemKind.Property);
+ item.setFilterText(request.getFilterForStartTagName(tagName));
+ String xml = elt.getOwnerDocument().getText().substring(elt.getStart(), elt.getEnd());
+ item.setTextEdit(new TextEdit(request.getReplaceRange(), xml));
+ response.addCompletionItem(item);
+ tags.add(item.getLabel());
+ }
}
addTagName(elt.getChildNodes(), tags, request, response);
}
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/CloseStartTagCodeAction.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/CloseStartTagCodeAction.java
index d223f4887c..be478bf38b 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/CloseStartTagCodeAction.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/CloseStartTagCodeAction.java
@@ -47,7 +47,7 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen
if (!element.hasStartTag()) {
//
DOMElement parent = element.getParentElement();
- if (parent != null && parent.getTagName() != null) {
+ if (parent != null && parent.hasTagName()) {
//
// Replace with 'b' closing tag
String tagName = parent.getTagName();
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/EqRequiredInAttributeCodeAction.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/EqRequiredInAttributeCodeAction.java
index 4bd89c77c1..23d94e1324 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/EqRequiredInAttributeCodeAction.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/EqRequiredInAttributeCodeAction.java
@@ -42,8 +42,8 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen
int offset = document.offsetAt(range.getStart());
DOMNode node = document.findNodeAt(offset);
if (node != null && node.isElement()) {
- String tagName = ((DOMElement) node).getTagName();
- if (tagName != null) {
+ DOMElement element = (DOMElement) node;
+ if (element.hasTagName()) {
String insertText = "=\"\"";
CodeAction insertEqualsAndQuotesAction = CodeActionFactory.insert("Insert '" + insertText + "'",
diagnosticRange.getEnd(), insertText, document.getTextDocument(), diagnostic);
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java
index 4fd418364f..ce5ced722b 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java
@@ -30,7 +30,6 @@
import org.eclipse.lemminx.services.extensions.ICodeActionParticipant;
import org.eclipse.lemminx.services.extensions.IComponentProvider;
import org.eclipse.lemminx.settings.SharedSettings;
-import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lemminx.utils.XMLBuilder;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Diagnostic;
@@ -51,7 +50,7 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen
SharedSettings sharedSettings, IComponentProvider componentProvider) {
try {
DOMElement documentElement = document.getDocumentElement();
- if (documentElement == null || StringUtils.isEmpty(documentElement.getTagName())) {
+ if (documentElement == null || !documentElement.hasTagName()) {
return;
}
@@ -103,8 +102,8 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen
docType.endDoctype();
docType.linefeed();
CodeAction docTypeAction = createGrammarFileAndBindIt(
- "Generate '" + dtdFileName + "' and bind with DOCTYPE", dtdURI, dtdTemplate,
- docType.toString(), beforeTagOffset, document, diagnostic);
+ "Generate '" + dtdFileName + "' and bind with DOCTYPE", dtdURI, dtdTemplate, docType.toString(),
+ beforeTagOffset, document, diagnostic);
codeActions.add(docTypeAction);
// xml-model
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/AbstractPositionRequest.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/AbstractPositionRequest.java
index adff2fe96f..61966afd57 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/AbstractPositionRequest.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/AbstractPositionRequest.java
@@ -96,7 +96,7 @@ public int getOffset() {
@Override
public String getCurrentTag() {
- if (node != null && node.isElement() && ((DOMElement) node).getTagName() != null) {
+ if (node != null && node.isElement() && ((DOMElement) node).hasTagName()) {
return ((DOMElement) node).getTagName();
}
return null;
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLCompletions.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLCompletions.java
index ae593e3c86..7a7ea19bb2 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLCompletions.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLCompletions.java
@@ -489,7 +489,7 @@ public AutoCloseTagResponse doTagComplete(DOMDocument xmlDocument, Position posi
return null;
}
DOMElement element = ((DOMElement) node);
- if (node != null && node.isElement() && !element.isSelfClosed() && element.getTagName() != null
+ if (node != null && node.isElement() && !element.isSelfClosed() && element.hasTagName()
&& !isEmptyElement(((DOMElement) node).getTagName()) && node.getStart() < offset
&& (!element.hasEndTag() || (element.getTagName().equals(node.getParentNode().getNodeName())
&& !isBalanced(node)))) {
@@ -498,7 +498,7 @@ public AutoCloseTagResponse doTagComplete(DOMDocument xmlDocument, Position posi
}
} else if (cBefore == '<' && c == '/') { // Case: |
DOMNode node = xmlDocument.findNodeBefore(offset);
- while (node != null && node.isClosed()) {
+ while ((node != null && node.isClosed()) || (node.isElement() && ((DOMElement) node).isOrphanEndTag())) {
node = node.getParentNode();
}
if (node != null && node.isElement() && ((DOMElement) node).getTagName() != null) {
@@ -785,7 +785,7 @@ private static Range getTextRangeInsideContent(DOMNode node) {
}
return null;
case Node.TEXT_NODE:
- // ex : |
+ // ex : |
return XMLPositionUtility.selectText((DOMText) node);
}
// should never occur
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLHighlighting.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLHighlighting.java
index 618763514d..96348c8431 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLHighlighting.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLHighlighting.java
@@ -70,7 +70,7 @@ public List findDocumentHighlights(DOMDocument xmlDocument, P
private static void fillWithDefaultHighlights(DOMNode node, Position position, int offset,
List highlights, CancelChecker cancelChecker) {
- if (!node.isElement() || ((DOMElement) node).getTagName() == null) {
+ if (!node.isElement() || !((DOMElement) node).hasTagName()) {
return;
}
@@ -122,7 +122,7 @@ private void fillWithCustomHighlights(DOMNode node, Position position, int offse
List highlights, CancelChecker cancelChecker) {
// Consume highlighting participant
for (IHighlightingParticipant highlightingParticipant : extensionsRegistry.getHighlightingParticipants()) {
- highlightingParticipant.findDocumentHighlights(node, position, offset,highlights, cancelChecker);
+ highlightingParticipant.findDocumentHighlights(node, position, offset, highlights, cancelChecker);
}
}
}
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLHover.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLHover.java
index fe0a0845ad..6946bcbf2a 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLHover.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLHover.java
@@ -66,7 +66,7 @@ public Hover doHover(DOMDocument xmlDocument, Position position, SharedSettings
if (node == null) {
return null;
}
- if (node.isElement() && ((DOMElement) node).getTagName() != null) {
+ if (node.isElement() && ((DOMElement) node).hasTagName()) {
// Element is hovered
DOMElement element = (DOMElement) node;
if (element.hasEndTag() && offset >= element.getEndTagOpenOffset()) {
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLRename.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLRename.java
index 826ab8bb08..3ac8af9042 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLRename.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLRename.java
@@ -42,8 +42,7 @@
/**
* Handle all rename requests
*
- * Author:
- * Nikolas Komonen - nkomonen@redhat.com
+ * Author: Nikolas Komonen - nkomonen@redhat.com
*/
public class XMLRename {
@@ -68,27 +67,27 @@ public WorkspaceEdit doRename(DOMDocument xmlDocument, Position position, String
DOMNode node = renameRequest.getNode();
- if (node == null ||
- (!node.isAttribute() && !node.isElement()) ||
- (node.isElement() && ((DOMElement) node).getTagName() == null)) {
-
+ if (node == null || (!node.isAttribute() && !node.isElement())
+ || (node.isElement() && !((DOMElement) node).hasTagName())) {
+
return createWorkspaceEdit(xmlDocument.getDocumentURI(), Collections.emptyList());
}
List textEdits = new ArrayList<>();
-
+
for (IRenameParticipant participant : extensionsRegistry.getRenameParticipants()) {
participant.doRename(renameRequest, textEdits);
}
- for (TextEdit textEdit: getRenameTextEdits(xmlDocument, node, position, newText)) {
+ for (TextEdit textEdit : getRenameTextEdits(xmlDocument, node, position, newText)) {
textEdits.add(textEdit);
}
return createWorkspaceEdit(xmlDocument.getDocumentURI(), textEdits);
}
- private List getRenameTextEdits(DOMDocument xmlDocument, DOMNode node, Position position, String newText) {
+ private List getRenameTextEdits(DOMDocument xmlDocument, DOMNode node, Position position,
+ String newText) {
DOMElement element = getAssociatedElement(node);
if (node == null) {
@@ -111,8 +110,7 @@ private List getRenameTextEdits(DOMDocument xmlDocument, DOMNode node,
}
/**
- * Returns DOMElement
associated with
- * node
+ * Returns DOMElement
associated with node
*
* @param node node representing an element or attribute
* @return associated DOMElement
@@ -125,11 +123,12 @@ private DOMElement getAssociatedElement(DOMNode node) {
if (node.isAttribute()) {
return ((DOMAttr) node).getOwnerElement();
}
-
+
return (DOMElement) node;
}
- private List getCDATARenameTextEdits(DOMDocument xmlDocument, DOMElement element, Position position, String newText) {
+ private List getCDATARenameTextEdits(DOMDocument xmlDocument, DOMElement element, Position position,
+ String newText) {
Position startPos = null;
Position endPos = null;
Range tempRange = null;
@@ -143,7 +142,7 @@ private List getCDATARenameTextEdits(DOMDocument xmlDocument, DOMEleme
LOGGER.log(Level.SEVERE, "In XMLRename the Node at provided Offset is a BadLocation", e);
return Collections.emptyList();
}
-
+
if (covers(tempRange, position)) {
startPos.setCharacter(startPos.getCharacter() + 1); // {Cursor} <{Cursor}![CDATA[
endPos.setCharacter(endPos.getCharacter() - 1); // ]]>{Cursor} -> ]]{Cursor}>
@@ -159,27 +158,32 @@ private List getCDATARenameTextEdits(DOMDocument xmlDocument, DOMEleme
private boolean isRenameTagName(DOMDocument document, DOMElement element, Position position) {
Range startTagRange = getTagNameRange(TokenType.StartTag, element.getStart(), document);
- Range endTagRange = element.hasEndTag() ? getTagNameRange(TokenType.EndTag, element.getEndTagOpenOffset(), document)
+ Range endTagRange = element.hasEndTag()
+ ? getTagNameRange(TokenType.EndTag, element.getEndTagOpenOffset(), document)
: null;
-
+
return doesTagCoverPosition(startTagRange, endTagRange, position);
}
- private List getTagNameRenameTextEdits(DOMDocument xmlDocument, DOMElement element, Position position, String newText) {
-
+ private List getTagNameRenameTextEdits(DOMDocument xmlDocument, DOMElement element, Position position,
+ String newText) {
+
Range startTagRange = getTagNameRange(TokenType.StartTag, element.getStart(), xmlDocument);
- Range endTagRange = element.hasEndTag() ? getTagNameRange(TokenType.EndTag, element.getEndTagOpenOffset(), xmlDocument)
- : null;
-
- //Check if xsd namespace rename
+ Range endTagRange = element.hasEndTag()
+ ? getTagNameRange(TokenType.EndTag, element.getEndTagOpenOffset(), xmlDocument)
+ : null;
+
+ // Check if xsd namespace rename
String fullNodeName = element.getNodeName();
int indexOfColon = fullNodeName.indexOf(":");
- if(indexOfColon > 0) {
+ if (indexOfColon > 0) {
Position startTagStartPosition = startTagRange.getStart();
- Position startTagPrefixPosition = new Position(startTagStartPosition.getLine(), startTagStartPosition.getCharacter() + indexOfColon);
+ Position startTagPrefixPosition = new Position(startTagStartPosition.getLine(),
+ startTagStartPosition.getCharacter() + indexOfColon);
Position endTagStartPosition = endTagRange.getStart();
- Position endTagPrefixPosition = new Position(endTagStartPosition.getLine(), endTagStartPosition.getCharacter() + indexOfColon);
+ Position endTagPrefixPosition = new Position(endTagStartPosition.getLine(),
+ endTagStartPosition.getCharacter() + indexOfColon);
Range startTagPrefixRange = new Range(startTagStartPosition, startTagPrefixPosition);
Range endTagPrefixRange = new Range(endTagStartPosition, endTagPrefixPosition);
@@ -187,14 +191,16 @@ private List getTagNameRenameTextEdits(DOMDocument xmlDocument, DOMEle
if (doesTagCoverPosition(startTagPrefixRange, endTagPrefixRange, position)) {// Element prefix rename
String prefix = element.getPrefix();
return renameElementNamespace(xmlDocument, element, prefix.length(), newText);
- } else { //suffix rename without wiping namespace
+ } else { // suffix rename without wiping namespace
String suffixName = element.getLocalName();
int suffixLength = suffixName.length();
Position startTagEndPosition = startTagRange.getEnd();
- Position suffixStartPositionStart = new Position(startTagEndPosition.getLine(), startTagEndPosition.getCharacter() - suffixLength);
+ Position suffixStartPositionStart = new Position(startTagEndPosition.getLine(),
+ startTagEndPosition.getCharacter() - suffixLength);
Position endTagEndPosition = endTagRange.getEnd();
- Position suffixEndPositionStart = new Position(endTagEndPosition.getLine(), endTagEndPosition.getCharacter() - suffixLength);
+ Position suffixEndPositionStart = new Position(endTagEndPosition.getLine(),
+ endTagEndPosition.getCharacter() - suffixLength);
Range suffixRangeStart = new Range(suffixStartPositionStart, startTagEndPosition);
Range suffixRangeEnd = new Range(suffixEndPositionStart, endTagEndPosition);
@@ -202,21 +208,22 @@ private List getTagNameRenameTextEdits(DOMDocument xmlDocument, DOMEle
return getRenameList(suffixRangeStart, suffixRangeEnd, newText);
}
}
- //Regular tag name rename
+ // Regular tag name rename
return getRenameList(startTagRange, endTagRange, newText);
}
- private List getXmlnsAttrRenameTextEdits(DOMDocument xmlDocument, DOMElement element, Position position, String newText) {
+ private List getXmlnsAttrRenameTextEdits(DOMDocument xmlDocument, DOMElement element, Position position,
+ String newText) {
List attributes = element.getAttributeNodes();
- if(attributes == null) {
+ if (attributes == null) {
return Collections.emptyList();
}
for (DOMAttr attr : attributes) {
DOMNode nameNode = attr.getNodeAttrName();
- if(!attr.isXmlns()) {
+ if (!attr.isXmlns()) {
continue;
}
@@ -228,8 +235,8 @@ private List getXmlnsAttrRenameTextEdits(DOMDocument xmlDocument, DOME
} catch (BadLocationException e) {
continue;
}
-
- if(covers(new Range(start, end), position)) { // Rename over the suffix of 'xmlns:XXX'
+
+ if (covers(new Range(start, end), position)) { // Rename over the suffix of 'xmlns:XXX'
String namespaceName = attr.getLocalName();
return renameAllNamespaceOccurrences(xmlDocument, namespaceName, newText, attr);
}
@@ -245,6 +252,7 @@ private WorkspaceEdit createWorkspaceEdit(String documentURI, List tex
/**
* Creates a list of start and end tag rename's.
+ *
* @param startTagRange
* @param endTagRange
* @param newText
@@ -262,42 +270,45 @@ private static List getRenameList(Range startTagRange, Range endTagRan
}
/**
- * Renames all occurences of the namespace in a document, that match
- * the given old namespace.
+ * Renames all occurences of the namespace in a document, that match the given
+ * old namespace.
+ *
* @param document
* @param oldNamespace
* @param newNamespace
* @param rootAttr
* @return
*/
- private static List renameAllNamespaceOccurrences(DOMDocument document, String oldNamespace, String newNamespace, @Nullable DOMAttr rootAttr) {
+ private static List renameAllNamespaceOccurrences(DOMDocument document, String oldNamespace,
+ String newNamespace, @Nullable DOMAttr rootAttr) {
DOMElement rootElement = document.getDocumentElement();
-
+
List edits = new ArrayList();
// Renames the xmlns:NAME_SPACE attribute
- if(rootAttr != null) {
+ if (rootAttr != null) {
Position start;
try {
start = document.positionAt(rootAttr.getStart() + "xmlns:".length());
} catch (BadLocationException e) {
start = null;
}
-
- if(start != null) {
+
+ if (start != null) {
Position end = new Position(start.getLine(), start.getCharacter() + oldNamespace.length());
edits.add(new TextEdit(new Range(start, end), newNamespace));
}
}
- //Renames all elements with oldNamespace
+ // Renames all elements with oldNamespace
List children = Arrays.asList(rootElement);
return renameElementsNamespace(document, edits, children, oldNamespace, newNamespace);
}
/**
- * Will traverse through the given elements and their children,
- * updating all namespaces that match the given old namespace.
+ * Will traverse through the given elements and their children, updating all
+ * namespaces that match the given old namespace.
+ *
* @param document
* @param edits
* @param elements
@@ -305,38 +316,40 @@ private static List renameAllNamespaceOccurrences(DOMDocument document
* @param newNamespace
* @return
*/
- private static List renameElementsNamespace(DOMDocument document, List edits, List elements, String oldNamespace, String newNamespace) {
+ private static List renameElementsNamespace(DOMDocument document, List edits,
+ List elements, String oldNamespace, String newNamespace) {
int oldNamespaceLength = oldNamespace.length();
for (DOMNode node : elements) {
- if(node.isElement()) {
+ if (node.isElement()) {
DOMElement element = (DOMElement) node;
- if(oldNamespace.equals(element.getPrefix())) {
+ if (oldNamespace.equals(element.getPrefix())) {
edits.addAll(renameElementNamespace(document, element, oldNamespaceLength, newNamespace));
}
- if(element.hasAttributes()) {
+ if (element.hasAttributes()) {
edits.addAll(renameElementAttributeValueNamespace(document, element, oldNamespace, newNamespace));
}
-
- if(element.hasChildNodes()) {
+
+ if (element.hasChildNodes()) {
renameElementsNamespace(document, edits, element.getChildren(), oldNamespace, newNamespace);
}
}
}
-
+
return edits;
}
/**
- * Will rename the namespace of a given element
+ * Will rename the namespace of a given element
*/
- private static List renameElementNamespace(DOMDocument document, DOMElement element, int oldNamespaceLength, String newNamespace) {
+ private static List renameElementNamespace(DOMDocument document, DOMElement element,
+ int oldNamespaceLength, String newNamespace) {
List edits = new ArrayList();
Range[] ranges = createNamespaceRange(document, element, oldNamespaceLength);
- if(ranges == null) {
+ if (ranges == null) {
return edits;
}
for (Range r : ranges) {
- if(r != null) {
+ if (r != null) {
edits.add(new TextEdit(r, newNamespace));
}
}
@@ -344,26 +357,29 @@ private static List renameElementNamespace(DOMDocument document, DOMEl
}
/**
- * Will rename the namespace of an element's attribute values with the matching namespace.
+ * Will rename the namespace of an element's attribute values with the matching
+ * namespace.
+ *
* @param document
* @param element
* @param oldNamespace
* @param newNamespace
* @return
*/
- private static List renameElementAttributeValueNamespace(DOMDocument document, DOMElement element, String oldNamespace, String newNamespace) {
-
+ private static List renameElementAttributeValueNamespace(DOMDocument document, DOMElement element,
+ String oldNamespace, String newNamespace) {
+
List attributes = element.getAttributeNodes();
List edits = new ArrayList();
- if(attributes != null) {
+ if (attributes != null) {
for (DOMAttr attr : attributes) {
DOMNode attrValue = attr.getNodeAttrValue();
- if(attrValue != null) {
+ if (attrValue != null) {
String attrValueText = attr.getValue();
- if(attrValueText != null && attrValueText.startsWith(oldNamespace + ":")) {
+ if (attrValueText != null && attrValueText.startsWith(oldNamespace + ":")) {
int startOffset = attrValue.getStart() + 1;
- Position start,end;
+ Position start, end;
try {
start = document.positionAt(startOffset);
end = new Position(start.getLine(), start.getCharacter() + oldNamespace.length());
@@ -380,6 +396,7 @@ private static List renameElementAttributeValueNamespace(DOMDocument d
/**
* Returns the ranges of the namespace of a start and end tag of an element.
+ *
* @param document
* @param element
* @param namespaceLength
@@ -391,14 +408,14 @@ private static Range[] createNamespaceRange(DOMDocument document, DOMElement ele
Position start;
Position end;
try {
- if(element.hasStartTag()) {
- int startName = element.getStart() + 1; //skip '<'
+ if (element.hasStartTag()) {
+ int startName = element.getStart() + 1; // skip '<'
start = document.positionAt(startName);
end = new Position(start.getLine(), start.getCharacter() + namespaceLength);
ranges[0] = new Range(start, end);
}
- if(element.hasEndTag()) {
- int startName = element.getEndTagOpenOffset() + 2; //skip ''
+ if (element.hasEndTag()) {
+ int startName = element.getEndTagOpenOffset() + 2; // skip ''
start = document.positionAt(startName);
end = new Position(start.getLine(), start.getCharacter() + namespaceLength);
ranges[1] = new Range(start, end);
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/ProcessingInstructionSnippetContext.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/ProcessingInstructionSnippetContext.java
index 08b0245ffe..1624cfcb90 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/ProcessingInstructionSnippetContext.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/ProcessingInstructionSnippetContext.java
@@ -56,7 +56,7 @@ public boolean isMatch(ICompletionRequest request, Map model) {
return false;
}
- if (documentElement != null && documentElement.getTagName() != null) {
+ if (documentElement != null && documentElement.hasTagName()) {
return offset <= documentElement.getStart();
}
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/SnippetContextUtils.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/SnippetContextUtils.java
index 71b6b5bfe6..7d4683cbf9 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/SnippetContextUtils.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/SnippetContextUtils.java
@@ -45,7 +45,12 @@ public static boolean canAcceptExpression(ICompletionRequest request) {
}
if (node.isElement()) {
DOMElement element = (DOMElement) node;
- if (element.getTagName() == null) {
+ if (element.isOrphanEndTag()) {
+ //
+ //
+ return false;
+ }
+ if (!element.hasTagName()) {
// <|
// model) {
// No xml processing instruction, check if completion was triggered before the
// document element
DOMElement documentElement = document.getDocumentElement();
- if (documentElement != null && documentElement.getTagName() != null) {
+ if (documentElement != null && documentElement.hasTagName()) {
return offset <= documentElement.getStart();
}
return true;
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLBuilder.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLBuilder.java
index cfed27deb7..b74f93e52d 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLBuilder.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLBuilder.java
@@ -90,7 +90,9 @@ public XMLBuilder endElement(String prefix, String name, boolean isEndTagClosed)
append(prefix);
append(":");
}
- append(name);
+ if (name != null) {
+ append(name);
+ }
if (isEndTagClosed) {
append(">");
}
diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLPositionUtility.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLPositionUtility.java
index 55608f7ba0..d8c40f7ee0 100644
--- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLPositionUtility.java
+++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLPositionUtility.java
@@ -268,7 +268,7 @@ private static int adjustOffsetForAttribute(int offset, DOMDocument document) {
public static Range selectChildEndTag(String childTag, int offset, DOMDocument document) {
DOMNode parent = document.findNodeAt(offset);
- if (parent == null || !parent.isElement() || ((DOMElement) parent).getTagName() == null) {
+ if (parent == null || !parent.isElement() || !((DOMElement) parent).hasTagName()) {
return null;
}
@@ -421,7 +421,7 @@ private static Range selectStartTagName(DOMNode element, boolean localNameOnly)
private static int getStartTagLength(DOMNode node) {
if (node.isElement()) {
DOMElement element = (DOMElement) node;
- return element.getTagName() != null ? element.getTagName().length() : 0;
+ return element.hasTagName() ? element.getTagName().length() : 0;
} else if (node.isProcessingInstruction() || node.isProlog()) {
DOMProcessingInstruction element = (DOMProcessingInstruction) node;
return element.getTarget() != null ? element.getTarget().length() : 0;
diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/dom/DOMParserTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/dom/DOMParserTest.java
index bc82bd7717..3cc7c666c9 100644
--- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/dom/DOMParserTest.java
+++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/dom/DOMParserTest.java
@@ -459,6 +459,115 @@ public void elementOffsets() {
assertFalse(a.isInStartTag(3)); // |
}
+ @Test
+ public void startTag() {
+ // '<'
+ DOMDocument document = DOMParser.getInstance().parse("<", "", null);
+ DOMElement a = document.getDocumentElement();
+ assertNotNull(a);
+ assertFalse(a.hasTagName());
+ assertTrue(a.hasStartTag());
+ assertFalse(a.hasEndTag());
+
+ // ''
+ document = DOMParser.getInstance().parse("", "", null);
+ a = document.getDocumentElement();
+ assertNotNull(a);
+ assertTrue(a.hasTagName());
+ assertEquals("a", a.getTagName());
+ assertTrue(a.hasStartTag());
+ assertTrue(a.isStartTagClosed());
+ assertFalse(a.hasEndTag());
+
+ // ''
+ document = DOMParser.getInstance().parse("", "", null);
+ a = document.getDocumentElement();
+ assertNotNull(a);
+ assertTrue(a.hasTagName());
+ assertEquals("a", a.getTagName());
+ assertTrue(a.hasStartTag());
+ assertTrue(a.isStartTagClosed());
+ assertTrue(a.hasEndTag());
+ assertTrue(a.isEndTagClosed());
+ }
+
+ @Test
+ public void endTag() {
+ // ''
+ DOMDocument document = DOMParser.getInstance().parse("", "", null);
+ DOMElement a = document.getDocumentElement();
+ assertNotNull(a);
+ assertFalse(a.hasTagName());
+ assertFalse(a.hasStartTag());
+ assertTrue(a.hasEndTag());
+ assertTrue(a.isOrphanEndTag());
+
+ // ''
+ document = DOMParser.getInstance().parse("", "", null);
+ a = document.getDocumentElement();
+ assertNotNull(a);
+ assertTrue(a.hasChildNodes());
+
+ DOMNode child = a.getChild(0);
+ assertNotNull(child);
+ assertTrue(child.isElement());
+ DOMElement invalidEndTag = (DOMElement) child;
+ assertFalse(invalidEndTag.hasTagName());
+ assertFalse(invalidEndTag.hasStartTag());
+ assertTrue(invalidEndTag.hasEndTag());
+ assertTrue(invalidEndTag.isOrphanEndTag());
+
+ // ''
+ document = DOMParser.getInstance().parse("", "", null);
+ DOMElement root = document.getDocumentElement();
+ assertNotNull(root);
+ assertTrue(root.hasChildNodes());
+
+ a = (DOMElement) root.getChild(0);
+ assertNotNull(a);
+ assertTrue(a.hasChildNodes());
+
+ child = a.getChild(0);
+ assertNotNull(child);
+ assertTrue(child.isElement());
+ invalidEndTag = (DOMElement) child;
+ assertFalse(invalidEndTag.hasTagName());
+ assertFalse(invalidEndTag.hasStartTag());
+ assertTrue(invalidEndTag.hasEndTag());
+ assertTrue(invalidEndTag.isOrphanEndTag());
+
+ }
+
@Test
public void testDoctype1() {
String xml = " \n"
diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLCompletionTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLCompletionTest.java
index fd06c96ac6..2514751e3f 100644
--- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLCompletionTest.java
+++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLCompletionTest.java
@@ -80,14 +80,7 @@ public void successfulEndTagCompletion() throws BadLocationException {
@Test
public void successfulEndTagCompletionWithIndent() throws BadLocationException {
- testCompletionFor(" \r\n" + //
- "|", 3 + 2 /* CDATA and Comments */, //
- c("End with ''", " ", r(1, 0, 1, 0), ""), //
- c("#region", "", r(1, 0, 1, 0), ""), //
- c("#endregion", "", r(1, 0, 1, 0), ""));
- testCompletionFor(" \r\n" + //
- "<|", 1 + 2 /* CDATA and Comments */, //
- c("End with ''", " ", r(1, 0, 1, 1), ""));
+
testCompletionFor("|", 1, c("End with ''", "/a>", r(0, 4, 0, 5), "/a>"));
testCompletionFor(" \r\n" + //
diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLFormatterTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLFormatterTest.java
index c1e67558ae..05ca632349 100644
--- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLFormatterTest.java
+++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLFormatterTest.java
@@ -33,7 +33,7 @@ public class XMLFormatterTest {
public void closeStartTagMissing() throws BadLocationException {
// Don't close tag with bad XML
String content = "\r\n" + //
+ " \r\n" + //
+ " \r\n" + //
+ "";
+ String expected = content;
+ assertFormat(content, expected);
+ }
+
@Test
public void endTagMissing() throws BadLocationException {
String content = "\r\n" + //
diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLSymbolInformationsTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLSymbolInformationsTest.java
index 352aef5f61..06c2ce0e60 100644
--- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLSymbolInformationsTest.java
+++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLSymbolInformationsTest.java
@@ -210,6 +210,36 @@ public void singleEndTag() throws BadLocationException {
}
+ @Test
+ public void invalidEndTag() {
+ String xmlText = "";
+ initializeTestObjects(xmlText, testURI);
+
+ List expectedSymbolInfos = new ArrayList();
+ currentLocation = createLocation(testURI, 0, 2, xmlDocument);
+ currentSymbolInfo = createSymbolInformation("?", SymbolKind.Field, currentLocation, "");
+ expectedSymbolInfos.add(currentSymbolInfo);
+
+ assertSymbols(expectedSymbolInfos, actualSymbolInfos);
+ }
+
+ @Test
+ public void invalidEndTagAfterRoot() {
+ String xmlText = "";
+ initializeTestObjects(xmlText, testURI);
+
+ List expectedSymbolInfos = new ArrayList();
+ currentLocation = createLocation(testURI, 0, 5, xmlDocument);
+ currentSymbolInfo = createSymbolInformation("a", SymbolKind.Field, currentLocation, "");
+ expectedSymbolInfos.add(currentSymbolInfo);
+
+ currentLocation = createLocation(testURI, 3, 5, xmlDocument);
+ currentSymbolInfo = createSymbolInformation("?", SymbolKind.Field, currentLocation, "a");
+ expectedSymbolInfos.add(currentSymbolInfo);
+
+ assertSymbols(expectedSymbolInfos, actualSymbolInfos);
+ }
+
@Test
public void insideEndTag() throws BadLocationException {
// assertRename("", "newText", edits("newText", r(0, 1, 5),