Skip to content

Commit

Permalink
Support for textDocument/documentColor
Browse files Browse the repository at this point in the history
Fixes #639

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
azerr authored and JessicaJHee committed Jan 5, 2023
1 parent 84a5962 commit 771ce44
Show file tree
Hide file tree
Showing 20 changed files with 1,274 additions and 4 deletions.
Expand Up @@ -58,6 +58,9 @@
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.CodeLensParams;
import org.eclipse.lsp4j.ColorInformation;
import org.eclipse.lsp4j.ColorPresentation;
import org.eclipse.lsp4j.ColorPresentationParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionList;
Expand All @@ -69,6 +72,7 @@
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.DocumentColorParams;
import org.eclipse.lsp4j.DocumentFormattingParams;
import org.eclipse.lsp4j.DocumentHighlight;
import org.eclipse.lsp4j.DocumentHighlightParams;
Expand Down Expand Up @@ -553,6 +557,20 @@ public CompletableFuture<LinkedEditingRanges> linkedEditingRange(LinkedEditingRa
});
}

@Override
public CompletableFuture<List<ColorInformation>> documentColor(DocumentColorParams params) {
return computeDOMAsync(params.getTextDocument(), (xmlDocument, cancelChecker) -> {
return getXMLLanguageService().findDocumentColors(xmlDocument, cancelChecker);
});
}

@Override
public CompletableFuture<List<ColorPresentation>> colorPresentation(ColorPresentationParams params) {
return computeDOMAsync(params.getTextDocument(), (xmlDocument, cancelChecker) -> {
return getXMLLanguageService().getColorPresentations(xmlDocument, params, cancelChecker);
});
}

