Skip to content

Commit

Permalink
References support for text node
Browse files Browse the repository at this point in the history
Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr authored and datho7561 committed Jan 3, 2023
1 parent f4b33cf commit 3a25dad
Show file tree
Hide file tree
Showing 12 changed files with 495 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -614,4 +614,32 @@ public boolean isEmpty() {
return true;
}

/**
* Returns the DOM text node from the given <code>offset</code> and null
* otherwise.
*
* @param offset the offset.
*
* @return the DOM text node from the given <code>offset</code> and null
* otherwise.
*/
public DOMText findTextAt(int offset) {
if (offset > startTagCloseOffset && startTagCloseOffset == endTagOpenOffset - 1) {
// <foo>|</foo>
// In this case, DOM text doesn't exists, create an empty DOM text
DOMText text = new DOMText(offset, offset);
text.parent = this;
return text;
}
DOMNode node = super.findNodeAt(offset);
if (node != null) {
if (node.isText()) {
return (DOMText) node;
}
if (node.isElement() && node != this) {
return ((DOMElement) node).findTextAt(offset);
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,17 @@
*******************************************************************************/
package org.eclipse.lemminx.extensions.references.participants;

import java.util.List;

import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.extensions.references.XMLReferencesPlugin;
import org.eclipse.lemminx.extensions.references.settings.XMLReferenceExpression;
import org.eclipse.lemminx.extensions.references.utils.XMLReferencesSearchContext;
import org.eclipse.lemminx.extensions.references.utils.XMLReferencesUtils;
import org.eclipse.lemminx.extensions.xsd.DataType;
import org.eclipse.lemminx.services.extensions.completion.CompletionParticipantAdapter;
import org.eclipse.lemminx.services.extensions.completion.ICompletionRequest;
import org.eclipse.lemminx.services.extensions.completion.ICompletionResponse;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MarkupKind;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
Expand All @@ -45,43 +41,55 @@ public XMLReferencesCompletionParticipant(XMLReferencesPlugin plugin) {
this.plugin = plugin;
}

@Override
public void onXMLContent(ICompletionRequest request, ICompletionResponse response, CancelChecker cancelChecker)
throws Exception {
DOMNode fromNode = request.getNode();
if (fromNode.isElement()) {
fromNode = ((DOMElement) fromNode).findTextAt(request.getOffset());
}
searchToNodes(fromNode, request, response);
}

@Override
public void onAttributeValue(String valuePrefix, ICompletionRequest request, ICompletionResponse response,
CancelChecker cancelChecker) throws Exception {
DOMNode node = request.getNode();
Range fullRange = request.getReplaceRange();
DOMAttr originAttr = node.findAttrAt(request.getOffset());
DOMNode fromNode = node.findAttrAt(request.getOffset());
searchToNodes(fromNode, request, response);
}

List<XMLReferenceExpression> references = XMLReferencesUtils.findExpressionsWhichMatcheFrom(originAttr,
private void searchToNodes(DOMNode fromNode, ICompletionRequest request, ICompletionResponse response) {
XMLReferencesSearchContext searchContext = XMLReferencesUtils.findExpressionsWhichMatchFrom(fromNode,
plugin.getReferencesSettings());
if (references != null && !references.isEmpty()) {
XMLReferencesUtils.searchToAttributes(originAttr, references, false, true,
(targetNamespacePrefix, targetAttr, expression) -> {
if (searchContext != null) {
XMLReferencesUtils.searchToNodes(fromNode, searchContext, false, true,
(toNamespacePrefix, toNode, expression) -> {
CompletionItem item = new CompletionItem();
item.setDocumentation(
new MarkupContent(MarkupKind.MARKDOWN, DataType.getDocumentation(targetAttr)));
String value = createReferenceValue(targetAttr, targetNamespacePrefix, expression);
String value = createReferenceValue(toNode, toNamespacePrefix, expression);
String insertText = request.getInsertAttrValue(value);
item.setLabel(value);
item.setKind(CompletionItemKind.Value);
item.setFilterText(insertText);
Range fullRange = request.getReplaceRange();
item.setTextEdit(Either.forLeft(new TextEdit(fullRange, insertText)));
response.addCompletionItem(item);
});
}
}

private static String createReferenceValue(DOMAttr targetAttr, String targetNamespacePrefix,
private static String createReferenceValue(DOMNode toNode, String toNamespacePrefix,
XMLReferenceExpression expression) {
StringBuilder value = new StringBuilder();
if (expression.getPrefix() != null) {
value.append(expression.getPrefix());
}
if (targetNamespacePrefix != null) {
value.append(targetNamespacePrefix);
if (toNamespacePrefix != null) {
value.append(toNamespacePrefix);
value.append(":");
}
value.append(targetAttr.getValue());
value.append(XMLReferencesUtils.getNodeValue(toNode));
return value.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@

import java.util.List;

import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.extensions.references.XMLReferencesPlugin;
import org.eclipse.lemminx.extensions.references.settings.XMLReferenceExpression;
import org.eclipse.lemminx.extensions.references.utils.XMLReferencesSearchContext;
import org.eclipse.lemminx.extensions.references.utils.XMLReferencesUtils;
import org.eclipse.lemminx.services.extensions.AbstractDefinitionParticipant;
import org.eclipse.lemminx.services.extensions.IDefinitionRequest;
Expand Down Expand Up @@ -47,18 +46,15 @@ protected boolean match(DOMDocument document) {
@Override
protected void doFindDefinition(IDefinitionRequest request, List<LocationLink> locations,
CancelChecker cancelChecker) {
DOMNode node = request.getNode();
if (!node.isAttribute()) {
return;
}
DOMAttr attr = (DOMAttr) node;
List<XMLReferenceExpression> references = XMLReferencesUtils.findExpressionsWhichMatcheFrom(attr,
DOMNode fromNode = request.getNode();
XMLReferencesSearchContext searchContext = XMLReferencesUtils.findExpressionsWhichMatchFrom(fromNode,
plugin.getReferencesSettings());
if (references != null && !references.isEmpty()) {
XMLReferencesUtils.searchToAttributes(attr, references, true, true,
(targetNamespacePrefix, targetAttr, expression) -> {
LocationLink location = XMLPositionUtility.createLocationLink(attr.getNodeAttrValue(),
targetAttr.getNodeAttrValue());
if (searchContext != null) {
XMLReferencesUtils.searchToNodes(fromNode, searchContext, true, true,
(toNamespacePrefix, toNode, expression) -> {
LocationLink location = XMLPositionUtility.createLocationLink(
XMLReferencesUtils.getNodeRange(fromNode),
XMLReferencesUtils.getNodeRange(toNode));
locations.add(location);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@

import java.util.List;

import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.extensions.references.XMLReferencesPlugin;
import org.eclipse.lemminx.extensions.references.settings.XMLReferenceExpression;
import org.eclipse.lemminx.extensions.references.utils.XMLReferencesSearchContext;
import org.eclipse.lemminx.extensions.references.utils.XMLReferencesUtils;
import org.eclipse.lemminx.services.extensions.IHighlightingParticipant;
import org.eclipse.lemminx.utils.XMLPositionUtility;
Expand All @@ -42,24 +41,21 @@ public XMLReferencesHighlightingParticipant(XMLReferencesPlugin plugin) {
@Override
public void findDocumentHighlights(DOMNode node, Position position, int offset, List<DocumentHighlight> highlights,
CancelChecker cancelChecker) {
// Highlight works only when attribute is selected (origin or target attribute)
DOMAttr fromAttr = node.findAttrAt(offset);
if (fromAttr == null || fromAttr.getNodeAttrValue() == null) {
return;
DOMNode fromNode = node;
if (fromNode.isElement()) {
fromNode = fromNode.findAttrAt(offset);
}

List<XMLReferenceExpression> references = XMLReferencesUtils.findExpressionsWhichMatcheFrom(fromAttr,
XMLReferencesSearchContext searchContext = XMLReferencesUtils.findExpressionsWhichMatchFrom(fromNode,
plugin.getReferencesSettings());
if (references != null && !references.isEmpty()) {
if (searchContext != null) {
highlights
.add(new DocumentHighlight(XMLPositionUtility.createRange(fromAttr.getNodeAttrValue().getStart(),
fromAttr.getNodeAttrValue().getEnd(), fromAttr.getOwnerDocument()),
.add(new DocumentHighlight(
XMLPositionUtility.createRange(XMLReferencesUtils.getNodeRange(fromNode)),
DocumentHighlightKind.Read));
XMLReferencesUtils.searchToAttributes(fromAttr, references, true, false,
(targetNamespacePrefix, toAttr, expression) -> {
XMLReferencesUtils.searchToNodes(fromNode, searchContext, true, false,
(toNamespacePrefix, toNode, expression) -> {
highlights.add(new DocumentHighlight(
XMLPositionUtility.createRange(toAttr.getNodeAttrValue().getStart(),
toAttr.getNodeAttrValue().getEnd(), toAttr.getOwnerDocument()),
XMLPositionUtility.createRange(XMLReferencesUtils.getNodeRange(toNode)),
DocumentHighlightKind.Write));
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*******************************************************************************/
package org.eclipse.lemminx.extensions.references.settings;

import org.eclipse.lemminx.xpath.matcher.IXPathNodeMatcher.MatcherType;
import org.eclipse.lemminx.xpath.matcher.XPathMatcher;
import org.w3c.dom.Node;

Expand Down Expand Up @@ -64,17 +65,59 @@ public void setPrefix(String prefix) {
this.prefix = prefix;
}

/**
* Returns true if the given DOM Node match the XPath expression of the 'from'
* XPath matcher and false otherwise.
*
* @param node the DOM Node to match.
*
* @return true if the given DOM Node match the XPath expression of the 'from'
* XPath matcher and false otherwise.
*/
public boolean matchFrom(final Node node) {
return getFromMatcher().match(node);
}

private XPathMatcher getFromMatcher() {
if (fromMatcher == null) {
fromMatcher = new XPathMatcher(from);
}
return fromMatcher.match(node);
return fromMatcher;
}

/**
* Returns true if the given DOM Node match the XPath expression of the 'to'
* XPath matcher and false otherwise.
*
* @param node the DOM Node to match.
*
* @return true if the given DOM Node match the XPath expression of the 'to'
* XPath matcher and false otherwise.
*/
public boolean matchTo(final Node node) {
return getToMatcher().match(node);
}

private XPathMatcher getToMatcher() {
if (toMatcher == null) {
toMatcher = new XPathMatcher(to);
}
return toMatcher.match(node);
return toMatcher;
}

public boolean isFromSearchInAttribute() {
return getFromMatcher().getNodeSelectorType() == MatcherType.ATTRIBUTE;
}

public boolean isFromSearchInText() {
return getFromMatcher().getNodeSelectorType() == MatcherType.TEXT;
}

public boolean isToSearchInAttribute() {
return getToMatcher().getNodeSelectorType() == MatcherType.ATTRIBUTE;
}

public boolean isToSearchInText() {
return getToMatcher().getNodeSelectorType() == MatcherType.TEXT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*******************************************************************************/
package org.eclipse.lemminx.extensions.references.utils;

import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.extensions.references.settings.XMLReferenceExpression;

/**
Expand All @@ -28,9 +28,9 @@ public interface IXMLReferenceTosCollector {
* Collect the given to attribute which matches the given expression.
*
* @param namespacePrefix namespace prefix.
* @param toAttr the to attribute to collect.
* @param toNode the to attribute, text node to collect.
* @param expression the reference expression which matches the to
* attribute.
* node.
*/
void collect(String namespacePrefix, DOMAttr toAttr, XMLReferenceExpression expression);
void collect(String namespacePrefix, DOMNode toNode, XMLReferenceExpression expression);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2023 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
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.references.utils;

import java.util.List;

import org.eclipse.lemminx.extensions.references.settings.XMLReferenceExpression;

/**
* The XML reference searcher.
*
* @author Angelo ZERR
*
*/
public class XMLReferencesSearchContext {

private final List<XMLReferenceExpression> expressions;

private final boolean searchInAttribute;

private final boolean searchInText;

public XMLReferencesSearchContext(List<XMLReferenceExpression> expressions, boolean searchForFromNode) {
this.expressions = expressions;
if (searchForFromNode) {
searchInAttribute = expressions.stream()
.anyMatch(expr -> expr.isFromSearchInAttribute());
searchInText = expressions.stream()
.anyMatch(expr -> expr.isFromSearchInText());
} else {
searchInAttribute = expressions.stream()
.anyMatch(expr -> expr.isToSearchInAttribute());
searchInText = expressions.stream()
.anyMatch(expr -> expr.isToSearchInText());
}
}

/**
* Returns true if the search of nodes must be done in attribute nodes and false
* otherwise.
*
* @return true if the search of nodes must be done in attribute nodes and false
* otherwise.
*/
public boolean isSearchInAttribute() {
return searchInAttribute;
}

/**
* Returns true if the search of nodes must be done in text nodes and false
* otherwise.
*
* @return true if the search of nodes must be done in text nodes and false
* otherwise.
*/
public boolean isSearchInText() {
return searchInText;
}

/**
* Returns the list of reference expressions.
*
* @return the list of reference expressions.
*/
public List<XMLReferenceExpression> getExpressions() {
return expressions;
}
}
Loading

0 comments on commit 3a25dad

Please sign in to comment.