diff options
author | Benjamin Coenen <[email protected]> | 2020-05-21 09:53:29 +0100 |
---|---|---|
committer | Benjamin Coenen <[email protected]> | 2020-05-21 09:53:29 +0100 |
commit | a7c8aa7c60c05db66ba4e89ae9e05c82e62507a5 (patch) | |
tree | e848f47bdf5d031c408df94222f595d2efcb2070 /editors/code | |
parent | c6143742bd4e625d391ac3ea860be7578ab9f53f (diff) | |
parent | a4e6963a2313971fe7bbec97d03bc67266ef68a9 (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.json | 27 | ||||
-rw-r--r-- | editors/code/src/cargo.ts | 12 | ||||
-rw-r--r-- | editors/code/src/client.ts | 65 | ||||
-rw-r--r-- | editors/code/src/commands/index.ts | 35 | ||||
-rw-r--r-- | editors/code/src/commands/runnables.ts | 17 | ||||
-rw-r--r-- | editors/code/src/config.ts | 13 | ||||
-rw-r--r-- | editors/code/src/main.ts | 1 |
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; | 89 | class 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 | |||
99 | function 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 | ||
5 | import { Ctx, Cmd } from '../ctx'; | 5 | import { Ctx, Cmd } from '../ctx'; |
6 | import * as sourceChange from '../source_change'; | 6 | import * as sourceChange from '../source_change'; |
7 | import { assert } from '../util'; | ||
7 | 8 | ||
8 | export * from './analyzer_status'; | 9 | export * from './analyzer_status'; |
9 | export * from './matching_brace'; | 10 | export * from './matching_brace'; |
@@ -51,3 +52,37 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd { | |||
51 | } | 52 | } |
52 | }; | 53 | }; |
53 | } | 54 | } |
55 | |||
56 | export 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 | ||
8 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; | 8 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; |
9 | 9 | ||
10 | async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> { | 10 | async 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 | ||
148 | export function newDebugConfig(ctx: Ctx): Cmd { | 159 | export 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)); |