diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-05-15 15:29:01 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-05-15 15:29:01 +0100 |
commit | d51c1f62178c383363a2d95e865131d9a7b969d0 (patch) | |
tree | 5235615134ab3798f21a167c71ce175795fc6798 /editors/code/src/commands | |
parent | 982b92f966518a0e24632fafdc18d7b5ab6928b4 (diff) | |
parent | a4ecaa70969067c1149711dbf1f40a8a95cb5b72 (diff) |
Merge #4448
4448: Generate configuration for launch.json r=vsrs a=vsrs
This PR adds two new commands: `"rust-analyzer.debug"` and `"rust-analyzer.newDebugConfig"`. The former is a supplement to the existing `"rust-analyzer.run"` command and works the same way: asks for a runnable and starts new debug session. The latter allows adding a new configuration to **launch.json** (or to update an existing one).
If the new option `"rust-analyzer.debug.useLaunchJson"` is set to true then `"rust-analyzer.debug"` and Debug Lens will first look for existing debug configuration in **launch.json**. That is, it has become possible to specify startup arguments, env variables, etc.
`"rust-analyzer.debug.useLaunchJson"` is false by default, but it might be worth making true the default value. Personally I prefer true, but I'm not sure if it is good for all value.
----
I think that this PR also solves https://github.com/rust-analyzer/rust-analyzer/issues/3441.
Both methods to update launch.json mentioned in the issue do not work:
1. Menu. It is only possible to add a launch.json configuration template via a debug adapter. And anyway it's only a template and it is impossible to specify arguments from an extension.
2. DebugConfigurationProvider. The exact opposite situation: it is possible to specify all debug session settings, but it is impossible to export these settings to launch.json.
Separate `"rust-analyzer.newDebugConfig"` command looks better for me.
----
Fixes #4450
Fixes #3441
Co-authored-by: vsrs <[email protected]>
Co-authored-by: vsrs <[email protected]>
Diffstat (limited to 'editors/code/src/commands')
-rw-r--r-- | editors/code/src/commands/runnables.ts | 201 |
1 files changed, 103 insertions, 98 deletions
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index ae328d2a4..b1d93fc34 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts | |||
@@ -1,43 +1,82 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | import * as ra from '../rust-analyzer-api'; | 3 | import * as ra from '../rust-analyzer-api'; |
4 | import * as os from "os"; | ||
5 | 4 | ||
6 | import { Ctx, Cmd } from '../ctx'; | 5 | import { Ctx, Cmd } from '../ctx'; |
7 | import { Cargo } from '../cargo'; | 6 | import { startDebugSession, getDebugConfiguration } from '../debug'; |
8 | 7 | ||
9 | export function run(ctx: Ctx): Cmd { | 8 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; |
10 | let prevRunnable: RunnableQuickPick | undefined; | ||
11 | 9 | ||
12 | return async () => { | 10 | async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> { |
13 | const editor = ctx.activeRustEditor; | 11 | const editor = ctx.activeRustEditor; |
14 | const client = ctx.client; | 12 | const client = ctx.client; |
15 | if (!editor || !client) return; | 13 | if (!editor || !client) return; |
16 | 14 | ||
17 | const textDocument: lc.TextDocumentIdentifier = { | 15 | const textDocument: lc.TextDocumentIdentifier = { |
18 | uri: editor.document.uri.toString(), | 16 | uri: editor.document.uri.toString(), |
19 | }; | 17 | }; |
20 | 18 | ||
21 | const runnables = await client.sendRequest(ra.runnables, { | 19 | const runnables = await client.sendRequest(ra.runnables, { |
22 | textDocument, | 20 | textDocument, |
23 | position: client.code2ProtocolConverter.asPosition( | 21 | position: client.code2ProtocolConverter.asPosition( |
24 | editor.selection.active, | 22 | editor.selection.active, |
25 | ), | 23 | ), |
26 | }); | 24 | }); |
27 | const items: RunnableQuickPick[] = []; | 25 | const items: RunnableQuickPick[] = []; |
28 | if (prevRunnable) { | 26 | if (prevRunnable) { |
29 | items.push(prevRunnable); | 27 | items.push(prevRunnable); |
28 | } | ||
29 | for (const r of runnables) { | ||
30 | if ( | ||
31 | prevRunnable && | ||
32 | JSON.stringify(prevRunnable.runnable) === JSON.stringify(r) | ||
33 | ) { | ||
34 | continue; | ||
30 | } | 35 | } |
31 | for (const r of runnables) { | 36 | items.push(new RunnableQuickPick(r)); |
32 | if ( | 37 | } |
33 | prevRunnable && | 38 | |
34 | JSON.stringify(prevRunnable.runnable) === JSON.stringify(r) | 39 | return await new Promise((resolve) => { |
35 | ) { | 40 | const disposables: vscode.Disposable[] = []; |
36 | continue; | 41 | const close = (result?: RunnableQuickPick) => { |
37 | } | 42 | resolve(result); |
38 | items.push(new RunnableQuickPick(r)); | 43 | disposables.forEach(d => d.dispose()); |
44 | }; | ||
45 | |||
46 | const quickPick = vscode.window.createQuickPick<RunnableQuickPick>(); | ||
47 | quickPick.items = items; | ||
48 | quickPick.title = "Select Runnable"; | ||
49 | if (showButtons) { | ||
50 | quickPick.buttons = quickPickButtons; | ||
39 | } | 51 | } |
40 | const item = await vscode.window.showQuickPick(items); | 52 | disposables.push( |
53 | quickPick.onDidHide(() => close()), | ||
54 | quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), | ||
55 | quickPick.onDidTriggerButton((_button) => { | ||
56 | (async () => await makeDebugConfig(ctx, quickPick.activeItems[0]))(); | ||
57 | close(); | ||
58 | }), | ||
59 | quickPick.onDidChangeActive((active) => { | ||
60 | if (showButtons && active.length > 0) { | ||
61 | if (active[0].label.startsWith('cargo')) { | ||
62 | // save button makes no sense for `cargo test` or `cargo check` | ||
63 | quickPick.buttons = []; | ||
64 | } else if (quickPick.buttons.length === 0) { | ||
65 | quickPick.buttons = quickPickButtons; | ||
66 | } | ||
67 | } | ||
68 | }), | ||
69 | quickPick | ||
70 | ); | ||
71 | quickPick.show(); | ||
72 | }); | ||
73 | } | ||
74 | |||
75 | export function run(ctx: Ctx): Cmd { | ||
76 | let prevRunnable: RunnableQuickPick | undefined; | ||
77 | |||
78 | return async () => { | ||
79 | const item = await selectRunnable(ctx, prevRunnable); | ||
41 | if (!item) return; | 80 | if (!item) return; |
42 | 81 | ||
43 | item.detail = 'rerun'; | 82 | item.detail = 'rerun'; |
@@ -64,88 +103,54 @@ export function runSingle(ctx: Ctx): Cmd { | |||
64 | }; | 103 | }; |
65 | } | 104 | } |
66 | 105 | ||
67 | function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { | 106 | export function debug(ctx: Ctx): Cmd { |
68 | return { | 107 | let prevDebuggee: RunnableQuickPick | undefined; |
69 | type: "lldb", | 108 | |
70 | request: "launch", | 109 | return async () => { |
71 | name: config.label, | 110 | const item = await selectRunnable(ctx, prevDebuggee); |
72 | program: executable, | 111 | if (!item) return; |
73 | args: config.extraArgs, | 112 | |
74 | cwd: config.cwd, | 113 | item.detail = 'restart'; |
75 | sourceMap: sourceFileMap, | 114 | prevDebuggee = item; |
76 | sourceLanguages: ["rust"] | 115 | return await startDebugSession(ctx, item.runnable); |
77 | }; | 116 | }; |
78 | } | 117 | } |
79 | 118 | ||
80 | function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { | 119 | export function debugSingle(ctx: Ctx): Cmd { |
81 | return { | 120 | return async (config: ra.Runnable) => { |
82 | type: (os.platform() === "win32") ? "cppvsdbg" : 'cppdbg', | 121 | await startDebugSession(ctx, config); |
83 | request: "launch", | ||
84 | name: config.label, | ||
85 | program: executable, | ||
86 | args: config.extraArgs, | ||
87 | cwd: config.cwd, | ||
88 | sourceFileMap: sourceFileMap, | ||
89 | }; | 122 | }; |
90 | } | 123 | } |
91 | 124 | ||
92 | const debugOutput = vscode.window.createOutputChannel("Debug"); | 125 | async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void> { |
93 | 126 | const scope = ctx.activeRustEditor?.document.uri; | |
94 | async function getDebugExecutable(config: ra.Runnable): Promise<string> { | 127 | if (!scope) return; |
95 | const cargo = new Cargo(config.cwd || '.', debugOutput); | ||
96 | const executable = await cargo.executableFromArgs(config.args); | ||
97 | |||
98 | // if we are here, there were no compilation errors. | ||
99 | return executable; | ||
100 | } | ||
101 | 128 | ||
102 | type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration; | 129 | const debugConfig = await getDebugConfiguration(ctx, item.runnable); |
130 | if (!debugConfig) return; | ||
103 | 131 | ||
104 | export function debugSingle(ctx: Ctx): Cmd { | 132 | const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); |
105 | return async (config: ra.Runnable) => { | 133 | const configurations = wsLaunchSection.get<any[]>("configurations") || []; |
106 | const editor = ctx.activeRustEditor; | ||
107 | if (!editor) return; | ||
108 | 134 | ||
109 | const knownEngines: Record<string, DebugConfigProvider> = { | 135 | const index = configurations.findIndex(c => c.name === debugConfig.name); |
110 | "vadimcn.vscode-lldb": getLldbDebugConfig, | 136 | if (index !== -1) { |
111 | "ms-vscode.cpptools": getCppvsDebugConfig | 137 | const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update'); |
112 | }; | 138 | if (answer === "Cancel") return; |
113 | const debugOptions = ctx.config.debug; | ||
114 | |||
115 | let debugEngine = null; | ||
116 | if (debugOptions.engine === "auto") { | ||
117 | for (var engineId in knownEngines) { | ||
118 | debugEngine = vscode.extensions.getExtension(engineId); | ||
119 | if (debugEngine) break; | ||
120 | } | ||
121 | } | ||
122 | else { | ||
123 | debugEngine = vscode.extensions.getExtension(debugOptions.engine); | ||
124 | } | ||
125 | 139 | ||
126 | if (!debugEngine) { | 140 | configurations[index] = debugConfig; |
127 | vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` | 141 | } else { |
128 | + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`); | 142 | configurations.push(debugConfig); |
129 | return; | 143 | } |
130 | } | ||
131 | 144 | ||
132 | debugOutput.clear(); | 145 | await wsLaunchSection.update("configurations", configurations); |
133 | if (ctx.config.debug.openUpDebugPane) { | 146 | } |
134 | debugOutput.show(true); | ||
135 | } | ||
136 | 147 | ||
137 | const executable = await getDebugExecutable(config); | 148 | export function newDebugConfig(ctx: Ctx): Cmd { |
138 | const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); | 149 | return async () => { |
139 | if (debugConfig.type in debugOptions.engineSettings) { | 150 | const item = await selectRunnable(ctx, undefined, false); |
140 | const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; | 151 | if (!item) return; |
141 | for (var key in settingsMap) { | ||
142 | debugConfig[key] = settingsMap[key]; | ||
143 | } | ||
144 | } | ||
145 | 152 | ||
146 | debugOutput.appendLine("Launching debug configuration:"); | 153 | await makeDebugConfig(ctx, item); |
147 | debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); | ||
148 | return vscode.debug.startDebugging(undefined, debugConfig); | ||
149 | }; | 154 | }; |
150 | } | 155 | } |
151 | 156 | ||