diff options
Diffstat (limited to 'editors/code/src/commands/runnables.ts')
-rw-r--r-- | editors/code/src/commands/runnables.ts | 214 |
1 files changed, 62 insertions, 152 deletions
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index cf980e257..7919997ce 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts | |||
@@ -1,11 +1,68 @@ | |||
1 | import * as child_process from 'child_process'; | ||
2 | |||
3 | import * as util from 'util'; | ||
4 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
5 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
6 | 3 | ||
7 | import { Server } from '../server'; | 4 | import { Ctx, Cmd } from '../ctx'; |
8 | import { CargoWatchProvider, registerCargoWatchProvider } from './cargo_watch'; | 5 | |
6 | export function run(ctx: Ctx): Cmd { | ||
7 | let prevRunnable: RunnableQuickPick | undefined; | ||
8 | |||
9 | return async () => { | ||
10 | const editor = ctx.activeRustEditor; | ||
11 | const client = ctx.client; | ||
12 | if (!editor || !client) return; | ||
13 | |||
14 | const textDocument: lc.TextDocumentIdentifier = { | ||
15 | uri: editor.document.uri.toString(), | ||
16 | }; | ||
17 | const params: RunnablesParams = { | ||
18 | textDocument, | ||
19 | position: client.code2ProtocolConverter.asPosition( | ||
20 | editor.selection.active, | ||
21 | ), | ||
22 | }; | ||
23 | const runnables = await client.sendRequest<Runnable[]>( | ||
24 | 'rust-analyzer/runnables', | ||
25 | params, | ||
26 | ); | ||
27 | const items: RunnableQuickPick[] = []; | ||
28 | if (prevRunnable) { | ||
29 | items.push(prevRunnable); | ||
30 | } | ||
31 | for (const r of runnables) { | ||
32 | if ( | ||
33 | prevRunnable && | ||
34 | JSON.stringify(prevRunnable.runnable) === JSON.stringify(r) | ||
35 | ) { | ||
36 | continue; | ||
37 | } | ||
38 | items.push(new RunnableQuickPick(r)); | ||
39 | } | ||
40 | const item = await vscode.window.showQuickPick(items); | ||
41 | if (!item) return; | ||
42 | |||
43 | item.detail = 'rerun'; | ||
44 | prevRunnable = item; | ||
45 | const task = createTask(item.runnable); | ||
46 | return await vscode.tasks.executeTask(task); | ||
47 | }; | ||
48 | } | ||
49 | |||
50 | export function runSingle(ctx: Ctx): Cmd { | ||
51 | return async (runnable: Runnable) => { | ||
52 | const editor = ctx.activeRustEditor; | ||
53 | if (!editor) return; | ||
54 | |||
55 | const task = createTask(runnable); | ||
56 | task.group = vscode.TaskGroup.Build; | ||
57 | task.presentationOptions = { | ||
58 | reveal: vscode.TaskRevealKind.Always, | ||
59 | panel: vscode.TaskPanelKind.Dedicated, | ||
60 | clear: true, | ||
61 | }; | ||
62 | |||
63 | return vscode.tasks.executeTask(task); | ||
64 | }; | ||
65 | } | ||
9 | 66 | ||
10 | interface RunnablesParams { | 67 | interface RunnablesParams { |
11 | textDocument: lc.TextDocumentIdentifier; | 68 | textDocument: lc.TextDocumentIdentifier; |
@@ -71,150 +128,3 @@ function createTask(spec: Runnable): vscode.Task { | |||
71 | t.presentationOptions.clear = true; | 128 | t.presentationOptions.clear = true; |
72 | return t; | 129 | return t; |
73 | } | 130 | } |
74 | |||
75 | let prevRunnable: RunnableQuickPick | undefined; | ||
76 | export async function handle(): Promise<vscode.TaskExecution | undefined> { | ||
77 | const editor = vscode.window.activeTextEditor; | ||
78 | if (editor == null || editor.document.languageId !== 'rust') { | ||
79 | return; | ||
80 | } | ||
81 | const textDocument: lc.TextDocumentIdentifier = { | ||
82 | uri: editor.document.uri.toString(), | ||
83 | }; | ||
84 | const params: RunnablesParams = { | ||
85 | textDocument, | ||
86 | position: Server.client.code2ProtocolConverter.asPosition( | ||
87 | editor.selection.active, | ||
88 | ), | ||
89 | }; | ||
90 | const runnables = await Server.client.sendRequest<Runnable[]>( | ||
91 | 'rust-analyzer/runnables', | ||
92 | params, | ||
93 | ); | ||
94 | const items: RunnableQuickPick[] = []; | ||
95 | if (prevRunnable) { | ||
96 | items.push(prevRunnable); | ||
97 | } | ||
98 | for (const r of runnables) { | ||
99 | if ( | ||
100 | prevRunnable && | ||
101 | JSON.stringify(prevRunnable.runnable) === JSON.stringify(r) | ||
102 | ) { | ||
103 | continue; | ||
104 | } | ||
105 | items.push(new RunnableQuickPick(r)); | ||
106 | } | ||
107 | const item = await vscode.window.showQuickPick(items); | ||
108 | if (!item) { | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | item.detail = 'rerun'; | ||
113 | prevRunnable = item; | ||
114 | const task = createTask(item.runnable); | ||
115 | return await vscode.tasks.executeTask(task); | ||
116 | } | ||
117 | |||
118 | export async function handleSingle(runnable: Runnable) { | ||
119 | const editor = vscode.window.activeTextEditor; | ||
120 | if (editor == null || editor.document.languageId !== 'rust') { | ||
121 | return; | ||
122 | } | ||
123 | |||
124 | const task = createTask(runnable); | ||
125 | task.group = vscode.TaskGroup.Build; | ||
126 | task.presentationOptions = { | ||
127 | reveal: vscode.TaskRevealKind.Always, | ||
128 | panel: vscode.TaskPanelKind.Dedicated, | ||
129 | clear: true, | ||
130 | }; | ||
131 | |||
132 | return vscode.tasks.executeTask(task); | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * Interactively asks the user whether we should run `cargo check` in order to | ||
137 | * provide inline diagnostics; the user is met with a series of dialog boxes | ||
138 | * that, when accepted, allow us to `cargo install cargo-watch` and then run it. | ||
139 | */ | ||
140 | export async function interactivelyStartCargoWatch( | ||
141 | context: vscode.ExtensionContext, | ||
142 | ): Promise<CargoWatchProvider | undefined> { | ||
143 | if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') { | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | if (Server.config.cargoWatchOptions.enableOnStartup === 'ask') { | ||
148 | const watch = await vscode.window.showInformationMessage( | ||
149 | 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)', | ||
150 | 'yes', | ||
151 | 'no', | ||
152 | ); | ||
153 | if (watch !== 'yes') { | ||
154 | return; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return startCargoWatch(context); | ||
159 | } | ||
160 | |||
161 | export async function startCargoWatch( | ||
162 | context: vscode.ExtensionContext, | ||
163 | ): Promise<CargoWatchProvider | undefined> { | ||
164 | const execPromise = util.promisify(child_process.exec); | ||
165 | |||
166 | const { stderr, code = 0 } = await execPromise( | ||
167 | 'cargo watch --version', | ||
168 | ).catch(e => e); | ||
169 | |||
170 | if (stderr.includes('no such subcommand: `watch`')) { | ||
171 | const msg = | ||
172 | 'The `cargo-watch` subcommand is not installed. Install? (takes ~1-2 minutes)'; | ||
173 | const install = await vscode.window.showInformationMessage( | ||
174 | msg, | ||
175 | 'yes', | ||
176 | 'no', | ||
177 | ); | ||
178 | if (install !== 'yes') { | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | const label = 'install-cargo-watch'; | ||
183 | const taskFinished = new Promise((resolve, _reject) => { | ||
184 | const disposable = vscode.tasks.onDidEndTask(({ execution }) => { | ||
185 | if (execution.task.name === label) { | ||
186 | disposable.dispose(); | ||
187 | resolve(); | ||
188 | } | ||
189 | }); | ||
190 | }); | ||
191 | |||
192 | vscode.tasks.executeTask( | ||
193 | createTask({ | ||
194 | label, | ||
195 | bin: 'cargo', | ||
196 | args: ['install', 'cargo-watch'], | ||
197 | env: {}, | ||
198 | }), | ||
199 | ); | ||
200 | await taskFinished; | ||
201 | const output = await execPromise('cargo watch --version').catch(e => e); | ||
202 | if (output.stderr !== '') { | ||
203 | vscode.window.showErrorMessage( | ||
204 | `Couldn't install \`cargo-\`watch: ${output.stderr}`, | ||
205 | ); | ||
206 | return; | ||
207 | } | ||
208 | } else if (code !== 0) { | ||
209 | vscode.window.showErrorMessage( | ||
210 | `\`cargo watch\` failed with ${code}: ${stderr}`, | ||
211 | ); | ||
212 | return; | ||
213 | } | ||
214 | |||
215 | const provider = await registerCargoWatchProvider(context.subscriptions); | ||
216 | if (provider) { | ||
217 | provider.start(); | ||
218 | } | ||
219 | return provider; | ||
220 | } | ||