aboutsummaryrefslogtreecommitdiff
path: root/editors/code
diff options
context:
space:
mode:
authorBenjamin Coenen <[email protected]>2020-05-21 09:53:29 +0100
committerBenjamin Coenen <[email protected]>2020-05-21 09:53:29 +0100
commita7c8aa7c60c05db66ba4e89ae9e05c82e62507a5 (patch)
treee848f47bdf5d031c408df94222f595d2efcb2070 /editors/code
parentc6143742bd4e625d391ac3ea860be7578ab9f53f (diff)
parenta4e6963a2313971fe7bbec97d03bc67266ef68a9 (diff)
add support of feature flag for runnables #4464
Signed-off-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/package.json27
-rw-r--r--editors/code/src/cargo.ts12
-rw-r--r--editors/code/src/client.ts65
-rw-r--r--editors/code/src/commands/index.ts35
-rw-r--r--editors/code/src/commands/runnables.ts17
-rw-r--r--editors/code/src/config.ts13
-rw-r--r--editors/code/src/main.ts1
7 files changed, 156 insertions, 14 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index 4e7e3faf7..d899f60e3 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
21 "Programming Languages" 21 "Programming Languages"
22 ], 22 ],
23 "engines": { 23 "engines": {
24 "vscode": "^1.45.0" 24 "vscode": "^1.44.0"
25 }, 25 },
26 "enableProposedApi": true, 26 "enableProposedApi": true,
27 "scripts": { 27 "scripts": {
@@ -41,7 +41,7 @@
41 "@rollup/plugin-node-resolve": "^7.1.3", 41 "@rollup/plugin-node-resolve": "^7.1.3",
42 "@types/node": "^12.12.39", 42 "@types/node": "^12.12.39",
43 "@types/node-fetch": "^2.5.7", 43 "@types/node-fetch": "^2.5.7",
44 "@types/vscode": "^1.45.0", 44 "@types/vscode": "^1.44.0",
45 "@typescript-eslint/eslint-plugin": "^2.33.0", 45 "@typescript-eslint/eslint-plugin": "^2.33.0",
46 "@typescript-eslint/parser": "^2.33.0", 46 "@typescript-eslint/parser": "^2.33.0",
47 "eslint": "^6.8.0", 47 "eslint": "^6.8.0",
@@ -443,6 +443,26 @@
443 "type": "object", 443 "type": "object",
444 "default": {}, 444 "default": {},
445 "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }" 445 "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }"
446 },
447 "rust-analyzer.lens.enable": {
448 "description": "Whether to show CodeLens in Rust files.",
449 "type": "boolean",
450 "default": true
451 },
452 "rust-analyzer.lens.run": {
453 "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
454 "type": "boolean",
455 "default": true
456 },
457 "rust-analyzer.lens.debug": {
458 "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
459 "type": "boolean",
460 "default": true
461 },
462 "rust-analyzer.lens.implementations": {
463 "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
464 "type": "boolean",
465 "default": true
446 } 466 }
447 } 467 }
448 }, 468 },
@@ -604,6 +624,9 @@
604 { 624 {
605 "language": "rust", 625 "language": "rust",
606 "scopes": { 626 "scopes": {
627 "macro": [
628 "entity.name.function.macro.rust"
629 ],
607 "attribute": [ 630 "attribute": [
608 "meta.attribute.rust" 631 "meta.attribute.rust"
609 ], 632 ],
diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts
index 28c7de992..6a41873d0 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/cargo.ts
@@ -51,10 +51,14 @@ export class Cargo {
51 51
52 // arguments for a runnable from the quick pick should be updated. 52 // arguments for a runnable from the quick pick should be updated.
53 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens 53 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
54 if (cargoArgs[0] === "run") { 54 switch (cargoArgs[0]) {
55 cargoArgs[0] = "build"; 55 case "run": cargoArgs[0] = "build"; break;
56 } else if (cargoArgs.indexOf("--no-run") === -1) { 56 case "test": {
57 cargoArgs.push("--no-run"); 57 if (cargoArgs.indexOf("--no-run") === -1) {
58 cargoArgs.push("--no-run");
59 }
60 break;
61 }
58 } 62 }
59 63
60 let artifacts = await this.artifactsFromArgs(cargoArgs); 64 let artifacts = await this.artifactsFromArgs(cargoArgs);
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index cffdcf11a..fac1a0be3 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -31,24 +31,79 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
31 const res = await next(document, token); 31 const res = await next(document, token);
32 if (res === undefined) throw new Error('busy'); 32 if (res === undefined) throw new Error('busy');
33 return res; 33 return res;
34 },
35 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
36 const params: lc.CodeActionParams = {
37 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
38 range: client.code2ProtocolConverter.asRange(range),
39 context: client.code2ProtocolConverter.asCodeActionContext(context)
40 };
41 return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => {
42 if (values === null) return undefined;
43 const result: (vscode.CodeAction | vscode.Command)[] = [];
44 for (const item of values) {
45 if (lc.CodeAction.is(item)) {
46 const action = client.protocol2CodeConverter.asCodeAction(item);
47 if (isSnippetEdit(item)) {
48 action.command = {
49 command: "rust-analyzer.applySnippetWorkspaceEdit",
50 title: "",
51 arguments: [action.edit],
52 };
53 action.edit = undefined;
54 }
55 result.push(action);
56 } else {
57 const command = client.protocol2CodeConverter.asCommand(item);
58 result.push(command);
59 }
60 }
61 return result;
62 },
63 (_error) => undefined
64 );
34 } 65 }
66
35 } as any 67 } as any
36 }; 68 };
37 69
38 const res = new lc.LanguageClient( 70 const client = new lc.LanguageClient(
39 'rust-analyzer', 71 'rust-analyzer',
40 'Rust Analyzer Language Server', 72 'Rust Analyzer Language Server',
41 serverOptions, 73 serverOptions,
42 clientOptions, 74 clientOptions,
43 ); 75 );
44 76
45 // To turn on all proposed features use: res.registerProposedFeatures(); 77 // To turn on all proposed features use: client.registerProposedFeatures();
46 // Here we want to enable CallHierarchyFeature and SemanticTokensFeature 78 // Here we want to enable CallHierarchyFeature and SemanticTokensFeature
47 // since they are available on stable. 79 // since they are available on stable.
48 // Note that while these features are stable in vscode their LSP protocol 80 // Note that while these features are stable in vscode their LSP protocol
49 // implementations are still in the "proposed" category for 3.16. 81 // implementations are still in the "proposed" category for 3.16.
50 res.registerFeature(new CallHierarchyFeature(res)); 82 client.registerFeature(new CallHierarchyFeature(client));
51 res.registerFeature(new SemanticTokensFeature(res)); 83 client.registerFeature(new SemanticTokensFeature(client));
84 client.registerFeature(new SnippetTextEditFeature());
85
86 return client;
87}
52 88
53 return res; 89class SnippetTextEditFeature implements lc.StaticFeature {
90 fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
91 const caps: any = capabilities.experimental ?? {};
92 caps.snippetTextEdit = true;
93 capabilities.experimental = caps;
94 }
95 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
96 }
97}
98
99function isSnippetEdit(action: lc.CodeAction): boolean {
100 const documentChanges = action.edit?.documentChanges ?? [];
101 for (const edit of documentChanges) {
102 if (lc.TextDocumentEdit.is(edit)) {
103 if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) {
104 return true;
105 }
106 }
107 }
108 return false;
54} 109}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index bdb7fc3b0..0937b495c 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -4,6 +4,7 @@ import * as ra from '../rust-analyzer-api';
4 4
5import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
6import * as sourceChange from '../source_change'; 6import * as sourceChange from '../source_change';
7import { assert } from '../util';
7 8
8export * from './analyzer_status'; 9export * from './analyzer_status';
9export * from './matching_brace'; 10export * from './matching_brace';
@@ -51,3 +52,37 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd {
51 } 52 }
52 }; 53 };
53} 54}
55
56export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd {
57 return async (edit: vscode.WorkspaceEdit) => {
58 assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
59 const [uri, edits] = edit.entries()[0];
60
61 const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
62 if (!editor) return;
63
64 let editWithSnippet: vscode.TextEdit | undefined = undefined;
65 let lineDelta = 0;
66 await editor.edit((builder) => {
67 for (const indel of edits) {
68 const isSnippet = indel.newText.indexOf('$0') !== -1 || indel.newText.indexOf('${') !== -1;
69 if (isSnippet) {
70 editWithSnippet = indel;
71 } else {
72 if (!editWithSnippet) {
73 lineDelta = (indel.newText.match(/\n/g) || []).length - (indel.range.end.line - indel.range.start.line);
74 }
75 builder.replace(indel.range, indel.newText);
76 }
77 }
78 });
79 if (editWithSnippet) {
80 const snip = editWithSnippet as vscode.TextEdit;
81 const range = snip.range.with(
82 snip.range.start.with(snip.range.start.line + lineDelta),
83 snip.range.end.with(snip.range.end.line + lineDelta),
84 );
85 await editor.insertSnippet(new vscode.SnippetString(snip.newText), range);
86 }
87 };
88}
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index b1d93fc34..0bd30fb07 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -7,7 +7,7 @@ import { startDebugSession, getDebugConfiguration } from '../debug';
7 7
8const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; 8const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
9 9
10async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> { 10async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> {
11 const editor = ctx.activeRustEditor; 11 const editor = ctx.activeRustEditor;
12 const client = ctx.client; 12 const client = ctx.client;
13 if (!editor || !client) return; 13 if (!editor || !client) return;
@@ -33,9 +33,20 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showBu
33 ) { 33 ) {
34 continue; 34 continue;
35 } 35 }
36
37 if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) {
38 continue;
39 }
36 items.push(new RunnableQuickPick(r)); 40 items.push(new RunnableQuickPick(r));
37 } 41 }
38 42
43 if (items.length === 0) {
44 // it is the debug case, run always has at least 'cargo check ...'
45 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables
46 vscode.window.showErrorMessage("There's no debug target!");
47 return;
48 }
49
39 return await new Promise((resolve) => { 50 return await new Promise((resolve) => {
40 const disposables: vscode.Disposable[] = []; 51 const disposables: vscode.Disposable[] = [];
41 const close = (result?: RunnableQuickPick) => { 52 const close = (result?: RunnableQuickPick) => {
@@ -107,7 +118,7 @@ export function debug(ctx: Ctx): Cmd {
107 let prevDebuggee: RunnableQuickPick | undefined; 118 let prevDebuggee: RunnableQuickPick | undefined;
108 119
109 return async () => { 120 return async () => {
110 const item = await selectRunnable(ctx, prevDebuggee); 121 const item = await selectRunnable(ctx, prevDebuggee, true);
111 if (!item) return; 122 if (!item) return;
112 123
113 item.detail = 'restart'; 124 item.detail = 'restart';
@@ -147,7 +158,7 @@ async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void>
147 158
148export function newDebugConfig(ctx: Ctx): Cmd { 159export function newDebugConfig(ctx: Ctx): Cmd {
149 return async () => { 160 return async () => {
150 const item = await selectRunnable(ctx, undefined, false); 161 const item = await selectRunnable(ctx, undefined, true, false);
151 if (!item) return; 162 if (!item) return;
152 163
153 await makeDebugConfig(ctx, item); 164 await makeDebugConfig(ctx, item);
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 1652827c3..ee294fbe3 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -16,6 +16,10 @@ export class Config {
16 "files", 16 "files",
17 "highlighting", 17 "highlighting",
18 "updates.channel", 18 "updates.channel",
19 "lens.enable",
20 "lens.run",
21 "lens.debug",
22 "lens.implementations",
19 ] 23 ]
20 .map(opt => `${this.rootSection}.${opt}`); 24 .map(opt => `${this.rootSection}.${opt}`);
21 25
@@ -119,4 +123,13 @@ export class Config {
119 sourceFileMap: sourceFileMap 123 sourceFileMap: sourceFileMap
120 }; 124 };
121 } 125 }
126
127 get lens() {
128 return {
129 enable: this.get<boolean>("lens.enable"),
130 run: this.get<boolean>("lens.run"),
131 debug: this.get<boolean>("lens.debug"),
132 implementations: this.get<boolean>("lens.implementations"),
133 };
134 }
122} 135}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index c015460b8..ac3bb365e 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -91,6 +91,7 @@ export async function activate(context: vscode.ExtensionContext) {
91 ctx.registerCommand('debugSingle', commands.debugSingle); 91 ctx.registerCommand('debugSingle', commands.debugSingle);
92 ctx.registerCommand('showReferences', commands.showReferences); 92 ctx.registerCommand('showReferences', commands.showReferences);
93 ctx.registerCommand('applySourceChange', commands.applySourceChange); 93 ctx.registerCommand('applySourceChange', commands.applySourceChange);
94 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEdit);
94 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); 95 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange);
95 96
96 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 97 ctx.pushCleanup(activateTaskProvider(workspaceFolder));