From 7d0dd17b09240385333805637ea17992a8089cf2 Mon Sep 17 00:00:00 2001
From: vsrs <vit@conrlab.com>
Date: Wed, 3 Jun 2020 14:15:54 +0300
Subject: Add hover actions as LSP extension

---
 editors/code/src/client.ts  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 editors/code/src/config.ts  | 13 +++++++++----
 editors/code/src/lsp_ext.ts | 12 ++++++++++++
 3 files changed, 66 insertions(+), 4 deletions(-)

(limited to 'editors/code/src')

diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 40ad1e3cd..9df670283 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -7,6 +7,29 @@ import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.pr
 import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
 import { assert } from './util';
 
+function toTrusted(obj: vscode.MarkedString): vscode.MarkedString {
+    const md = <vscode.MarkdownString>obj;
+    if (md && md.value.includes("```rust")) {
+        md.isTrusted = true;
+        return md;
+    }
+    return obj;
+}
+
+function renderCommand(cmd: CommandLink) {
+    return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`;
+}
+
+function renderHoverActions(actions: CommandLinkGroup[]): vscode.MarkdownString {
+    const text = actions.map(group =>
+        (group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ')
+    ).join('___');
+
+    const result = new vscode.MarkdownString(text);
+    result.isTrusted = true;
+    return result;
+}
+
 export function createClient(serverPath: string, cwd: string): lc.LanguageClient {
     // '.' Is the fallback if no folder is open
     // TODO?: Workspace folders support Uri's (eg: file://test.txt).
@@ -35,6 +58,27 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
                 if (res === undefined) throw new Error('busy');
                 return res;
             },
+            async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) {
+                return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then(
+                    (result) => {
+                        const hover = client.protocol2CodeConverter.asHover(result);
+                        if (hover) {
+                            // Workaround to support command links (trusted vscode.MarkdownString) in hovers
+                            // https://github.com/microsoft/vscode/issues/33577
+                            hover.contents = hover.contents.map(toTrusted);
+                            
+                            const actions = (<any>result).actions;
+                            if (actions) {
+                                hover.contents.push(renderHoverActions(actions));
+                            }
+                        }
+                        return hover;
+                    },
+                    (error) => {
+                        client.logFailedRequest(lc.HoverRequest.type, error);
+                        return Promise.resolve(null);
+                    });
+            },
             // Using custom handling of CodeActions where each code action is resloved lazily
             // That's why we are not waiting for any command or edits
             async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
@@ -129,6 +173,7 @@ class ExperimentalFeatures implements lc.StaticFeature {
         caps.snippetTextEdit = true;
         caps.codeActionGroup = true;
         caps.resolveCodeAction = true;
+        caps.hoverActions = true;
         capabilities.experimental = caps;
     }
     initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index e8abf8284..d8f0037d4 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -16,10 +16,8 @@ export class Config {
         "files",
         "highlighting",
         "updates.channel",
-        "lens.enable",
-        "lens.run",
-        "lens.debug",
-        "lens.implementations",
+        "lens", // works as lens.*
+        "hoverActions", // works as hoverActions.*
     ]
         .map(opt => `${this.rootSection}.${opt}`);
 
@@ -132,4 +130,11 @@ export class Config {
             implementations: this.get<boolean>("lens.implementations"),
         };
     }
+
+    get hoverActions() {
+        return {
+            enable: this.get<boolean>("hoverActions.enable"),
+            implementations: this.get<boolean>("hoverActions.implementations"),
+        };
+    }
 }
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 9793b926c..e16ea799c 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -90,3 +90,15 @@ export interface SsrParams {
     parseOnly: boolean;
 }
 export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr');
+
+export interface CommandLink extends lc.Command {
+    /**
+     * A tooltip for the command, when represented in the UI.
+     */
+    tooltip?: string;
+}
+
+export interface CommandLinkGroup {
+    title?: string;
+    commands: CommandLink[];
+}
-- 
cgit v1.2.3