Skip to content

Commit

Permalink
Add support for textDocument/documentHighlight for XML Schema types
Browse files Browse the repository at this point in the history
Fix #470

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Jun 26, 2019
1 parent 00509d7 commit 4ebac6a
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDCodeLensParticipant;
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDCompletionParticipant;
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDDefinitionParticipant;
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDHighlightingParticipant;
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDReferenceParticipant;
import org.eclipse.lsp4xml.extensions.xsd.participants.diagnostics.XSDDiagnosticsParticipant;
import org.eclipse.lsp4xml.services.extensions.ICodeLensParticipant;
import org.eclipse.lsp4xml.services.extensions.ICompletionParticipant;
import org.eclipse.lsp4xml.services.extensions.IDefinitionParticipant;
import org.eclipse.lsp4xml.services.extensions.IHighlightingParticipant;
import org.eclipse.lsp4xml.services.extensions.IReferenceParticipant;
import org.eclipse.lsp4xml.services.extensions.IXMLExtension;
import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry;
Expand All @@ -42,9 +44,8 @@ public class XSDPlugin implements IXMLExtension {
private final IDiagnosticsParticipant diagnosticsParticipant;

private final IReferenceParticipant referenceParticipant;

private final ICodeLensParticipant codeLensParticipant;

private final IHighlightingParticipant highlightingParticipant;
private XSDURIResolverExtension uiResolver;

public XSDPlugin() {
Expand All @@ -53,6 +54,7 @@ public XSDPlugin() {
diagnosticsParticipant = new XSDDiagnosticsParticipant();
referenceParticipant = new XSDReferenceParticipant();
codeLensParticipant = new XSDCodeLensParticipant();
highlightingParticipant = new XSDHighlightingParticipant();
}

@Override
Expand Down Expand Up @@ -82,6 +84,7 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerDiagnosticsParticipant(diagnosticsParticipant);
registry.registerReferenceParticipant(referenceParticipant);
registry.registerCodeLensParticipant(codeLensParticipant);
registry.registerHighlightingParticipant(highlightingParticipant);
}

@Override
Expand All @@ -92,5 +95,6 @@ public void stop(XMLExtensionsRegistry registry) {
registry.unregisterDiagnosticsParticipant(diagnosticsParticipant);
registry.unregisterReferenceParticipant(referenceParticipant);
registry.unregisterCodeLensParticipant(codeLensParticipant);
registry.unregisterHighlightingParticipant(highlightingParticipant);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void doCodeLens(DOMDocument xmlDocument, List<CodeLens> lenses, CancelChe
// Add references CodeLens for each xs:simpleType, xs:complexType, xs:element,
// xs:group root element.
Map<DOMElement, CodeLens> cache = new HashMap<>();
XSDUtils.collectXSReferenceTypes(xmlDocument, (origin, target) -> {
XSDUtils.searchXSOriginAttributes(xmlDocument, (origin, target) -> {
// Increment references count Codelens for the given target element
DOMElement targetElement = target.getOwnerElement();
CodeLens codeLens = cache.get(targetElement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void onAttributeValue(String valuePrefix, ICompletionRequest request, ICo
// - @type (ex : xs:element/@type)
// - @base (ex : xs:extension/@base)
// bound to complextTypes/@name
XSDUtils.collectXSTypes(originAttr, bindingType, false, (targetNamespacePrefix, targetAttr) -> {
XSDUtils.searchXSTargetAttributes(originAttr, bindingType, false, (targetNamespacePrefix, targetAttr) -> {
CompletionItem item = new CompletionItem();
item.setDocumentation(new MarkupContent(MarkupKind.MARKDOWN, DataType.getDocumentation(targetAttr)));
String value = createComplexTypeValue(targetAttr, targetNamespacePrefix);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected void findDefinition(DOMNode node, Position position, int offset, List<
DOMAttr attr = node.findAttrAt(offset);
BindingType bindingType = XSDUtils.getBindingType(attr);
if (bindingType != BindingType.NONE) {
XSDUtils.collectXSTypes(attr, bindingType, true, (targetNamespacePrefix, targetAttr) -> {
XSDUtils.searchXSTargetAttributes(attr, bindingType, true, (targetNamespacePrefix, targetAttr) -> {
LocationLink location = XMLPositionUtility.createLocationLink(attr.getNodeAttrValue(),
targetAttr.getNodeAttrValue());
locations.add(location);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*******************************************************************************
* 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.DocumentHighlight;
import org.eclipse.lsp4j.DocumentHighlightKind;
import org.eclipse.lsp4j.Position;
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.extensions.xsd.utils.XSDUtils.BindingType;
import org.eclipse.lsp4xml.services.extensions.IHighlightingParticipant;
import org.eclipse.lsp4xml.utils.DOMUtils;
import org.eclipse.lsp4xml.utils.XMLPositionUtility;

/**
* XSD highlight participant
*
* @author Angelo ZERR
*
*/
public class XSDHighlightingParticipant implements IHighlightingParticipant {

@Override
public void findDocumentHighlights(DOMNode node, Position position, int offset, List<DocumentHighlight> highlights,
CancelChecker cancelChecker) {
// XSD highlight applicable only for XSD file
DOMDocument document = node.getOwnerDocument();
if (!DOMUtils.isXSD(document)) {
return;
}
// Highlight works ony when attribute is selected (orign or target attribute)
DOMAttr attr = node.findAttrAt(offset);
if (attr == null) {
return;
}
// Try to get the binding from the origin attribute
BindingType bindingType = XSDUtils.getBindingType(attr);
if (bindingType != BindingType.NONE) {
// It's an origin attribute, highlight the origin and target attribute
DOMAttr originAttr = attr;
highlights
.add(new DocumentHighlight(XMLPositionUtility.createRange(originAttr.getNodeAttrValue().getStart(),
originAttr.getNodeAttrValue().getEnd(), document), DocumentHighlightKind.Read));
// Search target attributes
XSDUtils.searchXSTargetAttributes(originAttr, bindingType, true, (targetNamespacePrefix, targetAttr) -> {
highlights.add(new DocumentHighlight(
XMLPositionUtility.createRange(targetAttr.getNodeAttrValue().getStart(),
targetAttr.getNodeAttrValue().getEnd(), targetAttr.getOwnerDocument()),
DocumentHighlightKind.Write));
});

} else if (XSDUtils.isXSTargetElement(attr.getOwnerElement())) {
// It's an target attribute, highlight all origin attributes linked to this
// target attribute
DOMAttr targetAttr = attr;
highlights.add(new DocumentHighlight(
XMLPositionUtility.createRange(targetAttr.getNodeAttrValue().getStart(),
targetAttr.getNodeAttrValue().getEnd(), targetAttr.getOwnerDocument()),
DocumentHighlightKind.Write));
XSDUtils.searchXSOriginAttributes(targetAttr,
(origin, target) -> highlights.add(new DocumentHighlight(
XMLPositionUtility.createRange(origin.getNodeAttrValue().getStart(),
origin.getNodeAttrValue().getEnd(), origin.getOwnerDocument()),
DocumentHighlightKind.Read)),
cancelChecker);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ protected void findReferences(DOMNode node, Position position, int offset, Refer
if (attr != null) {
node = attr;
}
XSDUtils.collectXSReferenceTypes(node,
(from, to) -> locations.add(XMLPositionUtility.createLocation(from.getNodeAttrValue())), cancelChecker);
XSDUtils.searchXSOriginAttributes(node,
(origin, target) -> locations.add(XMLPositionUtility.createLocation(origin.getNodeAttrValue())),
cancelChecker);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,17 @@ public boolean isComplex() {
}

/**
* Returns the binding type of the given attribute.
* Returns the binding type of the origin attribute which bounds an another
* target attribute.
*
* @param attr the attribute
* @return the binding type of the given attribute.
* @param originAttr the origin attribute
* @return the binding type of the origin attribute which bounds an another
* target attribute.
*/
public static BindingType getBindingType(DOMAttr attr) {
String name = attr.getName();
public static BindingType getBindingType(DOMAttr originAttr) {
String name = originAttr.getName();
if ("type".equals(name)) {
if ("attribute".equals(attr.getOwnerElement().getLocalName())) {
if ("attribute".equals(originAttr.getOwnerElement().getLocalName())) {
// - <xs:attribute type="
return BindingType.SIMPLE;
}
Expand All @@ -71,7 +73,7 @@ public static BindingType getBindingType(DOMAttr attr) {
if ("base".equals(name)) {
// - <xs:restriction base="
// - <xs:extension base="
DOMElement element = attr.getOwnerElement();
DOMElement element = originAttr.getOwnerElement();
DOMElement parent = element.getParentElement();
if (parent != null) {
if (parent.getLocalName().equals("complexContent") || isXSComplexType(parent)) {
Expand Down Expand Up @@ -106,19 +108,15 @@ public static BindingType getBindingType(DOMAttr attr) {
}

/**
* Collect XSD types declared in the XML Schema according the given attribute
* and binding type.
*
* - xs:complexType/@name attributes - xs:simpleType/@name attributes -
* xs:element/@name attributes if attribute is xs:element/@ref - xs:group/@name
* attributes if attribute is xs:group/@ref
* Collect XSD target attributes declared in the XML Schema according the given
* attribute and binding type.
*
* @param originAttr the origin attribute.
* @param matchAttr true if the attribute value must match the value of
* complexType/@name and false otherwise.
* @param collector collector to collect XSD types attributes.
* @param matchAttr true if the attribute value must match the value of target
* attribute value and false otherwise.
* @param collector collector to collect XSD target attributes.
*/
public static void collectXSTypes(DOMAttr originAttr, BindingType bindingType, boolean matchAttr,
public static void searchXSTargetAttributes(DOMAttr originAttr, BindingType bindingType, boolean matchAttr,
BiConsumer<String, DOMAttr> collector) {
if (bindingType == BindingType.NONE) {
return;
Expand Down Expand Up @@ -192,12 +190,13 @@ private static boolean isBounded(Element originElement, BindingType originBindin
}

/**
* Collect references types from the given referenced node.
* Search origin attributes from the given target node..
*
* @param targetNode the referenced node
* @param collector the collector to collect reference origin and target node.
* @param collector the collector to collect reference between an origin and
* target attribute.
*/
public static void collectXSReferenceTypes(DOMNode targetNode, BiConsumer<DOMAttr, DOMAttr> collector,
public static void searchXSOriginAttributes(DOMNode targetNode, BiConsumer<DOMAttr, DOMAttr> collector,
CancelChecker cancelChecker) {
// get referenced attribute nodes from the given referenced node
List<DOMAttr> targetAttrs = getTargetAttrs(targetNode);
Expand All @@ -223,7 +222,7 @@ public static void collectXSReferenceTypes(DOMNode targetNode, BiConsumer<DOMAtt
// Collect references for each references nodes

NodeList nodes = documentElement.getChildNodes();
collectXSReferenceTypes(nodes, targetAttrs, targetNamespacePrefix, collector, cancelChecker);
searchXSOriginAttributes(nodes, targetAttrs, targetNamespacePrefix, collector, cancelChecker);
}

/**
Expand All @@ -241,7 +240,7 @@ private static List<DOMAttr> getTargetAttrs(DOMNode referencedNode) {
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);
addTargetNode(referencedNode, referencedNodes);
break;
case Node.DOCUMENT_NODE:
// The referenced node is the DOM document, collect all attributes
Expand All @@ -252,9 +251,8 @@ private static List<DOMAttr> getTargetAttrs(DOMNode referencedNode) {
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 (isXSTargetElement(element)) {
addTargetNode(element, referencedNodes);
}
}
}
Expand All @@ -268,7 +266,7 @@ private static List<DOMAttr> getTargetAttrs(DOMNode referencedNode) {
* @param node the node to add.
* @param targetAttrs the list of referenced nodes.
*/
private static void addReferenceNode(DOMNode node, List<DOMAttr> targetAttrs) {
private static void addTargetNode(DOMNode node, List<DOMAttr> targetAttrs) {
DOMAttr attr = null;
switch (node.getNodeType()) {
case Node.ATTRIBUTE_NODE:
Expand All @@ -284,7 +282,7 @@ private static void addReferenceNode(DOMNode node, List<DOMAttr> targetAttrs) {
}
}

private static void collectXSReferenceTypes(NodeList nodes, List<DOMAttr> targetAttrs, String targetNamespacePrefix,
private static void searchXSOriginAttributes(NodeList nodes, List<DOMAttr> targetAttrs, String targetNamespacePrefix,
BiConsumer<DOMAttr, DOMAttr> collector, CancelChecker cancelChecker) {
for (int i = 0; i < nodes.getLength(); i++) {
if (cancelChecker != null) {
Expand Down Expand Up @@ -315,7 +313,7 @@ private static void collectXSReferenceTypes(NodeList nodes, List<DOMAttr> target
}
}
if (node.hasChildNodes()) {
collectXSReferenceTypes(node.getChildNodes(), targetAttrs, targetNamespacePrefix, collector,
searchXSOriginAttributes(node.getChildNodes(), targetAttrs, targetNamespacePrefix, collector,
cancelChecker);
}
}
Expand All @@ -337,4 +335,8 @@ public static boolean isXSElement(Element element) {
public static boolean isXSGroup(Element element) {
return "group".equals(element.getLocalName());
}

public static boolean isXSTargetElement(Element element) {
return isXSComplexType(element) || isXSSimpleType(element) || isXSElement(element) || isXSGroup(element);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.eclipse.lsp4xml.dom.DOMElement;
import org.eclipse.lsp4xml.dom.DOMNode;
import org.eclipse.lsp4xml.dom.parser.TokenType;
import org.eclipse.lsp4xml.services.extensions.IHighlightingParticipant;
import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry;

/**
Expand All @@ -56,10 +57,22 @@ public List<DocumentHighlight> findDocumentHighlights(DOMDocument xmlDocument, P
return Collections.emptyList();
}
DOMNode node = xmlDocument.findNodeAt(offset);
if (node == null || !node.isElement() || ((DOMElement) node).getTagName() == null) {
if (node == null) {
return Collections.emptyList();
}
List<DocumentHighlight> highlights = new ArrayList<>();
fillWithDefaultHighlights(node, position, offset, highlights, cancelChecker);
fillWithCustomHighlights(node, position, offset, highlights, cancelChecker);
return highlights;
}

private static void fillWithDefaultHighlights(DOMNode node, Position position, int offset,
List<DocumentHighlight> highlights, CancelChecker cancelChecker) {
if (!node.isElement() || ((DOMElement) node).getTagName() == null) {
return;
}

DOMDocument xmlDocument = node.getOwnerDocument();
Range startTagRange = null;
Range endTagRange = null;
if (node.isCDATA()) {
Expand All @@ -73,39 +86,41 @@ public List<DocumentHighlight> findDocumentHighlights(DOMDocument xmlDocument, P

} catch (BadLocationException e) {
LOGGER.log(Level.SEVERE, "In XMLHighlighting the Node at provided Offset is a BadLocation", e);
return Collections.emptyList();
return;
}
if (covers(tempRange, position)) {
startPos.setCharacter(startPos.getCharacter() + 1); // {Cursor}<![CDATA[ -> <{Cursor}![CDATA[
endPos.setCharacter(endPos.getCharacter() - 1); // ]]>{Cursor} -> ]]{Cursor}>
Position startPosEnd = new Position(startPos.getLine(), startPos.getCharacter() + 8);
Position endPosStart = new Position(endPos.getLine(), endPos.getCharacter() - 2);
return getHighlightsList(new Range(startPos, startPosEnd), new Range(endPosStart, endPos));
fillHighlightsList(new Range(startPos, startPosEnd), new Range(endPosStart, endPos), highlights);
}
return Collections.emptyList();
} else if (node.isElement()) {
DOMElement element = (DOMElement) node;
startTagRange = getTagNameRange(TokenType.StartTag, node.getStart(), xmlDocument);
endTagRange = element.hasEndTag()
? getTagNameRange(TokenType.EndTag, element.getEndTagOpenOffset(), xmlDocument)
: null;
if (doesTagCoverPosition(startTagRange, endTagRange, position)) {
return getHighlightsList(startTagRange, endTagRange);
fillHighlightsList(startTagRange, endTagRange, highlights);
}
}
return Collections.emptyList();
}

private static List<DocumentHighlight> getHighlightsList(Range startTagRange, Range endTagRange) {

List<DocumentHighlight> result = new ArrayList<>(2);
private static void fillHighlightsList(Range startTagRange, Range endTagRange, List<DocumentHighlight> result) {
if (startTagRange != null) {
result.add(new DocumentHighlight(startTagRange, DocumentHighlightKind.Read));
}
if (endTagRange != null) {
result.add(new DocumentHighlight(endTagRange, DocumentHighlightKind.Read));
}
return result;
}

private void fillWithCustomHighlights(DOMNode node, Position position, int offset,
List<DocumentHighlight> highlights, CancelChecker cancelChecker) {
// Consume highlighting participant
for (IHighlightingParticipant highlightingParticipant : extensionsRegistry.getHighlightingParticipants()) {
highlightingParticipant.findDocumentHighlights(node, position, offset,highlights, cancelChecker);
}
}
}
Loading

0 comments on commit 4ebac6a

Please sign in to comment.