@Override
public void didSave(DidSaveTextDocumentParams params) {
computeAsync((monitor) -> {
Expand Down
@@ -0,0 +1,98 @@
/*******************************************************************************
* 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.colors;

import org.eclipse.lemminx.extensions.colors.participants.XMLDocumentColorParticipant;
import org.eclipse.lemminx.extensions.colors.settings.XMLColorsSettings;
import org.eclipse.lemminx.services.extensions.IDocumentColorParticipant;
import org.eclipse.lemminx.services.extensions.IXMLExtension;
import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry;
import org.eclipse.lemminx.services.extensions.save.ISaveContext;
import org.eclipse.lsp4j.InitializeParams;

/**
* XML colors plugin.
*
* This plugin provides the capability to support color with 'xml.colors'
* settings.
*
* <code>
"xml.colors": [
// XML colors applied for text node for android colors.xml files
{
"pattern": "/res/values/colors.xml",
"expressions": [
{
"path": "resources/color/text()"
}
]
},
// XML colors applied for @color attribute for another files
{
"pattern": "/my-colors.xml",
"expressions": [
{
"path": "@color"
}
]
}
]
*
* </code>
*
*
* @author Angelo ZERR
*
*/
public class XMLColorsPlugin implements IXMLExtension {

private final IDocumentColorParticipant documentColorParticipant;

private XMLColorsSettings colorsSettings;

public XMLColorsPlugin() {
documentColorParticipant = new XMLDocumentColorParticipant(this);
}

@Override
public void doSave(ISaveContext context) {
if (context.getType() != ISaveContext.SaveContextType.DOCUMENT) {
// Settings
updateSettings(context);
}
}

private void updateSettings(ISaveContext saveContext) {
Object initializationOptionsSettings = saveContext.getSettings();
XMLColorsSettings referencesSettings = XMLColorsSettings
.getXMLColorsSettings(initializationOptionsSettings);
updateSettings(referencesSettings, saveContext);
}

private void updateSettings(XMLColorsSettings settings, ISaveContext context) {
this.colorsSettings = settings;
}

@Override
public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerDocumentColorParticipant(documentColorParticipant);
}

@Override
public void stop(XMLExtensionsRegistry registry) {
registry.unregisterDocumentColorParticipant(documentColorParticipant);
}

public XMLColorsSettings getColorsSettings() {
return colorsSettings;
}
}
@@ -0,0 +1,177 @@
/*******************************************************************************
* 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.colors.participants;

import static org.eclipse.lemminx.extensions.colors.utils.ColorUtils.getColorValue;
import static org.eclipse.lemminx.extensions.colors.utils.ColorUtils.toHexa;
import static org.eclipse.lemminx.extensions.colors.utils.ColorUtils.toRGB;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.dom.DOMText;
import org.eclipse.lemminx.extensions.colors.XMLColorsPlugin;
import org.eclipse.lemminx.extensions.colors.settings.XMLColorExpression;
import org.eclipse.lemminx.extensions.colors.settings.XMLColors;
import org.eclipse.lemminx.extensions.colors.settings.XMLColorsSettings;
import org.eclipse.lemminx.services.extensions.IDocumentColorParticipant;
import org.eclipse.lemminx.utils.XMLPositionUtility;
import org.eclipse.lsp4j.Color;
import org.eclipse.lsp4j.ColorInformation;
import org.eclipse.lsp4j.ColorPresentation;
import org.eclipse.lsp4j.ColorPresentationParams;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;

/**
* XML document color particpant based on the {@link XMLColorsSettings}.
*
* @author Angelo ZERR
*
*/
public class XMLDocumentColorParticipant implements IDocumentColorParticipant {

private final XMLColorsPlugin xmlColorsPlugin;

public XMLDocumentColorParticipant(XMLColorsPlugin xmlColorsPlugin) {
this.xmlColorsPlugin = xmlColorsPlugin;
}

@Override
public void doDocumentColor(DOMDocument xmlDocument, List<ColorInformation> colors, CancelChecker cancelChecker) {
List<XMLColorExpression> expressions = findColorExpression(xmlDocument);
if (expressions.isEmpty()) {
return;
}
doDocumentColor(xmlDocument, expressions, colors, cancelChecker);
}

private void doDocumentColor(DOMNode node, List<XMLColorExpression> expressions, List<ColorInformation> colors,
CancelChecker cancelChecker) {
if (node.isElement()) {
DOMElement element = (DOMElement) node;
if (element.hasAttributes()) {
List<DOMAttr> attributes = element.getAttributeNodes();
for (DOMAttr attr : attributes) {
if (isColorNode(attr, expressions)) {
// The current attribute node matches an XML color expression declared in the
// "xml/colors"
// settings
// ex :
// - xpath="foo/@color"
Color color = getColorValue(attr.getValue());
if (color != null) {
Range range = XMLPositionUtility.selectAttributeValue(attr, true);
ColorInformation colorInformation = new ColorInformation(range, color);
colors.add(colorInformation);
}
}
}
}
} else if (node.isText()) {
if (isColorNode(node, expressions)) {
// The current text node matches an XML color expression declared in the
// "xml/colors"
// settings
// ex :
// - xpath="resources/color/text()"
DOMText text = (DOMText) node;
Color color = getColorValue(text.getData());
if (color != null) {
Range range = XMLPositionUtility.selectText(text);
ColorInformation colorInformation = new ColorInformation(range, color);
colors.add(colorInformation);
}
}
}
List<DOMNode> children = node.getChildren();
for (DOMNode child : children) {
cancelChecker.checkCanceled();
doDocumentColor(child, expressions, colors, cancelChecker);
}
}

@Override
public void doColorPresentations(DOMDocument xmlDocument, ColorPresentationParams params,
List<ColorPresentation> presentations, CancelChecker cancelChecker) {
Color color = params.getColor();
Range replace = params.getRange();
// RGB color presentation
presentations.add(toRGB(color, replace));
// Hexa color presentation
presentations.add(toHexa(color, replace));
}

/**
* Returns true if the given <code>node>code> matches an XML color expression
* and false otherwise.
*
* @param node the node to match.
* @param expressions XML color expressions.
*
* @return true if the given <code>node>code> matches an XML color expression
* and false otherwise.
*/
private static boolean isColorNode(DOMNode node, List<XMLColorExpression> expressions) {
if (node.isAttribute()) {
DOMAttr attr = (DOMAttr) node;
if (attr.getValue() == null && attr.getValue().isEmpty()) {
return false;
}
} else if (node.isText()) {
DOMText text = (DOMText) node;
if (!text.hasData()) {
return false;
}
}
for (XMLColorExpression expression : expressions) {
if (expression.match(node)) {
return true;
}
}
return false;
}

/**
* Return the list of {@link XMLColorExpression} for the given document and an
* empty list otherwise.
*
* @param xmlDocument the DOM document
*
* @return the list of {@link XMLColorExpression} for the given document and an
* empty list otherwise.
*/
private List<XMLColorExpression> findColorExpression(DOMDocument xmlDocument) {
XMLColorsSettings settings = xmlColorsPlugin.getColorsSettings();
if (settings == null) {
return Collections.emptyList();
}

List<XMLColors> colorsDef = settings.getColors();
if (colorsDef == null) {
return Collections.emptyList();
}
List<XMLColorExpression> expressions = new ArrayList<>();
for (XMLColors xmlColors : colorsDef) {
if (xmlColors.matches(xmlDocument.getDocumentURI())) {
expressions.addAll(xmlColors.getExpressions());
}
}
return expressions;
}

}
@@ -0,0 +1,53 @@
/*******************************************************************************
* 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.colors.settings;

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

/**
* XML colors expression
*
* <code>
* {
"xpath": "@color"
}
* </code>
*
* @author Angelo ZERR
*
*/
public class XMLColorExpression {

private transient XPathMatcher pathMatcher;

private String xpath;

public String getXPath() {
return xpath;
}

public void setXPath(String xpath) {
this.xpath = xpath;
}

public boolean match(final Node node) {
if (xpath == null) {
return false;
}
if (pathMatcher == null) {
pathMatcher = new XPathMatcher(xpath);
}
return pathMatcher.match(node);
}

}

0 comments on commit 771ce44

Please sign in to comment.