diff options
author | angelozerr | 2018-07-30 14:42:33 +0000 |
---|---|---|
committer | Mickael Istria | 2018-08-02 10:32:08 +0000 |
commit | 1bd95e2e2219228bc87dbff40b1cc0bebf91700a (patch) | |
tree | 785fe97c3e95d8c7647ef4573d96d256d64693f5 | |
parent | 19a965840032cae962561902ca70c1b215b98d8d (diff) | |
download | lsp4e-1bd95e2e2219228bc87dbff40b1cc0bebf91700a.tar.gz lsp4e-1bd95e2e2219228bc87dbff40b1cc0bebf91700a.tar.xz lsp4e-1bd95e2e2219228bc87dbff40b1cc0bebf91700a.zip |
Bug 533322 - [code mining] Support ‘textDocument/documentColor’ with
CodeMining
Change-Id: Icaa99403c00940f215949194440e08d1edd4f19e
Signed-off-by: angelozerr <angelo.zerr@gmail.com>
5 files changed, 331 insertions, 0 deletions
diff --git a/org.eclipse.lsp4e/plugin.xml b/org.eclipse.lsp4e/plugin.xml index 646e1243..c8e366f6 100644 --- a/org.eclipse.lsp4e/plugin.xml +++ b/org.eclipse.lsp4e/plugin.xml @@ -426,6 +426,19 @@ </with> </enabledWhen> </codeMiningProvider> + <codeMiningProvider + class="org.eclipse.lsp4e.operations.color.DocumentColorProvider" + id="org.eclipse.lsp4e.documentColor" + label="Color"> + <enabledWhen> + <with + variable="editorInput"> + <test + property="org.eclipse.lsp4e.hasLanguageServer"> + </test> + </with> + </enabledWhen> + </codeMiningProvider> </extension> <extension point="org.eclipse.ui.genericeditor.reconcilers"> diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java index 9d3e6003..fb2f5728 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java @@ -55,6 +55,7 @@ import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.RewriteSessionEditProcessor; import org.eclipse.jface.text.TextSelection; +import org.eclipse.lsp4j.Color; import org.eclipse.lsp4j.CompletionParams; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.Location; @@ -73,6 +74,8 @@ import org.eclipse.lsp4j.jsonrpc.validation.NonNull; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.DocumentChange; import org.eclipse.ltk.core.refactoring.PerformChangeOperation; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.RGBA; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; @@ -610,4 +613,31 @@ public class LSPEclipseUtils { } return null; } + + /** + * Convert the given Eclipse <code>rgb</code> instance to a LSP {@link Color} + * instance. + * + * @param rgb + * the rgb instance to convert + * @return the given Eclipse <code>rgb</code> instance to a LSP {@link Color} + * instance. + */ + public static Color toColor(RGB rgb) { + return new Color(rgb.red / 255d, rgb.green / 255d, rgb.blue / 255d, 1); + } + + /** + * Convert the given LSP <code>color</code> instance to a Eclipse {@link RGBA} + * instance. + * + * @param rgb + * the color instance to convert + * @return the given LSP <code>color</code> instance to a Eclipse {@link RGBA} + * instance. + */ + public static RGBA toRGBA(Color color) { + return new RGBA((int) (color.getRed() * 255), (int) (color.getGreen() * 255), (int) (color.getBlue() * 255), + (int) color.getAlpha()); + } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java index fbaaaf57..02ca6919 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java @@ -61,6 +61,7 @@ import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.CodeActionCapabilities; import org.eclipse.lsp4j.CodeActionLiteralSupportCapabilities; import org.eclipse.lsp4j.CodeLensCapabilities; +import org.eclipse.lsp4j.ColorProviderCapabilities; import org.eclipse.lsp4j.CompletionCapabilities; import org.eclipse.lsp4j.CompletionItemCapabilities; import org.eclipse.lsp4j.DefinitionCapabilities; @@ -235,6 +236,7 @@ public class LanguageServerWrapper { codeActionCapabilities.setCodeActionLiteralSupport(new CodeActionLiteralSupportCapabilities()); textDocumentClientCapabilities.setCodeAction(codeActionCapabilities); textDocumentClientCapabilities.setCodeLens(new CodeLensCapabilities()); + textDocumentClientCapabilities.setColorProvider(new ColorProviderCapabilities()); textDocumentClientCapabilities.setCompletion(new CompletionCapabilities(new CompletionItemCapabilities(Boolean.TRUE))); textDocumentClientCapabilities.setDefinition(new DefinitionCapabilities()); textDocumentClientCapabilities.setDocumentHighlight(new DocumentHighlightCapabilities()); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/ColorInformationMining.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/ColorInformationMining.java new file mode 100644 index 00000000..22531381 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/ColorInformationMining.java @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2018 Angelo ZERR. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Angelo Zerr <angelo.zerr@gmail.com> - [code mining] Support 'textDocument/documentColor' with CodeMining - Bug 533322 + */ +package org.eclipse.lsp4e.operations.color; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.codemining.LineContentCodeMining; +import org.eclipse.jface.util.Geometry; +import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4e.LanguageServerPlugin; +import org.eclipse.lsp4j.ColorInformation; +import org.eclipse.lsp4j.ColorPresentationParams; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.RGBA; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.ColorDialog; +import org.eclipse.swt.widgets.Shell; + +/** + * Draw the LSP color information with a colorized square. + * + */ +public class ColorInformationMining extends LineContentCodeMining { + + private final RGBA rgba; + private final DocumentColorProvider colorProvider; + + /** + * Click on colorized square opens a color dialog to pick a color and update + * text color declaration. + * + */ + private static class UpdateColorWithDialog implements Consumer<MouseEvent> { + + private final TextDocumentIdentifier textDocumentIdentifier; + private final ColorInformation colorInformation; + private final CompletableFuture<LanguageServer> languageServer; + private final IDocument document; + + public UpdateColorWithDialog(TextDocumentIdentifier textDocumentIdentifier, ColorInformation colorInformation, + CompletableFuture<LanguageServer> languageServer, IDocument document) { + this.textDocumentIdentifier = textDocumentIdentifier; + this.colorInformation = colorInformation; + this.languageServer = languageServer; + this.document = document; + } + + @Override + public void accept(MouseEvent event) { + StyledText styledText = (StyledText) event.widget; + Shell shell = new Shell(styledText.getDisplay()); + Rectangle location = Geometry.toDisplay(styledText, new Rectangle(event.x, event.y, 1, 1)); + shell.setLocation(location.x, location.y); + // Open color dialog + ColorDialog dialog = new ColorDialog(shell); + dialog.setRGB(LSPEclipseUtils.toRGBA(colorInformation.getColor()).rgb); + RGB rgb = dialog.open(); + if (rgb != null) { + // get LSP color presentation list for the picked color + ColorPresentationParams params = new ColorPresentationParams(textDocumentIdentifier, + LSPEclipseUtils.toColor(rgb), colorInformation.getRange()); + this.languageServer.thenCompose( + ls -> ls.getTextDocumentService().colorPresentation(params).thenAccept(presentations -> { + if (presentations.isEmpty()) { + return; + } + // As ColorDialog cannot be customized (to choose the color presentation (rgb, + // hexa, ....) we pick the first color presentation. + try { + TextEdit textEdit = presentations.get(0).getTextEdit(); + LSPEclipseUtils.applyEdit(textEdit, document); + } catch (BadLocationException e) { + LanguageServerPlugin.logError(e); + } + })); + } + } + + } + + public ColorInformationMining(ColorInformation colorInformation, @NonNull IDocument document, + TextDocumentIdentifier textDocumentIdentifier, CompletableFuture<LanguageServer> languageServer, + DocumentColorProvider colorProvider) throws BadLocationException { + super(toPosition(colorInformation.getRange(), document), colorProvider, + new UpdateColorWithDialog(textDocumentIdentifier, colorInformation, languageServer, document)); + this.rgba = LSPEclipseUtils.toRGBA(colorInformation.getColor()); + this.colorProvider = colorProvider; + // set label with space to mark the mining as resolved. + super.setLabel(" "); //$NON-NLS-1$ + } + + @Override + public Point draw(GC gc, StyledText textWidget, Color color, int x, int y) { + FontMetrics fontMetrics = gc.getFontMetrics(); + // Compute position and size of the color square + int size = getSquareSize(fontMetrics); + x += fontMetrics.getLeading(); + y += fontMetrics.getDescent(); + Rectangle rect = new Rectangle(x, y, size, size); + // Fill square + gc.setBackground(colorProvider.getColor(this.rgba, textWidget.getDisplay())); + gc.fillRectangle(rect); + // Draw square box + gc.setForeground(textWidget.getForeground()); + gc.drawRectangle(rect); + return new Point(getSquareWidth(fontMetrics), size); + } + + /** + * Returns the colorized square size. + * + * @param fontMetrics + * @return the colorized square size. + */ + private static int getSquareSize(FontMetrics fontMetrics) { + return fontMetrics.getHeight() - 2 * fontMetrics.getDescent(); + } + + /** + * Compute width of square + * + * @param styledText + * @return the width of square + */ + private static int getSquareWidth(FontMetrics fontMetrics) { + // width = 1 space + size width of square + int width = (int) fontMetrics.getAverageCharacterWidth() + getSquareSize(fontMetrics); + return width; + } + + /** + * Returns the Eclipse position from the given LSP range. + * + * @param range + * the LSP range to convert + * @param document + * @return the Eclipse position from the given LSP range. + * @throws BadLocationException + */ + private static Position toPosition(Range range, IDocument document) throws BadLocationException { + int start = LSPEclipseUtils.toOffset(range.getStart(), document); + int end = LSPEclipseUtils.toOffset(range.getEnd(), document); + return new Position(start, end - start); + } +} diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/DocumentColorProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/DocumentColorProvider.java new file mode 100644 index 00000000..f4a2ded8 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/DocumentColorProvider.java @@ -0,0 +1,118 @@ +/**
+ * Copyright (c) 2018 Angelo ZERR.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Angelo Zerr <angelo.zerr@gmail.com> - [code mining] Support 'textDocument/documentColor' with CodeMining - Bug 533322
+ */
+package org.eclipse.lsp4e.operations.color;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider;
+import org.eclipse.jface.text.codemining.ICodeMining;
+import org.eclipse.lsp4e.LanguageServerPlugin;
+import org.eclipse.lsp4e.LanguageServiceAccessor;
+import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
+import org.eclipse.lsp4j.ColorInformation;
+import org.eclipse.lsp4j.DocumentColorParams;
+import org.eclipse.lsp4j.ServerCapabilities;
+import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGBA;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Consume the 'textDocument/documentColor' request to decorate color references
+ * in the editor.
+ *
+ */
+public class DocumentColorProvider extends AbstractCodeMiningProvider {
+
+ private final Map<RGBA, Color> colorTable;
+
+ public DocumentColorProvider() {
+ colorTable = new HashMap<>();
+ }
+
+ private CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(@NonNull IDocument document) {
+ return CompletableFuture.supplyAsync(() -> {
+ List<@NonNull LSPDocumentInfo> docInfos = LanguageServiceAccessor.getLSPDocumentInfosFor(document,
+ (capabilities) -> (capabilities.getColorProvider() != null
+ && ((capabilities.getColorProvider().getLeft() != null
+ && capabilities.getColorProvider().getLeft())
+ || capabilities.getColorProvider().getRight() != null)));
+ final CompletableFuture<?>[] requests = new CompletableFuture<?>[docInfos.size()];
+ final List<ColorInformationMining> colorResults = Collections
+ .synchronizedList(new ArrayList<>(docInfos.size()));
+ for (int i = 0; i < docInfos.size(); i++) {
+ final LSPDocumentInfo info = docInfos.get(i);
+ final ServerCapabilities capabilites = info.getCapabilites();
+ final TextDocumentIdentifier textDocumentIdentifier = new TextDocumentIdentifier(
+ info.getFileUri().toString());
+ DocumentColorParams param = new DocumentColorParams(textDocumentIdentifier);
+ requests[i] = info.getInitializedLanguageClient()
+ .thenCompose(languageServer -> languageServer.getTextDocumentService().documentColor(param))
+ .thenAccept(colors -> {
+ for (ColorInformation color : colors) {
+ if (color != null && capabilites != null) {
+ try {
+ colorResults.add(new ColorInformationMining(color, document,
+ textDocumentIdentifier, info.getInitializedLanguageClient(),
+ DocumentColorProvider.this));
+ } catch (BadLocationException e) {
+ LanguageServerPlugin.logError(e);
+ }
+ }
+ }
+ });
+ }
+ CompletableFuture.allOf(requests).join();
+ return colorResults;
+ });
+ }
+
+ @Override
+ public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer,
+ IProgressMonitor monitor) {
+ return provideCodeMinings(viewer.getDocument());
+ }
+
+ /**
+ * Returns the color from the given rgba.
+ *
+ * @param rgba
+ * the rgba declaration
+ * @param display
+ * the display to use to create a color instance
+ * @return the color from the given rgba.
+ */
+ public Color getColor(RGBA rgba, Display display) {
+ Color color = colorTable.get(rgba);
+ if (color == null) {
+ color = new Color(display, rgba);
+ colorTable.put(rgba, color);
+ }
+ return color;
+ }
+
+ @Override
+ public void dispose() {
+ colorTable.values().forEach(color -> color.dispose());
+ super.dispose();
+ }
+
+}
|