From af09659c7ca4998fac87e1027de644a4167a4102 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 8 Oct 2020 16:57:10 -0400 Subject: [PATCH] Fix error range for cvc-complex-type-2.3 Now handles text nodes that are only take up one line, as well as . Closes #885 Signed-off-by: David Thompson --- .../lemminx/utils/XMLPositionUtility.java | 81 +++++++-------- .../XMLSchemaDiagnosticsTest.java | 99 ++++++++++++++++++- .../src/test/resources/xsd/close-tag-type.xsd | 19 ++++ 3 files changed, 158 insertions(+), 41 deletions(-) create mode 100644 org.eclipse.lemminx/src/test/resources/xsd/close-tag-type.xsd 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 d8c40f7ee..4b79e3b82 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 @@ -81,7 +81,7 @@ private XMLPositionUtility() { /** * Returns the attribute name range and null otherwise. - * + * * @param attr the attribute. * @return the attribute name range and null otherwise. */ @@ -100,7 +100,7 @@ public static Range selectAttributeNameAt(int offset, DOMDocument document) { /** * Returns the attribute value range and null otherwise. - * + * * @param attr the attribute. * @return the attribute value range and null otherwise. */ @@ -171,7 +171,7 @@ public static Range selectAttributeNameFromGivenNameAt(String attrName, int offs /** * Returns the range of the prefix of an attribute name - * + * * For example, if attrName = "xsi:example", the range for "xsi" will be * returned */ @@ -309,7 +309,7 @@ private static DOMNode findUnclosedChildNode(String childTag, List chil /** * Returns the range of the root start tag (excludes the '<') of the given * document and null otherwise. - * + * * @param document the DOM document. * @return the range of the root start tag (excludes the '<') of the given * document and null otherwise. @@ -328,9 +328,9 @@ public static Range selectRootStartTag(DOMDocument document) { /** * Finds the root element of the given document and returns the attribute value * Range for the attribute attrName. - * + * * If attrName is not declared then null is returned. - * + * * @param attrName The name of the attribute to find the range of the value for * @param document The document to use the root element of * @return The range in document where the declared value of @@ -363,7 +363,7 @@ public static Range selectStartTagName(int offset, DOMDocument document) { /** * Returns the range of the start tag name (excludes the '<') of the given * element and null otherwise. - * + * * @param element the DOM element * @return the range of the start tag of the given element and null * otherwise. @@ -375,7 +375,7 @@ public static Range selectStartTagName(DOMNode element) { /** * Returns the range of a tag's local name. If the tag does not have a prefix, * implying it doesn't have a local name, it will return null. - * + * * @param element * @return */ @@ -386,10 +386,10 @@ public static Range selectStartTagLocalName(DOMNode element) { /** * Returns the range of the start tag name (excludes the '<') of the given * element and null otherwise. - * + * * If suffixOnly is true then it will try to return the range of the * localName/suffix. Else it will return null. - * + * * @param element the DOM element * @param suffixOnly select the suffix portion, only when a prefix exists * @return the range of the start tag of the given element and null @@ -450,7 +450,7 @@ public static Range selectEndTagName(int offset, DOMDocument document) { /** * Returns the range of the end tag of the given element name and * null otherwise. - * + * * @param element the DOM element * @return the range of the end tag of the given element and null * otherwise. @@ -462,7 +462,7 @@ public static Range selectEndTagName(DOMElement element) { /** * Returns the range of the end tag of the given LOCAL element name * and null otherwise. - * + * * @param element the DOM element * @return the range of the end tag of the given element and null * otherwise. @@ -474,7 +474,7 @@ public static Range selectEndTagLocalName(DOMElement element) { /** * Returns the range of the end tag of the given element and null * otherwise. - * + * * @param element the DOM element * @return the range of the end tag of the given element and null * otherwise. @@ -502,7 +502,7 @@ public static Range selectEndTagName(DOMElement element, boolean localNameOnly) /** * Returns the range of the entity reference in a text node (ex : &) and * null otherwise. - * + * * @param offset the offset * @param document the document * @return the range of the entity reference in a text node (ex : &) and @@ -515,7 +515,7 @@ public static EntityReferenceRange selectEntityReference(int offset, DOMDocument /** * Returns the range of the entity reference in a text node (ex : &) and * null otherwise. - * + * * @param offset the offset * @param document the document * @param endsWithSemicolon true if the entity reference must end with ';' and @@ -547,7 +547,7 @@ public static EntityReferenceRange selectEntityReference(int offset, DOMDocument /** * Returns the start offset of the entity reference (ex : &am|p;) from the left * of the given offset and -1 if no entity reference. - * + * * @param text the XML content. * @param offset the offset. * @return the start offset of the entity reference (ex : &am|p;) from the left @@ -582,7 +582,7 @@ public static int getEntityReferenceStartOffset(String text, int offset) { /** * Returns the end offset of the entity reference (ex : &am|p;) from the right * of the given offset and -1 if no entity reference. - * + * * @param text the XML content. * @param offset the offset. * @return the end offset of the entity reference (ex : &am|p;) from the right @@ -605,12 +605,13 @@ public static Range selectFirstNonWhitespaceText(int offset, DOMDocument documen DOMNode element = document.findNodeAt(offset); if (element != null) { for (DOMNode node : element.getChildren()) { - if (node.isCharacterData() && ((DOMCharacterData) node).hasMultiLine()) { - String content = ((DOMCharacterData) node).getData(); - int start = node.getStart(); + if (node.isCharacterData()) { + DOMCharacterData data = (DOMCharacterData) node; + int start = data.getStartContent(); Integer end = null; - for (int i = 0; i < content.length(); i++) { - char c = content.charAt(i); + String text = document.getText(); + for (int i = start; i < data.getEndContent(); i++) { + char c = text.charAt(i); if (end == null) { if (Character.isWhitespace(c)) { start++; @@ -637,7 +638,7 @@ public static Range selectFirstNonWhitespaceText(int offset, DOMDocument documen /** * Returns the text content range and null otherwise. - * + * * @param text the DOM text node.. * @return the text content range and null otherwise. */ @@ -670,17 +671,17 @@ public static Range selectContent(int offset, DOMDocument document) { /** * Returns the range covering the trimmed text belonging to the node located at * offset. - * + * * This method assumes that the node located at offset only contains text. - * + * * For example, if the node located at offset is: - * + * * hello - * + * * - * + * * the returned range will cover only "hello". - * + * * @param offset * @param document * @return range covering the trimmed text belonging to the node located at @@ -731,7 +732,7 @@ public static Range selectDTDElementDeclAt(int offset, DOMDocument document) { /** * Will give the range for the last VALID DTD Decl parameter at 'offset'. An * unrecognized Parameter is not considered VALID, - * + * * eg: "node. - * + * * @param node the node * @return the range for the given node. */ @@ -868,7 +869,7 @@ public static Range createRange(int startOffset, int endOffset, DOMDocument docu /** * Returns the location link for the given origin and * target nodes. - * + * * @param origin the origin node. * @param target the target node. * @return the location link for the given origin and @@ -887,7 +888,7 @@ public static LocationLink createLocationLink(DOMRange origin, DOMRange target) /** * Returns the location link for the given origin and * target nodes. - * + * * @param origin the origin node. * @param target the target node. * @return the location link for the given origin and @@ -903,7 +904,7 @@ public static LocationLink createLocationLink(Range origin, DOMRange target) { /** * Returns the location link for the given origin and * target nodes. - * + * * @param origin the origin node. * @param target the target node. * @return the location link for the given origin and @@ -917,7 +918,7 @@ public static LocationLink createLocationLink(Range origin, TargetRange target) /** * Returns the location for the given target node. - * + * * @param target the target node. * @return the location for the given target node. */ @@ -929,7 +930,7 @@ public static Location createLocation(DOMRange target) { /** * Create a document link - * + * * @param target The range in the document that should be the link * @param location URI where the link should point * @param adjust true means the first and last character of @@ -949,10 +950,10 @@ public static DocumentLink createDocumentLink(DOMRange target, String location, /** * Returns the range covering the first child of the node located at offset. - * + * * Returns null if node is not a DOMElement, or if a node does not exist at * offset. - * + * * @param offset * @param document * @return range covering the first child of the node located at offset diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSchemaDiagnosticsTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSchemaDiagnosticsTest.java index 82371c212..4997a0625 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSchemaDiagnosticsTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSchemaDiagnosticsTest.java @@ -23,6 +23,7 @@ import java.util.Arrays; import org.eclipse.lemminx.XMLAssert; +import org.eclipse.lemminx.commons.BadLocationException; import org.eclipse.lemminx.extensions.contentmodel.participants.XMLSchemaErrorCode; import org.eclipse.lemminx.extensions.contentmodel.settings.ContentModelSettings; import org.eclipse.lemminx.settings.EnforceQuoteStyle; @@ -572,7 +573,7 @@ public void fuzzyElementNamesWithOtherOptionsCodeActionTest() throws Exception { } /** - * + * * @throws Exception * @see https://github.com/eclipse/lemminx/issues/856 */ @@ -769,6 +770,102 @@ public void localSchemaFileMissingCodeActionNotSupported() throws Exception { XMLAssert.testCodeActionsFor(xml, missingSchema); } + @Test + public void cvc_complex_type_2_3_singleLine() throws BadLocationException { + String xml = "\n" + // + " /bar>\n" + // + ""; + Diagnostic diagnostic = d(3, 7, 12, XMLSchemaErrorCode.cvc_complex_type_2_3); + XMLAssert.testDiagnosticsFor(xml, diagnostic); + XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 7, 3, 12, ""))); + } + + @Test + public void cvc_complex_type_2_3_multiLine() throws BadLocationException { + String xml = "\n" + // + " /bar>\n" + // + "barbarbar\n" + // + ""; + Diagnostic diagnostic = d(3, 7, 12, XMLSchemaErrorCode.cvc_complex_type_2_3); + XMLAssert.testDiagnosticsFor(xml, diagnostic); + XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 7, 3, 12, ""))); + } + + @Test + public void cvc_complex_type_2_3_singleLineSpaces() throws BadLocationException { + String xml = "\n" + // + " /bar> \n" + // + ""; + Diagnostic diagnostic = d(3, 12, 17, XMLSchemaErrorCode.cvc_complex_type_2_3); + XMLAssert.testDiagnosticsFor(xml, diagnostic); + XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 12, 3, 17, ""))); + } + + @Test + public void cvc_complex_type_2_3_singleLineCData() throws BadLocationException { + String xml = "\n" + // + " \n" + // + ""; + Diagnostic diagnostic = d(3, 18, 21, XMLSchemaErrorCode.cvc_complex_type_2_3); + XMLAssert.testDiagnosticsFor(xml, diagnostic); + XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 18, 3, 21, ""))); + } + + @Test + public void cvc_complex_type_2_3_multiLineCData() throws BadLocationException { + String xml = "\n" + // + " \n" + // + ""; + Diagnostic diagnostic = d(3, 18, 21, XMLSchemaErrorCode.cvc_complex_type_2_3); + XMLAssert.testDiagnosticsFor(xml, diagnostic); + XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 18, 3, 21, ""))); + } + + @Test + public void cvc_complex_type_2_3_blankCData() throws BadLocationException { + String xml = "\n" + // + " \n" + // + ""; + XMLAssert.testDiagnosticsFor(xml); + } + + @Test + public void cvc_complex_type_2_3_blankCDataWithTextAfter() throws BadLocationException { + String xml = "\n" + // + " TextContent \n" + // + ""; + Diagnostic diagnostic = d(3, 23, 34, XMLSchemaErrorCode.cvc_complex_type_2_3); + XMLAssert.testDiagnosticsFor(xml, diagnostic); + XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 23, 3, 34, ""))); + } + + @Test + public void cvc_complex_type_2_3_elementBeforeText() throws BadLocationException { + String xml = "\n" + // + " TextContent \n" + // + ""; + Diagnostic diagnostic = d(3, 10, 21, XMLSchemaErrorCode.cvc_complex_type_2_3); + XMLAssert.testDiagnosticsFor(xml, diagnostic); + XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 10, 3, 21, ""))); + } + private static void testDiagnosticsFor(String xml, Diagnostic... expected) { XMLAssert.testDiagnosticsFor(xml, "src/test/resources/catalogs/catalog.xml", expected); } diff --git a/org.eclipse.lemminx/src/test/resources/xsd/close-tag-type.xsd b/org.eclipse.lemminx/src/test/resources/xsd/close-tag-type.xsd new file mode 100644 index 000000000..c40602358 --- /dev/null +++ b/org.eclipse.lemminx/src/test/resources/xsd/close-tag-type.xsd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file