Skip to content

Commit

Permalink
Fix error range for cvc-complex-type-2.3
Browse files Browse the repository at this point in the history
Now handles text nodes that are only take up one line,
as well as <![CDATA[]]>.

Closes eclipse-lemminx#885

Signed-off-by: David Thompson <[email protected]>
  • Loading branch information
datho7561 committed Oct 15, 2020
1 parent 7fe05f1 commit 3b2dd72
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMCDATASection;
import org.eclipse.lemminx.dom.DOMCharacterData;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMElement;
Expand Down Expand Up @@ -81,7 +82,7 @@ private XMLPositionUtility() {

/**
* Returns the attribute name range and null otherwise.
*
*
* @param attr the attribute.
* @return the attribute name range and null otherwise.
*/
Expand All @@ -100,7 +101,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.
*/
Expand Down Expand Up @@ -171,7 +172,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
*/
Expand Down Expand Up @@ -309,7 +310,7 @@ private static DOMNode findUnclosedChildNode(String childTag, List<DOMNode> chil
/**
* Returns the range of the root start tag (excludes the '<') of the given
* <code>document</code> and null otherwise.
*
*
* @param document the DOM document.
* @return the range of the root start tag (excludes the '<') of the given
* <code>document</code> and null otherwise.
Expand All @@ -328,9 +329,9 @@ public static Range selectRootStartTag(DOMDocument document) {
/**
* Finds the root element of the given document and returns the attribute value
* <code>Range</code> for the attribute <code>attrName</code>.
*
*
* If <code>attrName</code> 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 <code>document</code> where the declared value of
Expand Down Expand Up @@ -363,7 +364,7 @@ public static Range selectStartTagName(int offset, DOMDocument document) {
/**
* Returns the range of the start tag name (excludes the '<') of the given
* <code>element</code> and null otherwise.
*
*
* @param element the DOM element
* @return the range of the start tag of the given <code>element</code> and null
* otherwise.
Expand All @@ -375,7 +376,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
*/
Expand All @@ -386,10 +387,10 @@ public static Range selectStartTagLocalName(DOMNode element) {
/**
* Returns the range of the start tag name (excludes the '<') of the given
* <code>element</code> 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 <code>element</code> and null
Expand Down Expand Up @@ -450,7 +451,7 @@ public static Range selectEndTagName(int offset, DOMDocument document) {
/**
* Returns the range of the end tag of the given <code>element</code> name and
* null otherwise.
*
*
* @param element the DOM element
* @return the range of the end tag of the given <code>element</code> and null
* otherwise.
Expand All @@ -462,7 +463,7 @@ public static Range selectEndTagName(DOMElement element) {
/**
* Returns the range of the end tag of the given LOCAL <code>element</code> name
* and null otherwise.
*
*
* @param element the DOM element
* @return the range of the end tag of the given <code>element</code> and null
* otherwise.
Expand All @@ -474,7 +475,7 @@ public static Range selectEndTagLocalName(DOMElement element) {
/**
* Returns the range of the end tag of the given <code>element</code> and null
* otherwise.
*
*
* @param element the DOM element
* @return the range of the end tag of the given <code>element</code> and null
* otherwise.
Expand Down Expand Up @@ -502,7 +503,7 @@ public static Range selectEndTagName(DOMElement element, boolean localNameOnly)
/**
* Returns the range of the entity reference in a text node (ex : &amp;) and
* null otherwise.
*
*
* @param offset the offset
* @param document the document
* @return the range of the entity reference in a text node (ex : &amp;) and
Expand All @@ -515,7 +516,7 @@ public static EntityReferenceRange selectEntityReference(int offset, DOMDocument
/**
* Returns the range of the entity reference in a text node (ex : &amp;) and
* null otherwise.
*
*
* @param offset the offset
* @param document the document
* @param endsWithSemicolon true if the entity reference must end with ';' and
Expand Down Expand Up @@ -547,7 +548,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
Expand Down Expand Up @@ -582,7 +583,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
Expand All @@ -605,7 +606,12 @@ 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()) {
if (node.isCDATA()) {
DOMCDATASection cdataSection = (DOMCDATASection) node;
if (!StringUtils.isBlank(cdataSection.getTextContent())) {
return createRange(cdataSection.getStartContent(), cdataSection.getEndContent(), document);
}
} else if (node.isCharacterData()) {
String content = ((DOMCharacterData) node).getData();
int start = node.getStart();
Integer end = null;
Expand Down Expand Up @@ -637,7 +643,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.
*/
Expand Down Expand Up @@ -670,17 +676,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:
*
*
* <a> hello
*
*
* </a>
*
*
* the returned range will cover only "hello".
*
*
* @param offset
* @param document
* @return range covering the trimmed text belonging to the node located at
Expand Down Expand Up @@ -731,7 +737,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: "<!ELEMENT elementName (content) UNRECOGNIZED_CONTENT" will give the
* range of 1 character length, after '(content)'
*/
Expand Down Expand Up @@ -768,7 +774,7 @@ public static Range getLastValidDTDDeclParameter(int offset, DOMDocument documen
/**
* Will give the range for the last VALID DTD Decl parameter at 'offset'. An
* unrecognized Parameter is not considered VALID,
*
*
* eg: <!ELEMENT elementName (content) UNRECOGNIZED_CONTENT will give the range
* 1 character after '(content)'
*/
Expand Down Expand Up @@ -799,7 +805,7 @@ public static Range getLastValidDTDDeclParameterOrUnrecognized(int offset, DOMDo
/**
* Will give the range for the last DTD Decl parameter at 'offset'. An
* unrecognized Parameter is considered as well.
*
*
* eg: <!ELEMENT elementName (content) UNRECOGNIZED_CONTENT will give the range
* 1 character after '(content)'
*/
Expand Down Expand Up @@ -848,7 +854,7 @@ public static Range getElementDeclMissingContentOrCategory(int offset, DOMDocume

/**
* Returns the range for the given <code>node</code>.
*
*
* @param node the node
* @return the range for the given <code>node</code>.
*/
Expand All @@ -868,7 +874,7 @@ public static Range createRange(int startOffset, int endOffset, DOMDocument docu
/**
* Returns the location link for the given <code>origin</code> and
* <code>target</code> nodes.
*
*
* @param origin the origin node.
* @param target the target node.
* @return the location link for the given <code>origin</code> and
Expand All @@ -887,7 +893,7 @@ public static LocationLink createLocationLink(DOMRange origin, DOMRange target)
/**
* Returns the location link for the given <code>origin</code> and
* <code>target</code> nodes.
*
*
* @param origin the origin node.
* @param target the target node.
* @return the location link for the given <code>origin</code> and
Expand All @@ -903,7 +909,7 @@ public static LocationLink createLocationLink(Range origin, DOMRange target) {
/**
* Returns the location link for the given <code>origin</code> and
* <code>target</code> nodes.
*
*
* @param origin the origin node.
* @param target the target node.
* @return the location link for the given <code>origin</code> and
Expand All @@ -917,7 +923,7 @@ public static LocationLink createLocationLink(Range origin, TargetRange target)

/**
* Returns the location for the given <code>target</code> node.
*
*
* @param target the target node.
* @return the location for the given <code>target</code> node.
*/
Expand All @@ -929,7 +935,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 <code>true</code> means the first and last character of
Expand All @@ -949,10 +955,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -572,7 +573,7 @@ public void fuzzyElementNamesWithOtherOptionsCodeActionTest() throws Exception {
}

/**
*
*
* @throws Exception
* @see https://github.com/eclipse/lemminx/issues/856
*/
Expand Down Expand Up @@ -769,6 +770,69 @@ public void localSchemaFileMissingCodeActionNotSupported() throws Exception {
XMLAssert.testCodeActionsFor(xml, missingSchema);
}

@Test
public void cvc_complex_type_2_3_singleLine() throws BadLocationException {
String xml = "<foo\n" + //
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + //
" xsi:noNamespaceSchemaLocation=\"src/test/resources/xsd/close-tag-type.xsd\">\n" + //
" <bar>/bar></bar>\n" + //
"</foo>";
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 = "<foo\n" + //
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + //
" xsi:noNamespaceSchemaLocation=\"src/test/resources/xsd/close-tag-type.xsd\">\n" + //
" <bar>/bar>\n" + //
"barbarbar</bar>\n" + //
"</foo>";
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 = "<foo\n" + //
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + //
" xsi:noNamespaceSchemaLocation=\"src/test/resources/xsd/close-tag-type.xsd\">\n" + //
" <bar> /bar> </bar>\n" + //
"</foo>";
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 = "<foo\n" + //
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + //
" xsi:noNamespaceSchemaLocation=\"src/test/resources/xsd/close-tag-type.xsd\">\n" + //
" <bar> <![CDATA[ bar ]]> </bar>\n" + //
"</foo>";
Diagnostic diagnostic = d(3, 8, 25, XMLSchemaErrorCode.cvc_complex_type_2_3);
XMLAssert.testDiagnosticsFor(xml, diagnostic);
XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 8, 3, 25, "")));
}

@Test
public void cvc_complex_type_2_3_multiLineCData() throws BadLocationException {
String xml = "<foo\n" + //
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + //
" xsi:noNamespaceSchemaLocation=\"src/test/resources/xsd/close-tag-type.xsd\">\n" + //
" <bar> <![CDATA[ bar\n" + //
" hi ]]> </bar>\n" + //
"</foo>";
Diagnostic diagnostic = d(3, 8, 4, 9, XMLSchemaErrorCode.cvc_complex_type_2_3);
XMLAssert.testDiagnosticsFor(xml, diagnostic);
XMLAssert.testCodeActionsFor(xml, diagnostic, ca(diagnostic, te(3, 8, 4, 9, "")));
}


private static void testDiagnosticsFor(String xml, Diagnostic... expected) {
XMLAssert.testDiagnosticsFor(xml, "src/test/resources/catalogs/catalog.xml", expected);
}
Expand Down
19 changes: 19 additions & 0 deletions org.eclipse.lemminx/src/test/resources/xsd/close-tag-type.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="foo">
<xs:complexType>
<xs:sequence>
<xs:element ref="bar" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="foo" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="bar">
<xs:complexType>
<xs:sequence>
<xs:element ref="bar" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="foo" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

0 comments on commit 3b2dd72

Please sign in to comment.