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 385b8b3d68..4759b2ea58 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 8f45b456e9..909bb5028a 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 3524f53dc8..4033e22b72 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 a54e18fa50..4873b1b928 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 5bd26230ec..c15251b8f7 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 0000000000..a67434bd2f --- /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 7979cf79d4..a8c71523ad 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; @@ -29,6 +35,46 @@ */ public class XSDUtils { + /** + * Attribute name which can be referenced by another attribute: + * + *
    + *
  • xs:complexType/@name
  • + *
  • xs:simpleType/@name
  • + *
  • xs:element/@name
  • + *
  • xs:group/@name
  • + *
+ * + */ + private static class ReferencedAttr { + + private final DOMAttr attr; + private BindingType referencedBindingType; + private boolean xsElement; + + public ReferencedAttr(DOMAttr node) { + this.attr = node; + this.xsElement = isXSElement(node.getOwnerElement()); + this.referencedBindingType = getReferencedBindingType(node); + } + + public DOMAttr getNode() { + return attr; + } + + public boolean isReference(String value, BindingType bindingType) { + if (referencedBindingType.equals(bindingType) + || (referencedBindingType.isSimple() && bindingType.isSimple()) + || (referencedBindingType.isComplex() && bindingType.isComplex()) + || (xsElement && (BindingType.ELEMENT.equals(bindingType)) + || BindingType.REF.equals(bindingType))) { + return Objects.equal(value, attr.getValue()); + } + return false; + } + + } + /** * Binding type of xs attribute. * @@ -68,11 +114,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; } @@ -99,6 +145,18 @@ public static BindingType getBindingType(DOMAttr attr) { return BindingType.NONE; } + private static BindingType getReferencedBindingType(DOMAttr attr) { + Element element = attr.getOwnerElement(); + if (isXSSimpleType(element)) { + return BindingType.SIMPLE; + } else if (isXSComplexType(element)) { + return BindingType.COMPLEX; + } else if (isXSElement(element) || isXSGroup(element)) { + return BindingType.REF; + } + return BindingType.NONE; + } + /** * Collect XSD types declared in the XML Schema according the given attribute * and binding type. @@ -168,26 +226,154 @@ public static void collectXSTypes(DOMAttr originAttr, BindingType bindingType, b } private static boolean canCollectElement(DOMAttr originAttr, Element targetElement, BindingType bindingType) { - if (isComplexType(targetElement)) { + if (isXSComplexType(targetElement)) { return bindingType.isComplex(); - } else if (isSimpleType(targetElement)) { + } else if (isXSSimpleType(targetElement)) { return bindingType.isSimple(); } else if (bindingType == 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 isXSElement(targetElement); } return false; } - public static boolean isComplexType(Element element) { + /** + * Collect references types from the given referenced node. + * + * @param referencedNode the referenced node + * @param collector the collector to collect reference origin and target + * node. + */ + public static void collectXSReferenceTypes(DOMNode referencedNode, BiConsumer collector, + CancelChecker cancelChecker) { + // get referenced attribute nodes from the given referenced node + List referencedAttrs = getReferencedAttrs(referencedNode); + if (referencedAttrs.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 + + // Collect references for each references nodes + Document document = referencedNode.getOwnerDocument(); + NodeList nodes = document.getDocumentElement().getChildNodes(); + collectXSReferenceTypes(nodes, referencedAttrs, 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 getReferencedAttrs(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 referencedNodes the list of referenced nodes. + */ + private static void addReferenceNode(DOMNode node, List referencedNodes) { + 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())) { + referencedNodes.add(new ReferencedAttr(attr)); + } + } + + private static void collectXSReferenceTypes(NodeList nodes, List referencedNodes, + 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 element = (DOMElement) node; + NamedNodeMap attributes = element.getAttributes(); + if (attributes != null) { + for (int j = 0; j < attributes.getLength(); j++) { + DOMAttr attr = (DOMAttr) attributes.item(j); + BindingType bindingType = XSDUtils.getBindingType(attr); + if (bindingType != BindingType.NONE) { + String value = attr.getValue(); + int index = value.indexOf(":"); + if (index != -1) { + value = value.substring(index + 1, value.length()); + } + for (ReferencedAttr referencedNode : referencedNodes) { + if (referencedNode.isReference(value, bindingType)) { + collector.accept(attr, referencedNode.getNode()); + } + } + } + } + } + + } + if (node.hasChildNodes()) { + collectXSReferenceTypes(node.getChildNodes(), referencedNodes, 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 86c4872034..4cc03a6a85 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 d13a62b47c..4461045566 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 4edd2a050e..491b1f492f 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 0000000000..5acd48172c --- /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 40f49add4d..6bf7492cd5 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 994d58e14f..2eb3629174 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 268087365b..ebca66f6f9 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 eeb4d2868c..6ee8234a73 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 c545d214e3..7c2e39ca5a 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 0b5e932fec..1eefa99bbb 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 965c12cc56..d1c7165e0f 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 0000000000..6224b5d852 --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/extensions/xsd/XSDReferenceExtensionsTest.java @@ -0,0 +1,84 @@ +/******************************************************************************* +* 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 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); + } +}