From 804d215e2318cb1d10974538a555ace633c56f40 Mon Sep 17 00:00:00 2001 From: azerr Date: Mon, 24 Jun 2019 23:13:25 +0200 Subject: [PATCH] Add support for `textDocument/references` for XML Schema types Fix #58 Signed-off-by: azerr --- .../lsp4xml/XMLTextDocumentService.java | 6 +- .../org/eclipse/lsp4xml/dom/DOMDocument.java | 5 - .../java/org/eclipse/lsp4xml/dom/DOMNode.java | 1 + .../lsp4xml/extensions/xsd/DataType.java | 4 +- .../lsp4xml/extensions/xsd/XSDPlugin.java | 7 + .../participants/XSDReferenceParticipant.java | 51 +++++ .../extensions/xsd/utils/XSDUtils.java | 187 +++++++++++++++++- .../lsp4xml/services/XMLLanguageService.java | 13 +- .../lsp4xml/services/XMLReference.java | 6 +- .../AbstractDefinitionParticipant.java | 32 +++ .../AbstractReferenceParticipant.java | 69 +++++++ .../extensions/IReferenceParticipant.java | 3 +- .../ClientCapabilitiesWrapper.java | 6 +- .../ServerCapabilitiesInitializer.java | 1 + .../capabilities/XMLCapabilityManager.java | 16 +- .../lsp4xml/utils/XMLPositionUtility.java | 6 + 16 files changed, 382 insertions(+), 31 deletions(-) create mode 100644 org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/participants/XSDReferenceParticipant.java create mode 100644 org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/extensions/AbstractReferenceParticipant.java 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..a638bcc30e --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/participants/XSDReferenceParticipant.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* 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)); + }, 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..a404208939 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 ReferencedNode { + + private final DOMAttr attr; + private BindingType referencedBindingType; + private boolean xsElement; + + public ReferencedNode(DOMAttr node) { + this.attr = node; + this.xsElement = isXSElement(node.getOwnerElement()); + this.referencedBindingType = getReferencedBindingType(node); + } + + public DOMNode 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,141 @@ 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) { + 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); + } + } + } + } + if (referencedNodes.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 + NodeList nodes = document.getDocumentElement().getChildNodes(); + collectXSReferenceTypes(nodes, referencedNodes, collector, cancelChecker); + } + + /** + * 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 ReferencedNode(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 (ReferencedNode 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) {