diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLTextDocumentService.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLTextDocumentService.java index 385b8b3d6..4759b2ea5 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLTextDocumentService.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLTextDocumentService.java @@ -285,7 +285,8 @@ public CompletableFuture, List { if (definitionLinkSupport) { - return Either.forRight(getXMLLanguageService().findDefinition(xmlDocument, params.getPosition(), cancelChecker)); + return Either.forRight( + getXMLLanguageService().findDefinition(xmlDocument, params.getPosition(), cancelChecker)); } List locations = getXMLLanguageService() .findDefinition(xmlDocument, params.getPosition(), cancelChecker) // @@ -299,7 +300,8 @@ public CompletableFuture, List> references(ReferenceParams params) { return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> { - return getXMLLanguageService().findReferences(xmlDocument, params.getPosition(), params.getContext()); + return getXMLLanguageService().findReferences(xmlDocument, params.getPosition(), params.getContext(), + cancelChecker); }); } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMDocument.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMDocument.java index 8f45b456e..909bb5028 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMDocument.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMDocument.java @@ -464,11 +464,6 @@ public DOMDocumentType getDoctype() { return null; } - /* - * (non-Javadoc) - * - * @see org.eclipse.lsp4xml.dom.Node#getOwnerDocument() - */ @Override public DOMDocument getOwnerDocument() { return this; 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 3524f53dc..4033e22b7 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 @@ -147,6 +147,7 @@ public DOMNode(int start, int end) { * * @return the owner document and null otherwise. */ + @Override public DOMDocument getOwnerDocument() { Node node = parent; while (node != null) { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/DataType.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/DataType.java index a54e18fa5..4873b1b92 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/DataType.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/DataType.java @@ -80,10 +80,10 @@ public static String getDocumentation(DOMAttr attr) { doc.append(attr.getValue()); doc.append("**"); DOMElement element = attr.getOwnerElement(); - if (XSDUtils.isComplexType(element)) { + if (XSDUtils.isXSComplexType(element)) { doc.append(lineSeparator); doc.append(" - Type: `Complex Type` "); - } else if (XSDUtils.isSimpleType(element)) { + } else if (XSDUtils.isXSSimpleType(element)) { doc.append(lineSeparator); doc.append(" - Type: `Simple Type` "); } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/XSDPlugin.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/XSDPlugin.java index 5bd26230e..c15251b8f 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/XSDPlugin.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/XSDPlugin.java @@ -17,9 +17,11 @@ import org.eclipse.lsp4xml.extensions.xsd.contentmodel.CMXSDContentModelProvider; import org.eclipse.lsp4xml.extensions.xsd.participants.XSDCompletionParticipant; import org.eclipse.lsp4xml.extensions.xsd.participants.XSDDefinitionParticipant; +import org.eclipse.lsp4xml.extensions.xsd.participants.XSDReferenceParticipant; import org.eclipse.lsp4xml.extensions.xsd.participants.diagnostics.XSDDiagnosticsParticipant; import org.eclipse.lsp4xml.services.extensions.ICompletionParticipant; import org.eclipse.lsp4xml.services.extensions.IDefinitionParticipant; +import org.eclipse.lsp4xml.services.extensions.IReferenceParticipant; import org.eclipse.lsp4xml.services.extensions.IXMLExtension; import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry; import org.eclipse.lsp4xml.services.extensions.diagnostics.IDiagnosticsParticipant; @@ -37,12 +39,15 @@ public class XSDPlugin implements IXMLExtension { private final IDiagnosticsParticipant diagnosticsParticipant; + private final IReferenceParticipant referenceParticipant; + private XSDURIResolverExtension uiResolver; public XSDPlugin() { completionParticipant = new XSDCompletionParticipant(); definitionParticipant = new XSDDefinitionParticipant(); diagnosticsParticipant = new XSDDiagnosticsParticipant(); + referenceParticipant = new XSDReferenceParticipant(); } @Override @@ -70,6 +75,7 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) { registry.registerCompletionParticipant(completionParticipant); registry.registerDefinitionParticipant(definitionParticipant); registry.registerDiagnosticsParticipant(diagnosticsParticipant); + registry.registerReferenceParticipant(referenceParticipant); } @Override @@ -78,5 +84,6 @@ public void stop(XMLExtensionsRegistry registry) { registry.unregisterCompletionParticipant(completionParticipant); registry.unregisterDefinitionParticipant(definitionParticipant); registry.unregisterDiagnosticsParticipant(diagnosticsParticipant); + registry.unregisterReferenceParticipant(referenceParticipant); } } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/participants/XSDReferenceParticipant.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/participants/XSDReferenceParticipant.java new file mode 100644 index 000000000..a67434bd2 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/participants/XSDReferenceParticipant.java @@ -0,0 +1,50 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4xml.extensions.xsd.participants; + +import java.util.List; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.ReferenceContext; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4xml.dom.DOMAttr; +import org.eclipse.lsp4xml.dom.DOMDocument; +import org.eclipse.lsp4xml.dom.DOMNode; +import org.eclipse.lsp4xml.extensions.xsd.utils.XSDUtils; +import org.eclipse.lsp4xml.services.extensions.AbstractReferenceParticipant; +import org.eclipse.lsp4xml.utils.DOMUtils; +import org.eclipse.lsp4xml.utils.XMLPositionUtility; + +/** + * XSD reference + * + * @author Angelo ZERR + * + */ +public class XSDReferenceParticipant extends AbstractReferenceParticipant { + + @Override + protected boolean match(DOMDocument document) { + return DOMUtils.isXSD(document); + } + + @Override + protected void findReferences(DOMNode node, Position position, int offset, ReferenceContext context, + List locations, CancelChecker cancelChecker) { + DOMAttr attr = node.findAttrAt(offset); + if (attr != null) { + node = attr; + } + XSDUtils.collectXSReferenceTypes(node, + (from, to) -> locations.add(XMLPositionUtility.createLocation(from.getNodeAttrValue())), cancelChecker); + } + +} diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/utils/XSDUtils.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/utils/XSDUtils.java index 7979cf79d..8be041986 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/utils/XSDUtils.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/utils/XSDUtils.java @@ -9,13 +9,19 @@ *******************************************************************************/ package org.eclipse.lsp4xml.extensions.xsd.utils; +import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.eclipse.lsp4xml.dom.DOMAttr; import org.eclipse.lsp4xml.dom.DOMDocument; import org.eclipse.lsp4xml.dom.DOMElement; +import org.eclipse.lsp4xml.dom.DOMNode; import org.eclipse.lsp4xml.utils.StringUtils; +import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -68,11 +74,11 @@ public static BindingType getBindingType(DOMAttr attr) { DOMElement element = attr.getOwnerElement(); DOMElement parent = element.getParentElement(); if (parent != null) { - if (parent.getLocalName().equals("complexContent") | isComplexType(parent)) { + if (parent.getLocalName().equals("complexContent") || isXSComplexType(parent)) { // parent element is complexContent or complexType -> bounded type is complex return BindingType.COMPLEX; } - if (parent.getLocalName().equals("simpleContent") || isSimpleType(parent)) { + if (parent.getLocalName().equals("simpleContent") || isXSSimpleType(parent)) { // parent element is simpleContent or simpleType -> bounded type is simple return BindingType.SIMPLE; } @@ -123,8 +129,8 @@ public static void collectXSTypes(DOMAttr originAttr, BindingType bindingType, b if (documentElement == null) { return; } - String attrValue = originAttr.getValue(); - if (matchAttr && StringUtils.isEmpty(attrValue)) { + String originAttrValue = originAttr.getValue(); + if (matchAttr && StringUtils.isEmpty(originAttrValue)) { return; } @@ -135,18 +141,9 @@ public static void collectXSTypes(DOMAttr originAttr, BindingType bindingType, b // http://camel.apache.org/schema/spring String targetNamespacePrefix = documentElement.getPrefix(targetNamespace); // -> tns - String matchAttrName = null; + String originName = null; if (matchAttr) { - matchAttrName = attrValue; - String prefix = null; - int index = attrValue.indexOf(":"); - if (index != -1) { - prefix = attrValue.substring(0, index); - if (!Objects.equal(prefix, targetNamespacePrefix)) { - return; - } - matchAttrName = attrValue.substring(index + 1, attrValue.length()); - } + originName = getOriginName(originAttrValue, targetNamespacePrefix); } // Loop for element complexType. @@ -155,11 +152,11 @@ public static void collectXSTypes(DOMAttr originAttr, BindingType bindingType, b Node node = children.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { Element targetElement = (Element) node; - if (canCollectElement(originAttr, targetElement, bindingType)) { + if (isBounded(originAttr.getOwnerElement(), bindingType, targetElement)) { // node is a xs:complexType, xs:simpleType element, xsl:element, xs:group which // matches the binding type of the originAttr DOMAttr targetAttr = (DOMAttr) targetElement.getAttributeNode("name"); - if (targetAttr != null && (!matchAttr || matchAttrName.equals(targetAttr.getValue()))) { + if (targetAttr != null && (!matchAttr || originName.equals(targetAttr.getValue()))) { collector.accept(targetNamespacePrefix, targetAttr); } } @@ -167,27 +164,177 @@ public static void collectXSTypes(DOMAttr originAttr, BindingType bindingType, b } } - private static boolean canCollectElement(DOMAttr originAttr, Element targetElement, BindingType bindingType) { - if (isComplexType(targetElement)) { - return bindingType.isComplex(); - } else if (isSimpleType(targetElement)) { - return bindingType.isSimple(); - } else if (bindingType == BindingType.REF) { + private static String getOriginName(String originAttrValue, String targetNamespacePrefix) { + int index = originAttrValue.indexOf(":"); + if (index != -1) { + String prefix = originAttrValue.substring(0, index); + if (!Objects.equal(prefix, targetNamespacePrefix)) { + return null; + } + return originAttrValue.substring(index + 1, originAttrValue.length()); + } + return originAttrValue; + } + + private static boolean isBounded(Element originElement, BindingType originBinding, Element targetElement) { + if (isXSComplexType(targetElement)) { + return originBinding.isComplex(); + } else if (isXSSimpleType(targetElement)) { + return originBinding.isSimple(); + } else if (originBinding == BindingType.REF) { // - xs:element/@name attributes if originAttr is xs:element/@ref // - xs:group/@name attributes if originAttr is xs:group/@ref - return (originAttr.getOwnerElement().getLocalName().equals(targetElement.getLocalName())); - } else if (bindingType == BindingType.ELEMENT) { - return "element".equals(targetElement.getLocalName()); + return (originElement.getLocalName().equals(targetElement.getLocalName())); + } else if (originBinding == BindingType.ELEMENT) { + return isXSElement(targetElement); } return false; } - public static boolean isComplexType(Element element) { + /** + * Collect references types from the given referenced node. + * + * @param targetNode the referenced node + * @param collector the collector to collect reference origin and target node. + */ + public static void collectXSReferenceTypes(DOMNode targetNode, BiConsumer collector, + CancelChecker cancelChecker) { + // get referenced attribute nodes from the given referenced node + List targetAttrs = getTargetAttrs(targetNode); + if (targetAttrs.isEmpty()) { + // None referenced nodes, stop the search of references + return; + } + + // Here referencedNodes is filled with a list of attributes + // xs:complexType/@name, + // xs:simpleType/@name, xs:element/@name, xs:group/@name + + DOMDocument document = targetNode.getOwnerDocument(); + DOMElement documentElement = document.getDocumentElement(); + + // + String targetNamespace = documentElement.getAttribute("targetNamespace"); // -> + // http://camel.apache.org/schema/spring + String targetNamespacePrefix = documentElement.getPrefix(targetNamespace); // -> tns + + // Collect references for each references nodes + + NodeList nodes = documentElement.getChildNodes(); + collectXSReferenceTypes(nodes, targetAttrs, targetNamespacePrefix, collector, cancelChecker); + } + + /** + * Returns the referenced attributes list from the given referenced node. + * + * @param referencedNode the referenced node. + * @return the referenced attributes list from the given referenced node. + */ + private static List getTargetAttrs(DOMNode referencedNode) { + List referencedNodes = new ArrayList<>(); + Document document = referencedNode.getOwnerDocument(); + switch (referencedNode.getNodeType()) { + case Node.ATTRIBUTE_NODE: + // The referenced node is an attribute, add it to search references from it. + case Node.ELEMENT_NODE: + // The referenced node is an element, get the attribute name) and add it to + // search references from it. + addReferenceNode(referencedNode, referencedNodes); + break; + case Node.DOCUMENT_NODE: + // The referenced node is the DOM document, collect all attributes + // xs:complexType/@name, xs:simpleType/@name, xs:element/@name, xs:group/@name + // which can be referenced + NodeList nodes = document.getDocumentElement().getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node n = nodes.item(i); + if (n.getNodeType() == Node.ELEMENT_NODE) { + DOMElement element = (DOMElement) n; + if (isXSComplexType(element) || isXSSimpleType(element) || isXSElement(element) + || isXSGroup(element)) { + addReferenceNode(element, referencedNodes); + } + } + } + } + return referencedNodes; + } + + /** + * Add the given node as reference node if it is applicable. + * + * @param node the node to add. + * @param targetAttrs the list of referenced nodes. + */ + private static void addReferenceNode(DOMNode node, List targetAttrs) { + DOMAttr attr = null; + switch (node.getNodeType()) { + case Node.ATTRIBUTE_NODE: + attr = (DOMAttr) node; + break; + case Node.ELEMENT_NODE: + attr = ((DOMElement) node).getAttributeNode("name"); + break; + } + // Attribute must exists and her value must be not empty. + if (attr != null && !StringUtils.isEmpty(attr.getValue())) { + targetAttrs.add(attr); + } + } + + private static void collectXSReferenceTypes(NodeList nodes, List targetAttrs, String targetNamespacePrefix, + BiConsumer collector, CancelChecker cancelChecker) { + for (int i = 0; i < nodes.getLength(); i++) { + if (cancelChecker != null) { + cancelChecker.checkCanceled(); + } + Node node = nodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + DOMElement originElement = (DOMElement) node; + NamedNodeMap originAttributes = originElement.getAttributes(); + if (originAttributes != null) { + for (int j = 0; j < originAttributes.getLength(); j++) { + DOMAttr originAttr = (DOMAttr) originAttributes.item(j); + BindingType originBnding = XSDUtils.getBindingType(originAttr); + if (originBnding != BindingType.NONE) { + String originName = getOriginName(originAttr.getValue(), targetNamespacePrefix); + for (DOMAttr targetAttr : targetAttrs) { + Element targetElement = targetAttr.getOwnerElement(); + if (isBounded(originAttr.getOwnerElement(), originBnding, targetElement)) { + // node is a xs:complexType, xs:simpleType element, xsl:element, xs:group which + // matches the binding type of the originAttr + if (targetAttr != null && (originName.equals(targetAttr.getValue()))) { + collector.accept(originAttr, targetAttr); + } + } + } + } + } + } + } + if (node.hasChildNodes()) { + collectXSReferenceTypes(node.getChildNodes(), targetAttrs, targetNamespacePrefix, collector, + cancelChecker); + } + } + + } + + public static boolean isXSComplexType(Element element) { return "complexType".equals(element.getLocalName()); } - public static boolean isSimpleType(Element element) { + public static boolean isXSSimpleType(Element element) { return "simpleType".equals(element.getLocalName()); } + public static boolean isXSElement(Element element) { + return "element".equals(element.getLocalName()); + } + + public static boolean isXSGroup(Element element) { + return "group".equals(element.getLocalName()); + } } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLLanguageService.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLLanguageService.java index 86c487203..4cc03a6a8 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLLanguageService.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLLanguageService.java @@ -71,8 +71,8 @@ public void checkCanceled() { private final XMLDiagnostics diagnostics; private final XMLFoldings foldings; private final XMLDocumentLink documentLink; - private XMLDefinition definition; - private XMLReference reference; + private final XMLDefinition definition; + private final XMLReference reference; private final XMLCodeActions codeActions; private final XMLRename rename; @@ -204,13 +204,14 @@ public List findDocumentLinks(DOMDocument document) { return documentLink.findDocumentLinks(document); } - public List findDefinition(DOMDocument xmlDocument, Position position, CancelChecker cancelChecker) { + public List findDefinition(DOMDocument xmlDocument, Position position, + CancelChecker cancelChecker) { return definition.findDefinition(xmlDocument, position, cancelChecker); } - public List findReferences(DOMDocument xmlDocument, Position position, - ReferenceContext context) { - return reference.findReferences(xmlDocument, position, context); + public List findReferences(DOMDocument xmlDocument, Position position, ReferenceContext context, + CancelChecker cancelChecker) { + return reference.findReferences(xmlDocument, position, context, cancelChecker); } public List doCodeActions(CodeActionContext context, Range range, DOMDocument document, diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLReference.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLReference.java index d13a62b47..446104556 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLReference.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLReference.java @@ -16,6 +16,7 @@ import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.ReferenceContext; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.eclipse.lsp4xml.dom.DOMDocument; import org.eclipse.lsp4xml.services.extensions.IReferenceParticipant; import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry; @@ -32,10 +33,11 @@ public XMLReference(XMLExtensionsRegistry extensionsRegistry) { this.extensionsRegistry = extensionsRegistry; } - public List findReferences(DOMDocument document, Position position, ReferenceContext context) { + public List findReferences(DOMDocument document, Position position, ReferenceContext context, + CancelChecker cancelChecker) { List locations = new ArrayList<>(); for (IReferenceParticipant participant : extensionsRegistry.getReferenceParticipants()) { - participant.findReference(document, position, context, locations); + participant.findReference(document, position, context, locations, cancelChecker); } return locations; } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/AbstractDefinitionParticipant.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/AbstractDefinitionParticipant.java index 4edd2a050..491b1f492 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/AbstractDefinitionParticipant.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/AbstractDefinitionParticipant.java @@ -1,3 +1,12 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ package org.eclipse.lsp4xml.services.extensions; import java.util.List; @@ -9,6 +18,12 @@ import org.eclipse.lsp4xml.dom.DOMDocument; import org.eclipse.lsp4xml.dom.DOMNode; +/** + * Abstract class for definition. + * + * @author Angelo ZERR + * + */ public abstract class AbstractDefinitionParticipant implements IDefinitionParticipant { @Override @@ -28,8 +43,25 @@ public void findDefinition(DOMDocument document, Position position, List locations, CancelChecker cancelChecker); diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/AbstractReferenceParticipant.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/AbstractReferenceParticipant.java new file mode 100644 index 000000000..5acd48172 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/AbstractReferenceParticipant.java @@ -0,0 +1,69 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4xml.services.extensions; + +import java.util.List; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.ReferenceContext; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4xml.commons.BadLocationException; +import org.eclipse.lsp4xml.dom.DOMDocument; +import org.eclipse.lsp4xml.dom.DOMNode; + +/** + * Abstract class for reference participant. + * + * @author Angelo ZERR + * + */ +public abstract class AbstractReferenceParticipant implements IReferenceParticipant { + + @Override + public void findReference(DOMDocument document, Position position, ReferenceContext context, + List locations, CancelChecker cancelChecker) { + if (!match(document)) { + return; + } + try { + int offset = document.offsetAt(position); + DOMNode node = document.findNodeAt(offset); + if (node != null) { + findReferences(node, position, offset, context, locations, cancelChecker); + } + } catch (BadLocationException e) { + + } + } + + /** + * Returns true if the reference support is applicable for the given document + * and false otherwise. + * + * @param document + * @return true if the reference support is applicable for the given document + * and false otherwise. + */ + protected abstract boolean match(DOMDocument document); + + /** + * Find the references + * + * @param node + * @param position + * @param offset + * @param locations + * @param cancelChecker + */ + protected abstract void findReferences(DOMNode node, Position position, int offset, ReferenceContext context, + List locations, CancelChecker cancelChecker); + +} diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/IReferenceParticipant.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/IReferenceParticipant.java index 40f49add4..6bf7492cd 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/IReferenceParticipant.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/IReferenceParticipant.java @@ -15,6 +15,7 @@ import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.ReferenceContext; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.eclipse.lsp4xml.dom.DOMDocument; /** @@ -23,6 +24,6 @@ */ public interface IReferenceParticipant { - void findReference(DOMDocument document, Position position, ReferenceContext context, List locations); + void findReference(DOMDocument document, Position position, ReferenceContext context, List locations, CancelChecker cancelChecker); } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ClientCapabilitiesWrapper.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ClientCapabilitiesWrapper.java index 994d58e14..2eb362917 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ClientCapabilitiesWrapper.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ClientCapabilitiesWrapper.java @@ -76,6 +76,10 @@ public boolean isDefinitionDynamicRegistered() { return v3Supported && isDynamicRegistrationSupported(getTextDocument().getDefinition()); } + public boolean isReferencesDynamicRegistrationSupported() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getReferences()); + } + public boolean isCodeActionDynamicRegistered() { return v3Supported && isDynamicRegistrationSupported(getTextDocument().getCodeAction()); } @@ -87,7 +91,7 @@ public boolean isHoverDynamicRegistered() { public boolean isDocumentHighlightDynamicRegistered() { return v3Supported && isDynamicRegistrationSupported(getTextDocument().getDocumentHighlight()); } - + private boolean isDynamicRegistrationSupported(DynamicRegistrationCapabilities capability) { return capability != null && capability.getDynamicRegistration() != null && capability.getDynamicRegistration().booleanValue(); diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesInitializer.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesInitializer.java index 268087365..ebca66f6f 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesInitializer.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesInitializer.java @@ -48,6 +48,7 @@ public static ServerCapabilities getNonDynamicServerCapabilities(ClientCapabilit serverCapabilities.setRenameProvider(!clientCapabilities.isRenameDynamicRegistrationSupported()); serverCapabilities.setFoldingRangeProvider(!clientCapabilities.isRangeFoldingDynamicRegistrationSupported()); serverCapabilities.setDefinitionProvider(!clientCapabilities.isDefinitionDynamicRegistered()); + serverCapabilities.setReferencesProvider(!clientCapabilities.isReferencesDynamicRegistrationSupported()); if (!clientCapabilities.isLinkDynamicRegistrationSupported()) { serverCapabilities.setDocumentLinkProvider(DEFAULT_LINK_OPTIONS); diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilityManager.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilityManager.java index eeb4d2868..6ee8234a7 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilityManager.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilityManager.java @@ -32,6 +32,8 @@ import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_HOVER; import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_LINK; import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_RENAME; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.REFERENCES_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_REFERENCES; import java.util.Collections; import java.util.HashSet; @@ -141,14 +143,19 @@ public void initializeCapabilities() { if (this.getClientCapabilities().isDefinitionDynamicRegistered()) { registerCapability(DEFINITION_ID, TEXT_DOCUMENT_DEFINITION); } + if (this.getClientCapabilities().isReferencesDynamicRegistrationSupported()) { + registerCapability(REFERENCES_ID, TEXT_DOCUMENT_REFERENCES); + } syncDynamicCapabilitiesWithPreferences(); } /** - * Registers(indicates the servers ability to support the service) all capabilities that have the ability to be turned - * on/off on the client side through preferences. + * Registers(indicates the servers ability to support the service) all + * capabilities that have the ability to be turned on/off on the client side + * through preferences. * - * In the case the preference is set to off/false this server will tell the cliet it does not support this capability. + * In the case the preference is set to off/false this server will tell the + * cliet it does not support this capability. * * If a capability is not dynamic, it's handled by * {@link ServerCapabilitiesInitializer} @@ -169,8 +176,7 @@ public void syncDynamicCapabilitiesWithPreferences() { XMLSymbolSettings symbolSettings = this.textDocumentService.getSharedSymbolSettings(); if (this.getClientCapabilities().isDocumentSymbolDynamicRegistrationSupported()) { - toggleCapability(symbolSettings.isEnabled(), DOCUMENT_SYMBOL_ID, - TEXT_DOCUMENT_DOCUMENT_SYMBOL, null); + toggleCapability(symbolSettings.isEnabled(), DOCUMENT_SYMBOL_ID, TEXT_DOCUMENT_DOCUMENT_SYMBOL, null); } } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLPositionUtility.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLPositionUtility.java index c545d214e..7c2e39ca5 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLPositionUtility.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLPositionUtility.java @@ -410,6 +410,12 @@ public static LocationLink createLocationLink(DOMNode origin, DOMNode target) { originSelectionRange); } + public static Location createLocation(DOMNode target) { + DOMDocument targetDocument = target.getOwnerDocument(); + Range targetRange = XMLPositionUtility.createRange(target.getStart(), target.getEnd(), targetDocument); + return new Location(targetDocument.getDocumentURI(), targetRange); + } + public static Range selectContent(int offset, DOMDocument document) { DOMNode node = document.findNodeAt(offset); if (node != null) { diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/XMLAssert.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/XMLAssert.java index 0b5e932fe..1eefa99bb 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/XMLAssert.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/XMLAssert.java @@ -31,12 +31,14 @@ import org.eclipse.lsp4j.DocumentLink; import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.MarkedString; import org.eclipse.lsp4j.MarkupContent; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.ReferenceContext; import org.eclipse.lsp4j.SymbolKind; import org.eclipse.lsp4j.TextDocumentEdit; import org.eclipse.lsp4j.TextEdit; @@ -651,7 +653,7 @@ public static void testDefinitionFor(String value, String fileURI, LocationLink. } - public static LocationLink l(final String uri, final Range originRange, Range targetRange) { + public static LocationLink ll(final String uri, final Range originRange, Range targetRange) { return new LocationLink(uri, targetRange, targetRange, originRange); } @@ -660,4 +662,44 @@ public static void assertLocationLink(List actual, Locat Assert.assertArrayEquals(expected, actual.toArray()); } + // ------------------- Reference assert + + public static void testReferencesFor(String xml, Location... expected) throws BadLocationException { + testReferencesFor(xml, null, expected); + } + + public static void testReferencesFor(String value, String fileURI, Location... expected) + throws BadLocationException { + int offset = value.indexOf('|'); + value = value.substring(0, offset) + value.substring(offset + 1); + + TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); + Position position = document.positionAt(offset); + + XMLLanguageService xmlLanguageService = new XMLLanguageService(); + + ContentModelSettings settings = new ContentModelSettings(); + settings.setUseCache(false); + xmlLanguageService.doSave(new SettingsSaveContext(settings)); + + DOMDocument xmlDocument = DOMParser.getInstance().parse(document, + xmlLanguageService.getResolverExtensionManager()); + xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); + + List actual = xmlLanguageService.findReferences(xmlDocument, position, + new ReferenceContext(), () -> { + }); + assertLocation(actual, expected); + + } + + public static Location l(final String uri, final Range range) { + return new Location(uri, range); + } + + public static void assertLocation(List actual, Location... expected) { + Assert.assertEquals(expected.length, actual.size()); + Assert.assertArrayEquals(expected, actual.toArray()); + } + } \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDDefinitionExtensionsTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDDefinitionExtensionsTest.java index 965c12cc5..d1c7165e0 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDDefinitionExtensionsTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDDefinitionExtensionsTest.java @@ -9,7 +9,7 @@ *******************************************************************************/ package org.eclipse.lsp4xml.extensions.xsd; -import static org.eclipse.lsp4xml.XMLAssert.l; +import static org.eclipse.lsp4xml.XMLAssert.ll; import static org.eclipse.lsp4xml.XMLAssert.r; import org.eclipse.lsp4j.LocationLink; @@ -39,7 +39,7 @@ public void definitionOnElementType() throws BadLocationException { " \r\n" + // " \r\n" + // ""; - testDefinitionFor(xml, l("test.xsd", r(3, 34, 3, 50), r(5, 22, 5, 38))); + testDefinitionFor(xml, ll("test.xsd", r(3, 34, 3, 50), r(5, 22, 5, 38))); } @Test @@ -63,7 +63,7 @@ public void definitionOnExtensionBase() throws BadLocationException { " \r\n" + // " \r\n" + // ""; - testDefinitionFor(xml, l("test.xsd", r(9, 22, 9, 34), r(3, 22, 3, 34))); + testDefinitionFor(xml, ll("test.xsd", r(9, 22, 9, 34), r(3, 22, 3, 34))); } @Test @@ -83,7 +83,7 @@ public void definitionWithTargetNamespace() throws BadLocationException { " \r\n" + // " \r\n" + // ""; - testDefinitionFor(xml, l("test.xsd", r(3, 35, 3, 60), r(6, 22, 6, 43))); + testDefinitionFor(xml, ll("test.xsd", r(3, 35, 3, 60), r(6, 22, 6, 43))); } @Test @@ -111,7 +111,7 @@ public void definitionOnElementRef() throws BadLocationException { " \r\n" + // " \r\n" + // ""; - testDefinitionFor(xml, l("test.xsd", r(8, 19, 8, 32), r(18, 18, 18, 28))); + testDefinitionFor(xml, ll("test.xsd", r(8, 19, 8, 32), r(18, 18, 18, 28))); } @Test @@ -139,7 +139,7 @@ public void definitionOnGroupRef() throws BadLocationException { " \r\n" + // " \r\n" + // ""; - testDefinitionFor(xml, l("test.xsd", r(5, 17, 5, 33), r(12, 16, 12, 29))); + testDefinitionFor(xml, ll("test.xsd", r(5, 17, 5, 33), r(12, 16, 12, 29))); } private static void testDefinitionFor(String xml, LocationLink... expectedItems) throws BadLocationException { diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDReferenceExtensionsTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDReferenceExtensionsTest.java new file mode 100644 index 000000000..911445322 --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDReferenceExtensionsTest.java @@ -0,0 +1,103 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4xml.extensions.xsd; + +import static org.eclipse.lsp4xml.XMLAssert.l; +import static org.eclipse.lsp4xml.XMLAssert.r; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4xml.XMLAssert; +import org.eclipse.lsp4xml.commons.BadLocationException; +import org.junit.Test; + +/** + * XSD references tests + * + */ +public class XSDReferenceExtensionsTest { + + @Test + public void referenceOnElementName() throws BadLocationException { + String xml = "\r\n" + // + "\r\n" + + // + " \r\n" + // <-- find references from this xs:element/@name (referenced by + // xs:element/@ref + xs:element/@substitutionGroup) + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + ""; + testReferencesFor(xml, l("test.xsd", r(5, 19, 5, 27)), l("test.xsd", r(8, 43, 8, 51))); + } + + @Test + public void referenceOnGroupName() throws BadLocationException { + String xml = "\r\n" + // + "\r\n" + + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // <-- find references from this xs:group/@name (referenced by + // xs:group/@ref) + ""; + testReferencesFor(xml, l("test.xsd", r(6, 17, 6, 25))); + } + + @Test + public void referenceOnComplexTypeName() throws BadLocationException { + String xml = "\r\n" + // + "\r\n" + + // + " \r\n" + // + " \r\n" + // <-- find references from xs:complexType/@name + // (referenced by xs:element/@type + + // xs:extension/@base) + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + ""; + testReferencesFor(xml, l("test.xsd", r(2, 29, 2, 46)), l("test.xsd", r(7, 22, 7, 39))); + } + + @Test + public void referenceOnSimpleTypeName() throws BadLocationException { + String xml = "\r\n" + // + "\r\n" + + // + " \r\n" + // + " \r\n" + // <-- find references from xs:simpleType/@name + // (referenced by xs:element/@type + + // xs:restriction/@base) + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + ""; + testReferencesFor(xml, l("test.xsd", r(2, 29, 2, 45)), l("test.xsd", r(7, 23, 7, 39))); + } + + private void testReferencesFor(String xml, Location... expectedItems) throws BadLocationException { + XMLAssert.testReferencesFor(xml, "test.xsd", expectedItems); + } +}