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..26eafa6375 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 @@ -402,7 +402,11 @@ public boolean isEndTagClosed() { * tag, eg: ) and false otherwise. */ public boolean isOrphanEndTag(String tagName) { - return isSameTag(tagName) && hasEndTag() && !hasStartTag(); + return isSameTag(tagName) && isOrphanEndTag(); + } + + public boolean isOrphanEndTag() { + return hasEndTag() && !hasStartTag(); } @Override diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/XMLSyntaxErrorCode.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/XMLSyntaxErrorCode.java index 49b7d82939..08c8fe6479 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/XMLSyntaxErrorCode.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/XMLSyntaxErrorCode.java @@ -216,7 +216,7 @@ public static Range toLSPRange(XMLLocator location, XMLSyntaxErrorCode code, Obj return XMLPositionUtility.selectEndTagName(offset, document); case ETagRequired: { String tag = getString(arguments[0]); - return XMLPositionUtility.selectChildEndTag(tag, offset, document); + return XMLPositionUtility.selectStartTagNameOfUnclosedElement(tag, offset, document); } case SemicolonRequiredInReference: { EntityReferenceRange range = XMLPositionUtility.selectEntityReference(offset + 1, document, false); 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..b496c0a0b8 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 @@ -44,18 +44,9 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen if (node != null && node.isElement()) { int diagnosticEndOffset = document.offsetAt(diagnosticRange.getEnd()); DOMElement element = (DOMElement) node; - if (!element.hasStartTag()) { + if (element.isOrphanEndTag()) { // - DOMElement parent = element.getParentElement(); - if (parent != null && parent.getTagName() != null) { - // - // Replace with 'b' closing tag - String tagName = parent.getTagName(); - Range replaceRange = XMLPositionUtility.selectEndTagName(element); - CodeAction replaceTagAction = CodeActionFactory.replace("Replace with '" + tagName + "' closing tag", - replaceRange, tagName, document.getTextDocument(), diagnostic); - codeActions.add(replaceTagAction); - } + addReplaceCodeAction(element, diagnostic, codeActions); } else { // boolean startTagClosed = element.isStartTagClosed(); @@ -99,7 +90,16 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen endPosition, insertText, document.getTextDocument(), diagnostic); codeActions.add(closeEndTagAction); } - + // Search orphean elements in the children + List children = element.getChildren(); + for (DOMNode child : children) { + if (child.isElement()) { + DOMElement childElement = (DOMElement) child; + if (childElement.isOrphanEndTag()) { + addReplaceCodeAction(childElement, diagnostic, codeActions); + } + } + } } else { // ex : children) { - for (DOMNode child : children) { - if (!child.isClosed()) { - return child; - } + DOMElement element = findUnclosedElement(tagName, parent); + if (element != null) { + return selectStartTagName(element); } return null; } - private static DOMNode findUnclosedChildNode(String childTag, List children) { - for (DOMNode child : children) { - if (child.isElement() && childTag != null && childTag.equals(((DOMElement) child).getTagName()) - && !child.isClosed()) { - return child; + private static DOMElement findUnclosedElement(String tagName, DOMElement parent) { + List children = parent.getChildren(); + for (int i = children.size() - 1; i >= 0; i--) { + DOMNode child = parent.getChild(i); + if (child.isElement()) { + DOMElement element = (DOMElement) child; + DOMElement element2 = findUnclosedElement(tagName, element); + if (element2 != null) { + return element2; + } + if (isUnclosedStartTag(element, tagName)) { + return element; + } } } + if (isUnclosedStartTag(parent, tagName)) { + return parent; + } return null; } + private static boolean isUnclosedStartTag(DOMElement element, String tagName) { + return (!element.isClosed() && element.hasStartTag() && element.isSameTag(tagName)); + } + /** * Returns the range of the root start tag (excludes the '<') of the given * document and null otherwise. diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java index 272a57b8b2..90531d199e 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java @@ -211,10 +211,24 @@ public void testETagRequiredWithReplace() throws Exception { String xml = "\r\n" + // " \r\n" + // " "; - Diagnostic d = d(2, 4, 2, 5, XMLSyntaxErrorCode.ETagRequired); + Diagnostic d = d(1, 2, 1, 3, XMLSyntaxErrorCode.ETagRequired); testDiagnosticsFor(xml, d); testCodeActionsFor(xml, d, // - ca(d, te(2, 4, 2, 5, "b"))); + ca(d, te(2, 6, 2, 6, "\r\n ")), // code action for close + ca(d, te(2, 4, 2, 5, "b") // code action for replacing with + )); + } + + @Test + public void testETagRequired4() throws Exception { + String xml = "\r\n" + // + " "; + Diagnostic d = d(1, 3, 1, 6, XMLSyntaxErrorCode.ETagRequired); + testDiagnosticsFor(xml, d); + // testCodeActionsFor(xml, d, // + // ca(d, te(2, 6, 2, 6, "\r\n ")) // code action for close + //); } /**