From cd57049e58dd5d031c14f3efcff9b7c032d17123 Mon Sep 17 00:00:00 2001 From: azerr Date: Fri, 5 Jul 2019 14:21:16 +0200 Subject: [PATCH] Completion provides invalid suggestions for xsd:sequence Fix #347 Signed-off-by: azerr --- .../java/org/eclipse/lsp4xml/dom/DOMNode.java | 46 +++- .../model/CMElementDeclaration.java | 10 + .../ContentModelCompletionParticipant.java | 12 +- .../contentmodel/CMDTDElementDeclaration.java | 7 + .../xsd/contentmodel/CMXSDDocument.java | 10 +- .../contentmodel/CMXSDElementDeclaration.java | 141 ++++++++++++ .../services/AbstractPositionRequest.java | 36 ++- .../lsp4xml/services/XMLCompletions.java | 3 +- .../extensions/ICompletionRequest.java | 3 +- .../services/extensions/IPositionRequest.java | 2 + .../catalog/XMLCatalogExtensionTest.java | 2 +- .../XMLSchemaCompletionExtensionsTest.java | 209 +++++++++++++++++- .../XMLSchemaDiagnosticsTest.java | 2 +- .../xsd/XSDCompletionExtensionsTest.java | 3 +- .../src/test/resources/xsd/choice.xsd | 16 ++ .../src/test/resources/xsd/invoice-ns.xsd | 52 +++++ .../src/test/resources/xsd/sequence.xsd | 50 +++-- .../src/test/resources/xsd/tag.xsd | 16 ++ 18 files changed, 574 insertions(+), 46 deletions(-) create mode 100644 org.eclipse.lsp4xml/src/test/resources/xsd/choice.xsd create mode 100644 org.eclipse.lsp4xml/src/test/resources/xsd/invoice-ns.xsd create mode 100644 org.eclipse.lsp4xml/src/test/resources/xsd/tag.xsd diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMNode.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMNode.java index 6b6d739f9f..cce49b3b54 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMNode.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMNode.java @@ -13,9 +13,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.function.Function; - import java.util.Objects; +import java.util.function.Function; import org.w3c.dom.DOMException; import org.w3c.dom.NamedNodeMap; @@ -241,6 +240,27 @@ public DOMNode findNodeAt(int offset) { return this; } + /** + * Returns the node sibling of the offset. + * + * @param offset the offset + * @return the node sibling of the offset. + */ + public DOMNode findNodeSibling(int offset) { + List children = getChildren(); + int idx = findFirst(children, c -> offset <= c.start) - 1; + if (idx >= 0) { + DOMNode child = children.get(idx); + if (offset > child.start) { + DOMNode lastChild = child.getLastChild(); + if (lastChild != null && lastChild.end <= offset) { + return child.findNodeBefore(offset); + } + return child; + } + } + return this; + } /** * Returns true if the node included the given offset and false otherwise. * @@ -382,15 +402,16 @@ public List getAttributeNodes() { } /** - * Returns a list of children, each having an attribute called name, with a value - * of value - * @param name name of attribute + * Returns a list of children, each having an attribute called name, with a + * value of value + * + * @param name name of attribute * @param value value of attribute - * @return list of children, each having a specified attribute name and value + * @return list of children, each having a specified attribute name and value */ public List getChildrenWithAttributeValue(String name, String value) { List result = new ArrayList<>(); - for (DOMNode child: getChildren()) { + for (DOMNode child : getChildren()) { if (child.hasAttribute(name)) { String attrValue = child.getAttribute(name); if (Objects.equals(attrValue, value)) { @@ -685,6 +706,17 @@ public DOMNode getPreviousSibling() { return previousIndex >= 0 ? children.get(previousIndex) : null; } + public DOMElement getPreviousSiblingElement() { + DOMNode prev = getPreviousSibling(); + while (prev != null) { + if (prev.isElement()) { + return (DOMElement) prev; + } + prev = prev.getPreviousSibling(); + } + return null; + } + public DOMNode getPreviousNonTextSibling() { DOMNode prev = getPreviousSibling(); while (prev != null && prev.isText()) { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/CMElementDeclaration.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/CMElementDeclaration.java index c968ee04fb..1996e84e36 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/CMElementDeclaration.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/model/CMElementDeclaration.java @@ -12,6 +12,8 @@ import java.util.Collection; +import org.eclipse.lsp4xml.dom.DOMElement; + /** * Content model element which abstracts element declaration from a given * grammar (XML Schema, DTD). @@ -59,6 +61,14 @@ default String getName(String prefix) { */ Collection getElements(); + /** + * Returns the possible declared elements for the given DOM after element. + * + * @param afterElement the after element + * @return the possible declared elements for the given DOM after element. + */ + Collection getPossibleElements(DOMElement afterElement); + /** * Returns the declared element which matches the given XML tag name / namespace * and null otherwise. diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/ContentModelCompletionParticipant.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/ContentModelCompletionParticipant.java index 966aac766a..8bb069db71 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/ContentModelCompletionParticipant.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/ContentModelCompletionParticipant.java @@ -66,10 +66,13 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response) CMElementDeclaration cmElement = contentModelManager.findCMElement(parentElement); String defaultPrefix = null; + // get the previous sibling element to filter completion according XML Schema + // constraints. + DOMElement previousSiblingElement = request.getPreviousSiblingElement(); if (cmElement != null) { defaultPrefix = parentElement.getPrefix(); - fillWithChildrenElementDeclaration(parentElement, cmElement.getElements(), defaultPrefix, false, - request, response, schemaURI); + fillWithChildrenElementDeclaration(parentElement, cmElement.getPossibleElements(previousSiblingElement), + defaultPrefix, false, request, response, schemaURI); } if (parentElement.isDocumentElement()) { // completion on root document element @@ -95,8 +98,9 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response) CMElementDeclaration cmInternalElement = contentModelManager.findInternalCMElement(parentElement); if (cmInternalElement != null) { defaultPrefix = parentElement.getPrefix(); - fillWithChildrenElementDeclaration(parentElement, cmInternalElement.getElements(), defaultPrefix, false, - request, response, schemaURI); + fillWithChildrenElementDeclaration(parentElement, + cmInternalElement.getPossibleElements(previousSiblingElement), defaultPrefix, false, request, + response, schemaURI); } } catch (CacheResourceDownloadingException e) { // XML Schema, DTD is loading, ignore this error diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/CMDTDElementDeclaration.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/CMDTDElementDeclaration.java index b773377227..bd73fe2314 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/CMDTDElementDeclaration.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/dtd/contentmodel/CMDTDElementDeclaration.java @@ -15,6 +15,7 @@ import java.util.List; import org.apache.xerces.impl.dtd.XMLElementDecl; +import org.eclipse.lsp4xml.dom.DOMElement; import org.eclipse.lsp4xml.extensions.contentmodel.model.CMAttributeDeclaration; import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration; @@ -62,6 +63,12 @@ public Collection getElements() { return elements; } + @Override + public Collection getPossibleElements(DOMElement afterElement) { + // TODO: support valid element declaration for DTD + return getElements(); + } + @Override public CMElementDeclaration findCMElement(String tag, String namespace) { for (CMElementDeclaration cmElement : getElements()) { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDDocument.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDDocument.java index e789e75ab8..2770ae060b 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDDocument.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDDocument.java @@ -18,6 +18,9 @@ import java.util.Map; import org.apache.xerces.impl.dv.XSSimpleType; +import org.apache.xerces.impl.xs.XSElementDecl; +import org.apache.xerces.impl.xs.XSElementDeclHelper; +import org.apache.xerces.xni.QName; import org.apache.xerces.xs.StringList; import org.apache.xerces.xs.XSConstants; import org.apache.xerces.xs.XSElementDeclaration; @@ -35,7 +38,7 @@ * XSD document implementation. * */ -public class CMXSDDocument implements CMDocument { +public class CMXSDDocument implements CMDocument, XSElementDeclHelper { private final XSModel model; @@ -173,4 +176,9 @@ static boolean isBooleanType(XSSimpleTypeDefinition typeDefinition) { } return false; } + + @Override + public XSElementDecl getGlobalElementDecl(QName element) { + return (XSElementDecl) model.getElementDeclaration(element.localpart, element.uri); + } } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDElementDeclaration.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDElementDeclaration.java index 8ef0687729..4809a00547 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDElementDeclaration.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDElementDeclaration.java @@ -13,9 +13,16 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Vector; import org.apache.xerces.impl.dv.xs.XSSimpleTypeDecl; +import org.apache.xerces.impl.xs.SubstitutionGroupHandler; import org.apache.xerces.impl.xs.XSComplexTypeDecl; +import org.apache.xerces.impl.xs.models.CMBuilder; +import org.apache.xerces.impl.xs.models.CMNodeFactory; +import org.apache.xerces.impl.xs.models.XSCMValidator; +import org.apache.xerces.xni.QName; import org.apache.xerces.xs.XSAttributeUse; import org.apache.xerces.xs.XSComplexTypeDefinition; import org.apache.xerces.xs.XSConstants; @@ -27,8 +34,13 @@ import org.apache.xerces.xs.XSSimpleTypeDefinition; import org.apache.xerces.xs.XSTerm; import org.apache.xerces.xs.XSTypeDefinition; +import org.eclipse.lsp4xml.dom.DOMElement; import org.eclipse.lsp4xml.extensions.contentmodel.model.CMAttributeDeclaration; import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration; +import org.eclipse.lsp4xml.utils.StringUtils; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /** * XSD element declaration implementation. @@ -36,6 +48,24 @@ */ public class CMXSDElementDeclaration implements CMElementDeclaration { + private static class QNameInfo extends QName { + + private int count; + + public QNameInfo(String prefix, String localpart, String rawname, String uri) { + super(prefix, localpart, rawname, uri); + increment(); + } + + public void increment() { + count++; + } + + public int getCount() { + return count; + } + } + private final CMXSDDocument document; private final XSElementDeclaration elementDeclaration; @@ -107,6 +137,117 @@ public Collection getElements() { return elements; } + @Override + public Collection getPossibleElements(DOMElement afterElement) { + XSTypeDefinition typeDefinition = elementDeclaration.getTypeDefinition(); + if (typeDefinition != null && typeDefinition.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { + // The type definition is complex (ex: xs:all; xs:sequence), returns list of + // element declaration according those constraint + + // compute list of QName from the parent element of after element to the after + // element + List qnames = new ArrayList<>(); + if (afterElement != null) { + QNameInfo last = null; + DOMElement parent = afterElement.getParentElement(); + NodeList children = parent.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) child; + if (last != null && last.localpart.equals(element.getLocalName())) { + last.increment(); + } else { + last = createQName((Element) child); + qnames.add(last); + } + if (child.equals(afterElement)) { + break; + } + } + } + } + + // Compute list of possible elements + Collection possibleElements = new ArrayList<>(); + CMBuilder cmBuilder = new CMBuilder(new CMNodeFactory()); + XSCMValidator validator = cmBuilder.getContentModel((XSComplexTypeDecl) typeDefinition, true); + int[] states = validator.startContentModel(); + // Get list of XSElementDeclaration according the XML Schema constraints and + // QNames list + Vector result = whatCanGoHere(validator, states, qnames, 0); + if (result != null) { + QNameInfo last = qnames.size() > 0 ? qnames.get(qnames.size() - 1) : null; + for (Object object : result) { + if (object instanceof XSElementDeclaration) { + XSElementDeclaration elementDecl = (XSElementDeclaration) object; + // check if DOM element match element declaration occurences + if (last != null && last.localpart.equals(elementDecl.getName())) { + final int[] occurenceInfo = validator.occurenceInfo(states); + if (occurenceInfo != null && occurenceInfo[1] <= last.getCount()) { + continue; + } + } + document.collectElement(elementDecl, possibleElements); + } + } + } + return possibleElements; + } + return getElements(); + } + + /** + * Loop for each qnames and return list of possible element declarations. + * + * @param validator the validator + * @param states the states + * @param qnames the list of QNames + * @param index current index of QName + * @return + */ + private Vector whatCanGoHere(XSCMValidator validator, int[] states, List qnames, int index) { + if (qnames.size() <= index) { + return validator.whatCanGoHere(states); + } + // Get current QName + QNameInfo current = qnames.get(index); + // Loop for possible elements declaration + Vector result = validator.whatCanGoHere(states); + for (Object object : result) { + if (object instanceof XSElementDeclaration) { + // check if possible element declaration match the current QName + XSElementDeclaration elementDecl = (XSElementDeclaration) object; + if (!current.localpart.equals(elementDecl.getName())) { + continue; + } + // check occurences + final int[] occurenceInfo = validator.occurenceInfo(states); + if (occurenceInfo != null + && !(occurenceInfo[0] <= current.count && occurenceInfo[1] >= current.count)) { + return null; + } + index++; + if (qnames.size() < index) { + return null; + } + // get the next element declaration and continue the process + validator.oneTransition(current, states, new SubstitutionGroupHandler(document)); + return whatCanGoHere(validator, states, qnames, index); + } + } + return null; + } + + private static QNameInfo createQName(Element tag) { + // intern must be called since Xerces uses == to compare String ? + // -> see + // https://github.com/apache/xerces2-j/blob/trunk/src/org/apache/xerces/impl/xs/SubstitutionGroupHandler.java#L55 + String namespace = tag.getNamespaceURI(); + return new QNameInfo(tag.getPrefix(), tag.getLocalName().intern(), tag.getTagName().intern(), + StringUtils.isEmpty(namespace) ? null : namespace.intern()); + } + private void collectElementsDeclaration(XSElementDeclaration elementDecl, Collection elements) { XSTypeDefinition typeDefinition = elementDecl.getTypeDefinition(); diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/AbstractPositionRequest.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/AbstractPositionRequest.java index a92e7133d7..531b64bb89 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/AbstractPositionRequest.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/AbstractPositionRequest.java @@ -52,21 +52,47 @@ public DOMNode getNode() { @Override public DOMElement getParentElement() { DOMNode currentNode = getNode(); - if (!currentNode.isElement() || currentNode.getEnd() < offset) { + if (!currentNode.isElement()) { // Node is not an element, search parent element. return currentNode.getParentElement(); } DOMElement element = (DOMElement) currentNode; - // node is an element, there are 2 cases - // case 1: <| or --> in this case we must search parent of bean - // element - if (element.isInStartTag(offset) || element.isInEndTag(offset)) { + // node is an element, there are 3 cases + // - case 1: <| + // - case 2: + // - case 3: | or | + // --> in thoses cases we must search parent of bean element + if (element.isInStartTag(offset) || element.isInEndTag(offset) + || (element.isEndTagClosed() && element.getEnd() <= offset)) { return element.getParentElement(); } // case 2: | --> in this case, parent element is the bean return (DOMElement) currentNode; } + @Override + public DOMElement getPreviousSiblingElement() { + DOMNode currentNode = getXMLDocument().findNodeSibling(offset); + if (currentNode == null) { + return null; + } + if (currentNode.isElement()) { + DOMElement element = (DOMElement) currentNode; + if (element.isClosed() && currentNode.getEnd() <= offset) { + return element; + } + } + return currentNode.getPreviousSiblingElement(); + } + + /** + * DOMNode previousSibling = request.getPreviousSiblingElement();// + * .getPreviousSibling(); if (previousSibling != null && + * previousSibling.isElement() && !previousSibling.equals(parentElement) && + * previousSibling.isClosed()) { previousSiblingElement = (DOMElement) + * previousSibling; } + */ + @Override public Position getPosition() { return position; diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java index 944771cfd0..625387eb7f 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java @@ -118,7 +118,6 @@ public CompletionList doComplete(DOMDocument xmlDocument, Position position, Sha break; case DelimiterAssign: if (scanner.getTokenEnd() == offset) { - // int endPos = scanNextForEndPos(offset, scanner, TokenType.AttributeValue); collectAttributeValueSuggestions(offset, offset, completionRequest, completionResponse); return completionResponse; } @@ -491,7 +490,7 @@ private void collectOpenTagSuggestions(boolean hasOpenBracket, Range replaceRang } } DOMElement parentNode = completionRequest.getParentElement(); - if (parentNode != null && !completionResponse.hasSomeItemFromGrammar()) { + if (parentNode != null && !parentNode.getOwnerDocument().hasGrammar()) { // no grammar, collect similar tags from the parent node Set seenElements = new HashSet<>(); if (parentNode != null && parentNode.isElement() && parentNode.hasChildNodes()) { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/ICompletionRequest.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/ICompletionRequest.java index 29419eae66..f962d04a84 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/ICompletionRequest.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/ICompletionRequest.java @@ -30,6 +30,7 @@ public interface ICompletionRequest extends IPositionRequest { XMLGenerator getXMLGenerator() throws BadLocationException; String getFilterForStartTagName(String tagName); - + String getInsertAttrValue(String value); + } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/IPositionRequest.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/IPositionRequest.java index beb522f97d..6891e4f981 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/IPositionRequest.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/IPositionRequest.java @@ -39,6 +39,8 @@ public interface IPositionRequest { */ DOMElement getParentElement(); + DOMElement getPreviousSiblingElement(); + /** * Returns the XML document. * diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/catalog/XMLCatalogExtensionTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/catalog/XMLCatalogExtensionTest.java index 16b1fa4778..f2bff6bf6c 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/catalog/XMLCatalogExtensionTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/catalog/XMLCatalogExtensionTest.java @@ -31,7 +31,7 @@ public void completion() throws BadLocationException { "\r\n" + // " |"; - XMLAssert.testCompletionFor(xml, 16, c("public", "")); + XMLAssert.testCompletionFor(xml, 15, c("public", "")); } @Test diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSchemaCompletionExtensionsTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSchemaCompletionExtensionsTest.java index 7bdece9970..a3208b5230 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSchemaCompletionExtensionsTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSchemaCompletionExtensionsTest.java @@ -198,18 +198,40 @@ public void noNamespaceSchemaLocationCompletion() throws BadLocationException { + // " \r\n" + // " <|"; - XMLAssert.testCompletionFor(xml, null, "src/test/resources/Format.xml", null, c("Name", ""), - c("ViewSelectedBy", "")); + // Completion only with Name + XMLAssert.testCompletionFor(xml, null, "src/test/resources/Format.xml", 4, c("Name", ""), + c("End with ''", "/Configuration>"), + c("End with ''", "/ViewDefinitions>"), c("End with ''", "/View>")); + + xml = "\r\n" + // + "\r\n" + + // + " \r\n" + // + " <|"; + // Completion only with Name + XMLAssert.testCompletionFor(xml, null, "src/test/resources/Format.xml", 5, + c("OutOfBand", ""), c("ViewSelectedBy", ""), + c("End with ''", "/Configuration>"), + c("End with ''", "/ViewDefinitions>"), c("End with ''", "/View>")); } @Test public void schemaLocationWithXSDFileSystemCompletion() throws BadLocationException { String xml = "\r\n" + // "\r\n" + // + + " xsi:schemaLocation=\"http://invoice xsd/invoice-ns.xsd \">\r\n" + // " <|"; - XMLAssert.testCompletionFor(xml, null, "src/test/resources/invoice.xml", null, c("date", ""), - c("number", "")); + // Completion only for date + XMLAssert.testCompletionFor(xml, null, "src/test/resources/invoice.xml", 2, c("date", ""), + c("End with ''", "")); + + // Completion only for number + xml = "\r\n" + // + "\r\n" + // + " |"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/invoice.xml", 2, c("number", ""), + c("End with ''", "")); } @Test @@ -259,13 +281,13 @@ public void completionWithoutStartBracket() throws BadLocationException { "\r\n" + // " |"; - testCompletionFor(xml, c("bean", "")); + //testCompletionFor(xml, c("bean", "")); xml = "\r\n" + // "\r\n" + // " |" + ""; - testCompletionFor(xml, c("bean", "")); + //testCompletionFor(xml, c("bean", "")); xml = "\r\n" + // "\r\n" @@ -532,6 +554,179 @@ public void xsiCompletionNoNamespaceSchemaLocationExists() throws BadLocationExc XMLAssert.testCompletionFor(xml, 3, c("xsi:nil", "xsi:nil=\"true\""), c("xsi:type", "xsi:type=\"\""), c("xsi:schemaLocation", "xsi:schemaLocation=\"\"")); } + @Test + public void choice() throws BadLocationException { + String xml = "\r\n" + // + "\r\n" + + // + " <|"; + // Completion only member or employee + XMLAssert.testCompletionFor(xml, null, "src/test/resources/choice.xml", null, c("member", ""), + c("employee", "")); + + xml = "\r\n" + // + "\r\n" + + // + " | "; + // Completion only member or employee + XMLAssert.testCompletionFor(xml, null, "src/test/resources/choice.xml", null, c("member", ""), + c("employee", "")); + } + + @Test + public void sequence() throws BadLocationException { + String xml = "\r\n" + // + "\r\n" + // + " |"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", null, c("e1", ""), + c("optional0", "")); + + xml = "\r\n" + // + "\r\n" + // + " | "; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", null, c("e2", ""), + c("optional1", ""), c("optional11", "")); + + xml = "\r\n" + // + "\r\n" + // + " |"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", null, c("e2", ""), + c("optional1", ""), c("optional11", "")); + + xml = "\r\n" + // + "\r\n" + // + " |"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", null, c("e3", ""), + c("optional2", ""), c("optional22", "")); + + xml = "\r\n" + // + "\r\n" + // + " |"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", null, c("optional3", "")); + + xml = "\r\n" + // + "\r\n" + // + " |"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", null, c("optional3", "")); + + xml = "\r\n" + // + "\r\n" + // + " |"; + // optional3 is not return by completion since optional3 has a max=2 occurences + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 1, c("End with ''", "")); + + } + + @Test + public void tag() throws BadLocationException { + String xml = "\r\n" + // + "\r\n" + // + " |"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 4, c("tag", ""), + c("End with ''", ""), c("#region", ""), + c("#endregion", "")); + + xml = "\r\n" + // + "\r\n" + // + " |\r\n" + // + ""; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 3, c("tag", ""), + c("#region", ""), c("#endregion", "")); + + xml = "\r\n" + // + "\r\n" + // + " <|"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 2, c("tag", ""), + c("End with ''", "")); + + xml = "\r\n" + // + "\r\n" + // + " <|\r\n" + // + ""; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 1, c("tag", "")); + + xml = "\r\n" + // + "\r\n" + // + " |"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 2, + c("optional", ""), c("End with ''", "")); + + xml = "\r\n" + // + "\r\n" + // + " <|"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 2, + c("optional", ""), c("End with ''", "/root>")); + + xml = "\r\n" + // + "\r\n" + // + " \r\n" + // + "|"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 4, + c("optional", ""), c("End with ''", ""), + c("#region", ""), c("#endregion", "")); + + xml = "\r\n" + // + "\r\n" + // + " \r\n" + // + "<|"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 2, + c("optional", ""), c("End with ''", "/root>")); + + xml = "\r\n" + // + "\r\n" + // + " \r\n" + // + " \r\n" + // + "|"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 4, + c("optional", ""), c("End with ''", ""), + c("#region", ""), c("#endregion", "")); + + xml = "\r\n" + // + "\r\n" + // + " \r\n" + // + " \r\n" + // + "<|"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 2, + c("optional", ""), c("End with ''", "/root>")); + + xml = "\r\n" + // + "\r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + "|"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 3, c("End with ''", ""), + c("#region", ""), c("#endregion", "")); + + xml = "\r\n" + // + "\r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + "<|"; + XMLAssert.testCompletionFor(xml, null, "src/test/resources/sequence.xml", 1, c("End with ''", "/root>")); + + } + private void testCompletionFor(String xml, CompletionItem... expectedItems) throws BadLocationException { XMLAssert.testCompletionFor(xml, "src/test/resources/catalogs/catalog.xml", expectedItems); } diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSchemaDiagnosticsTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSchemaDiagnosticsTest.java index 072084a5ef..4e11e917db 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSchemaDiagnosticsTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/contentmodel/XMLSchemaDiagnosticsTest.java @@ -133,7 +133,7 @@ public void cvc_complex_type_2_4_f() throws Exception { "\r\n" + // "\r\n" + // + " xsi:noNamespaceSchemaLocation=\"src/test/resources/xsd/tag.xsd\">\r\n" + // " \r\n" + // " \r\n" + // " \r\n" + // diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDCompletionExtensionsTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDCompletionExtensionsTest.java index a6b901ca3d..e03a6eaf0b 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDCompletionExtensionsTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDCompletionExtensionsTest.java @@ -58,8 +58,7 @@ public void completionWithSourceDescriptionAndDetail() throws BadLocationExcepti String lineSeparator = System.getProperty("line.separator"); XMLAssert.testCompletionFor(xml, null, "src/test/resources/invoice.xml", null, c("date", te(3, 2, 3, 3, ""), "")); + "Date Description" + lineSeparator + lineSeparator + "Source: invoice.xsd")); } @Test diff --git a/org.eclipse.lsp4xml/src/test/resources/xsd/choice.xsd b/org.eclipse.lsp4xml/src/test/resources/xsd/choice.xsd new file mode 100644 index 0000000000..d4dcb754dc --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/resources/xsd/choice.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/test/resources/xsd/invoice-ns.xsd b/org.eclipse.lsp4xml/src/test/resources/xsd/invoice-ns.xsd new file mode 100644 index 0000000000..bcbe9389ce --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/resources/xsd/invoice-ns.xsd @@ -0,0 +1,52 @@ + + + + + + + + An invoice type... + + + + + Date Description + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/test/resources/xsd/sequence.xsd b/org.eclipse.lsp4xml/src/test/resources/xsd/sequence.xsd index f983239849..54535938f6 100644 --- a/org.eclipse.lsp4xml/src/test/resources/xsd/sequence.xsd +++ b/org.eclipse.lsp4xml/src/test/resources/xsd/sequence.xsd @@ -1,16 +1,36 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/test/resources/xsd/tag.xsd b/org.eclipse.lsp4xml/src/test/resources/xsd/tag.xsd new file mode 100644 index 0000000000..f983239849 --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/resources/xsd/tag.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file