@@ -7,6 +7,8 @@ import type { CtxInit } from "./ctx";
77import { makeDebugConfig } from "./debug" ;
88import type { Config , RunnableEnvCfg , RunnableEnvCfgItem } from "./config" ;
99import { unwrapUndefinable } from "./undefinable" ;
10+ import type { LanguageClient } from "vscode-languageclient/node" ;
11+ import type { RustEditor } from "./util" ;
1012
1113const quickPickButtons = [
1214 { iconPath : new vscode . ThemeIcon ( "save" ) , tooltip : "Save as a launch.json configuration." } ,
@@ -21,73 +23,36 @@ export async function selectRunnable(
2123 const editor = ctx . activeRustEditor ;
2224 if ( ! editor ) return ;
2325
24- const client = ctx . client ;
25- const textDocument : lc . TextDocumentIdentifier = {
26- uri : editor . document . uri . toString ( ) ,
27- } ;
28-
29- const runnables = await client . sendRequest ( ra . runnables , {
30- textDocument,
31- position : client . code2ProtocolConverter . asPosition ( editor . selection . active ) ,
32- } ) ;
33- const items : RunnableQuickPick [ ] = [ ] ;
34- if ( prevRunnable ) {
35- items . push ( prevRunnable ) ;
26+ // show a placeholder while we get the runnables from the server
27+ const quickPick = vscode . window . createQuickPick ( ) ;
28+ quickPick . title = "Select Runnable" ;
29+ if ( showButtons ) {
30+ quickPick . buttons = quickPickButtons ;
3631 }
37- for ( const r of runnables ) {
38- if ( prevRunnable && JSON . stringify ( prevRunnable . runnable ) === JSON . stringify ( r ) ) {
39- continue ;
40- }
32+ quickPick . items = [ { label : "Looking for runnables..." } ] ;
33+ quickPick . activeItems = [ ] ;
34+ quickPick . show ( ) ;
4135
42- if ( debuggeeOnly && ( r . label . startsWith ( "doctest" ) || r . label . startsWith ( "cargo" ) ) ) {
43- continue ;
44- }
45- items . push ( new RunnableQuickPick ( r ) ) ;
46- }
36+ const runnables = await getRunnables ( ctx . client , editor , prevRunnable , debuggeeOnly ) ;
4737
48- if ( items . length === 0 ) {
38+ if ( runnables . length === 0 ) {
4939 // it is the debug case, run always has at least 'cargo check ...'
5040 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables
5141 await vscode . window . showErrorMessage ( "There's no debug target!" ) ;
42+ quickPick . dispose ( ) ;
5243 return ;
5344 }
5445
55- return await new Promise ( ( resolve ) => {
56- const disposables : vscode . Disposable [ ] = [ ] ;
57- const close = ( result ?: RunnableQuickPick ) => {
58- resolve ( result ) ;
59- disposables . forEach ( ( d ) => d . dispose ( ) ) ;
60- } ;
46+ // clear the list before we hook up listeners to to avoid invoking them
47+ // if the user happens to accept the placeholder item
48+ quickPick . items = [ ] ;
6149
62- const quickPick = vscode . window . createQuickPick < RunnableQuickPick > ( ) ;
63- quickPick . items = items ;
64- quickPick . title = "Select Runnable" ;
65- if ( showButtons ) {
66- quickPick . buttons = quickPickButtons ;
67- }
68- disposables . push (
69- quickPick . onDidHide ( ( ) => close ( ) ) ,
70- quickPick . onDidAccept ( ( ) => close ( quickPick . selectedItems [ 0 ] ) ) ,
71- quickPick . onDidTriggerButton ( async ( _button ) => {
72- const runnable = unwrapUndefinable ( quickPick . activeItems [ 0 ] ) . runnable ;
73- await makeDebugConfig ( ctx , runnable ) ;
74- close ( ) ;
75- } ) ,
76- quickPick . onDidChangeActive ( ( activeList ) => {
77- if ( showButtons && activeList . length > 0 ) {
78- const active = unwrapUndefinable ( activeList [ 0 ] ) ;
79- if ( active . label . startsWith ( "cargo" ) ) {
80- // save button makes no sense for `cargo test` or `cargo check`
81- quickPick . buttons = [ ] ;
82- } else if ( quickPick . buttons . length === 0 ) {
83- quickPick . buttons = quickPickButtons ;
84- }
85- }
86- } ) ,
87- quickPick ,
88- ) ;
89- quickPick . show ( ) ;
90- } ) ;
50+ return await populateAndGetSelection (
51+ quickPick as vscode . QuickPick < RunnableQuickPick > ,
52+ runnables ,
53+ ctx ,
54+ showButtons ,
55+ ) ;
9156}
9257
9358export class RunnableQuickPick implements vscode . QuickPickItem {
@@ -187,3 +152,75 @@ export function createArgs(runnable: ra.Runnable): string[] {
187152 }
188153 return args ;
189154}
155+
156+ async function getRunnables (
157+ client : LanguageClient ,
158+ editor : RustEditor ,
159+ prevRunnable ?: RunnableQuickPick ,
160+ debuggeeOnly = false ,
161+ ) : Promise < RunnableQuickPick [ ] > {
162+ const textDocument : lc . TextDocumentIdentifier = {
163+ uri : editor . document . uri . toString ( ) ,
164+ } ;
165+
166+ const runnables = await client . sendRequest ( ra . runnables , {
167+ textDocument,
168+ position : client . code2ProtocolConverter . asPosition ( editor . selection . active ) ,
169+ } ) ;
170+ const items : RunnableQuickPick [ ] = [ ] ;
171+ if ( prevRunnable ) {
172+ items . push ( prevRunnable ) ;
173+ }
174+ for ( const r of runnables ) {
175+ if ( prevRunnable && JSON . stringify ( prevRunnable . runnable ) === JSON . stringify ( r ) ) {
176+ continue ;
177+ }
178+
179+ if ( debuggeeOnly && ( r . label . startsWith ( "doctest" ) || r . label . startsWith ( "cargo" ) ) ) {
180+ continue ;
181+ }
182+ items . push ( new RunnableQuickPick ( r ) ) ;
183+ }
184+
185+ return items ;
186+ }
187+
188+ async function populateAndGetSelection (
189+ quickPick : vscode . QuickPick < RunnableQuickPick > ,
190+ runnables : RunnableQuickPick [ ] ,
191+ ctx : CtxInit ,
192+ showButtons : boolean ,
193+ ) : Promise < RunnableQuickPick | undefined > {
194+ return new Promise ( ( resolve ) => {
195+ const disposables : vscode . Disposable [ ] = [ ] ;
196+ const close = ( result ?: RunnableQuickPick ) => {
197+ resolve ( result ) ;
198+ disposables . forEach ( ( d ) => d . dispose ( ) ) ;
199+ } ;
200+ disposables . push (
201+ quickPick . onDidHide ( ( ) => close ( ) ) ,
202+ quickPick . onDidAccept ( ( ) => close ( quickPick . selectedItems [ 0 ] as RunnableQuickPick ) ) ,
203+ quickPick . onDidTriggerButton ( async ( _button ) => {
204+ const runnable = unwrapUndefinable (
205+ quickPick . activeItems [ 0 ] as RunnableQuickPick ,
206+ ) . runnable ;
207+ await makeDebugConfig ( ctx , runnable ) ;
208+ close ( ) ;
209+ } ) ,
210+ quickPick . onDidChangeActive ( ( activeList ) => {
211+ if ( showButtons && activeList . length > 0 ) {
212+ const active = unwrapUndefinable ( activeList [ 0 ] ) ;
213+ if ( active . label . startsWith ( "cargo" ) ) {
214+ // save button makes no sense for `cargo test` or `cargo check`
215+ quickPick . buttons = [ ] ;
216+ } else if ( quickPick . buttons . length === 0 ) {
217+ quickPick . buttons = quickPickButtons ;
218+ }
219+ }
220+ } ) ,
221+ quickPick ,
222+ ) ;
223+ // populate the list with the actual runnables
224+ quickPick . items = runnables ;
225+ } ) ;
226+ }
0 commit comments