From cdf2e9fa4427a2532d6f1378af362c57d4d36379 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 3 Oct 2017 16:27:58 -0700 Subject: [PATCH] Add support for 'sts/highlight' protocol extension Use it too highlight occurrences of the word 'foo' as an example of how to use this. --- .../boot/java/BootJavaLanguageServer.java | 1 + .../ide/vscode/boot/java/WordHighlighter.java | 58 ++++++++++++++++++ .../languageserver/HighlightParams.java | 44 +++++++++++++ .../languageserver/STS4LanguageClient.java | 3 + .../util/SimpleLanguageServer.java | 12 +++- .../testharness/LanguageServerHarness.java | 7 ++- .../commons-vscode/icons/boot-icon.png | Bin 0 -> 617 bytes vscode-extensions/commons-vscode/package.json | 2 +- .../commons-vscode/src/highlight-service.ts | 55 +++++++++++++++++ .../commons-vscode/src/launch-util.ts | 9 ++- 10 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/WordHighlighter.java create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/HighlightParams.java create mode 100644 vscode-extensions/commons-vscode/icons/boot-icon.png create mode 100644 vscode-extensions/commons-vscode/src/highlight-service.ts diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index f9b7ba00f..bbe8c4399 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -92,6 +92,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { documents.onDidChangeContent(params -> { TextDocument doc = params.getDocument(); validateWith(doc.getId(), reconcileEngine); + highlighWith(doc, new WordHighlighter(this, "foo")); }); ICompletionEngine bootCompletionEngine = createCompletionEngine(javaProjectFinder, indexProvider); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/WordHighlighter.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/WordHighlighter.java new file mode 100644 index 000000000..c089d78ff --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/WordHighlighter.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java; + +import java.util.List; +import java.util.function.Function; + +import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.languageserver.util.TextDocumentContentChange; +import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +import com.google.common.collect.ImmutableList; + +/** + * Sample implementation of a 'highlighter' that can be used with {@link SimpleLanguageServer}.highlightWith. + *

+ * Finds every occurence of a given word and highlights it. + * + * @author Kris De Volder + */ +public class WordHighlighter implements Function> { + + private final SimpleLanguageServer server; + private final String word; + + public WordHighlighter(SimpleLanguageServer server, String word) { + super(); + this.server = server; + this.word = word; + } + + @Override + public List apply(TextDocument doc) { + String text = doc.get(); + int wordStart = text.indexOf(word); + ImmutableList.Builder highlights = ImmutableList.builder(); + while (wordStart>=0) { + try { + highlights.add(doc.toRange(wordStart, word.length())); + } catch (BadLocationException e) { + Log.log(e); + } + wordStart = text.indexOf(word, wordStart+word.length()); + } + return highlights.build(); + } +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/HighlightParams.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/HighlightParams.java new file mode 100644 index 000000000..bf812f99a --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/HighlightParams.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver; + +import java.util.List; + +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextDocumentIdentifier; + +public class HighlightParams { + + private TextDocumentIdentifier doc; + private List ranges; + + public HighlightParams() { + } + + public HighlightParams(TextDocumentIdentifier doc, List ranges) { + super(); + this.doc = doc; + this.ranges = ranges; + } + public TextDocumentIdentifier getDoc() { + return doc; + } + public void setDoc(TextDocumentIdentifier doc) { + this.doc = doc; + } + public List getRanges() { + return ranges; + } + public void setRanges(List ranges) { + this.ranges = ranges; + } + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java index 4830dff83..2d7892023 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java @@ -25,6 +25,9 @@ import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEd */ public interface STS4LanguageClient extends LanguageClient { + @JsonNotification("sts/highlight") + void highlight(HighlightParams highlights); + @JsonNotification("sts/progress") void progress(ProgressParams progressEvent); diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 3b7f3bfa9..4c7fd0cd3 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -17,6 +17,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; import org.eclipse.lsp4j.ApplyWorkspaceEditParams; import org.eclipse.lsp4j.ApplyWorkspaceEditResponse; @@ -37,6 +38,7 @@ import org.eclipse.lsp4j.TextDocumentSyncKind; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageClientAware; import org.eclipse.lsp4j.services.LanguageServer; +import org.springframework.ide.vscode.commons.languageserver.HighlightParams; import org.springframework.ide.vscode.commons.languageserver.ProgressParams; import org.springframework.ide.vscode.commons.languageserver.ProgressService; import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; @@ -447,7 +449,7 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl } } - public LanguageClient getClient() { + public STS4LanguageClient getClient() { return client; } @@ -460,5 +462,13 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl testListener = languageServerTestListener; } + protected void highlighWith(TextDocument doc, Function> wordHighlighter) { + List highlights = wordHighlighter.apply(doc); + if (highlights==null) { + highlights = ImmutableList.of(); + } + client.highlight(new HighlightParams(doc.getId(), highlights)); + } + } diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index 07830f3b0..7ba98f330 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -75,6 +75,7 @@ import org.eclipse.lsp4j.WorkspaceClientCapabilities; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClientAware; +import org.springframework.ide.vscode.commons.languageserver.HighlightParams; import org.springframework.ide.vscode.commons.languageserver.ProgressParams; import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; @@ -232,7 +233,6 @@ public class LanguageServerHarness { @Override public void progress(ProgressParams progressEvent) { // TODO Auto-generated method stub - } @Override @@ -245,6 +245,11 @@ public class LanguageServerHarness { } return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false)); } + + @Override + public void highlight(HighlightParams highlights) { + // TODO Auto-generated method stub + } }); } diff --git a/vscode-extensions/commons-vscode/icons/boot-icon.png b/vscode-extensions/commons-vscode/icons/boot-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..de88ef0d5ceb61e6e6b141349ae5269c9187a59a GIT binary patch literal 617 zcmV-v0+#)WP)|WK~y-6rIWu)TTv9pKj+1~MB|H5O0)AYOXdvzXI;@+8$XypN@4-;fb1CtKd>3s`+DR9R`50! zjx7bnBBCPF3Q#UZz3B7*CXkX0Sz^_U8fP#zNO!oKVP}}kP=?J z(YKumibjni2wS(d7)gvUHZaC$|0tc0z7VOL7s^NnMt+9w*Hc05(2ukQ>hh+g}2I zj7r)^Tc$|Rz_AA$OZ*xvrV%1H*N%;nCo9KG(J>ctk z21d; + + dispose() { + this.DECORATION.dispose(); + } + + constructor() { + this.DECORATION = VSCode.window.createTextEditorDecorationType({ + // textDecoration: "underline", + gutterIconPath: path.resolve(__dirname, "../icons/boot-icon.png"), + gutterIconSize: "contain", + outline: "#32BA56 dotted thin" + }); + this.highlights = new Map(); + } + + handle(params : HighlightParams) : void { + this.highlights.set(params.doc.uri, params.ranges); + this.refresh(params.doc.uri); + } + + refresh(uri : String) { + let editor = VSCode.window.activeTextEditor; + let activeUri = editor.document.uri.toString(); + console.log("Refreshing uri :" + uri); + console.log(" active editor uri :" + editor.document.uri); + if (uri===activeUri) { + //We only update highlights in the active editor for now + let highlights : Range[] = this.highlights.get(uri) || []; + let decorations = highlights.map(hl => toDecoration(hl)); + editor.setDecorations(this.DECORATION, decorations); + } + } +} \ No newline at end of file diff --git a/vscode-extensions/commons-vscode/src/launch-util.ts b/vscode-extensions/commons-vscode/src/launch-util.ts index a465ad859..315500148 100644 --- a/vscode-extensions/commons-vscode/src/launch-util.ts +++ b/vscode-extensions/commons-vscode/src/launch-util.ts @@ -10,11 +10,12 @@ import * as FS from 'fs'; import PortFinder = require('portfinder'); import * as Net from 'net'; import * as ChildProcess from 'child_process'; -import { RequestType, LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo } from 'vscode-languageclient'; +import { TextDocumentIdentifier, RequestType, LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo, Range } from 'vscode-languageclient'; import { TextDocument, OutputChannel, Disposable, window } from 'vscode'; import { Trace, NotificationType } from 'vscode-jsonrpc'; import * as P2C from 'vscode-languageclient/lib/protocolConverter'; import {WorkspaceEdit, Position} from 'vscode-languageserver-types'; +import {HighlightService, HighlightParams} from './highlight-service'; let p2c = P2C.createConverter(); @@ -145,17 +146,23 @@ function setupLanguageClient(context: VSCode.ExtensionContext, createServer: Ser } let progressNotification = new NotificationType("sts/progress"); + let highlightNotification = new NotificationType("sts/highlight"); let moveCursorRequest = new RequestType("sts/moveCursor"); let disposable = client.start(); let progressService = new ProgressService(); + let highlightService = new HighlightService(); context.subscriptions.push(disposable); context.subscriptions.push(progressService); + context.subscriptions.push(highlightService); return client.onReady().then(() => { client.onNotification(progressNotification, (params: ProgressParams) => { progressService.handle(params); }); + client.onNotification(highlightNotification, (params: HighlightParams) => { + highlightService.handle(params); + }); client.onRequest(moveCursorRequest, (params: MoveCursorParams) => { let editors = VSCode.window.visibleTextEditors; for (let editor of editors) {