@@ -15,7 +15,9 @@ import {
1515 QuickPickItem ,
1616 Event ,
1717 window ,
18+ QuickPickItemButtonEvent ,
1819} from 'vscode' ;
20+ import { createDeferred } from './utils/async' ;
1921
2022// Borrowed from https://github.com/Microsoft/vscode-extension-samples/blob/master/quickinput-sample/src/multiStepInput.ts
2123// Why re-invent the wheel :)
@@ -37,7 +39,7 @@ export type InputStep<T extends any> = (input: MultiStepInput<T>, state: T) => P
3739
3840type buttonCallbackType < T extends QuickPickItem > = ( quickPick : QuickPick < T > ) => void ;
3941
40- type QuickInputButtonSetup = {
42+ export type QuickInputButtonSetup = {
4143 /**
4244 * Button for an action in a QuickPick.
4345 */
@@ -54,13 +56,12 @@ export interface IQuickPickParameters<T extends QuickPickItem, E = any> {
5456 totalSteps ?: number ;
5557 canGoBack ?: boolean ;
5658 items : T [ ] ;
57- activeItem ?: T | Promise < T > ;
59+ activeItem ?: T | ( ( quickPick : QuickPick < T > ) => Promise < T > ) ;
5860 placeholder : string | undefined ;
5961 customButtonSetups ?: QuickInputButtonSetup [ ] ;
6062 matchOnDescription ?: boolean ;
6163 matchOnDetail ?: boolean ;
6264 keepScrollPosition ?: boolean ;
63- sortByLabel ?: boolean ;
6465 acceptFilterBoxTextAsSelection ?: boolean ;
6566 /**
6667 * A method called only after quickpick has been created and all handlers are registered.
@@ -70,6 +71,7 @@ export interface IQuickPickParameters<T extends QuickPickItem, E = any> {
7071 callback : ( event : E , quickPick : QuickPick < T > ) => void ;
7172 event : Event < E > ;
7273 } ;
74+ onDidTriggerItemButton ?: ( e : QuickPickItemButtonEvent < T > ) => void ;
7375}
7476
7577interface InputBoxParameters {
@@ -83,7 +85,7 @@ interface InputBoxParameters {
8385 validate ( value : string ) : Promise < string | undefined > ;
8486}
8587
86- type MultiStepInputQuickPicResponseType < T , P > = T | ( P extends { buttons : ( infer I ) [ ] } ? I : never ) | undefined ;
88+ type MultiStepInputQuickPickResponseType < T , P > = T | ( P extends { buttons : ( infer I ) [ ] } ? I : never ) | undefined ;
8789type MultiStepInputInputBoxResponseType < P > = string | ( P extends { buttons : ( infer I ) [ ] } ? I : never ) | undefined ;
8890export interface IMultiStepInput < S > {
8991 run ( start : InputStep < S > , state : S ) : Promise < void > ;
@@ -95,7 +97,7 @@ export interface IMultiStepInput<S> {
9597 activeItem,
9698 placeholder,
9799 customButtonSetups,
98- } : P ) : Promise < MultiStepInputQuickPicResponseType < T , P > > ;
100+ } : P ) : Promise < MultiStepInputQuickPickResponseType < T , P > > ;
99101 showInputBox < P extends InputBoxParameters > ( {
100102 title,
101103 step,
@@ -131,8 +133,9 @@ export class MultiStepInput<S> implements IMultiStepInput<S> {
131133 acceptFilterBoxTextAsSelection,
132134 onChangeItem,
133135 keepScrollPosition,
136+ onDidTriggerItemButton,
134137 initialize,
135- } : P ) : Promise < MultiStepInputQuickPicResponseType < T , P > > {
138+ } : P ) : Promise < MultiStepInputQuickPickResponseType < T , P > > {
136139 const disposables : Disposable [ ] = [ ] ;
137140 const input = window . createQuickPick < T > ( ) ;
138141 input . title = title ;
@@ -161,7 +164,13 @@ export class MultiStepInput<S> implements IMultiStepInput<S> {
161164 initialize ( input ) ;
162165 }
163166 if ( activeItem ) {
164- input . activeItems = [ await activeItem ] ;
167+ if ( typeof activeItem === 'function' ) {
168+ activeItem ( input ) . then ( ( item ) => {
169+ if ( input . activeItems . length === 0 ) {
170+ input . activeItems = [ item ] ;
171+ }
172+ } ) ;
173+ }
165174 } else {
166175 input . activeItems = [ ] ;
167176 }
@@ -170,35 +179,46 @@ export class MultiStepInput<S> implements IMultiStepInput<S> {
170179 // so do it after initialization. This ensures quickpick starts with the active
171180 // item in focus when this is true, instead of having scroll position at top.
172181 input . keepScrollPosition = keepScrollPosition ;
173- try {
174- return await new Promise < MultiStepInputQuickPicResponseType < T , P > > ( ( resolve , reject ) => {
175- disposables . push (
176- input . onDidTriggerButton ( async ( item ) => {
177- if ( item === QuickInputButtons . Back ) {
178- reject ( InputFlowAction . back ) ;
179- }
180- if ( customButtonSetups ) {
181- for ( const customButtonSetup of customButtonSetups ) {
182- if ( JSON . stringify ( item ) === JSON . stringify ( customButtonSetup ?. button ) ) {
183- await customButtonSetup ?. callback ( input ) ;
184- }
185- }
182+
183+ const deferred = createDeferred < T > ( ) ;
184+
185+ disposables . push (
186+ input . onDidTriggerButton ( async ( item ) => {
187+ if ( item === QuickInputButtons . Back ) {
188+ deferred . reject ( InputFlowAction . back ) ;
189+ input . hide ( ) ;
190+ }
191+ if ( customButtonSetups ) {
192+ for ( const customButtonSetup of customButtonSetups ) {
193+ if ( JSON . stringify ( item ) === JSON . stringify ( customButtonSetup ?. button ) ) {
194+ await customButtonSetup ?. callback ( input ) ;
186195 }
187- } ) ,
188- input . onDidChangeSelection ( ( selectedItems ) => resolve ( selectedItems [ 0 ] ) ) ,
189- input . onDidHide ( ( ) => {
190- resolve ( undefined ) ;
191- } ) ,
192- ) ;
193- if ( acceptFilterBoxTextAsSelection ) {
194- disposables . push (
195- input . onDidAccept ( ( ) => {
196- // eslint-disable-next-line @typescript-eslint/no-explicit-any
197- resolve ( input . value as any ) ;
198- } ) ,
199- ) ;
196+ }
200197 }
201- } ) ;
198+ } ) ,
199+ input . onDidChangeSelection ( ( selectedItems ) => deferred . resolve ( selectedItems [ 0 ] ) ) ,
200+ input . onDidHide ( ( ) => {
201+ if ( ! deferred . completed ) {
202+ deferred . resolve ( undefined ) ;
203+ }
204+ } ) ,
205+ input . onDidTriggerItemButton ( async ( item ) => {
206+ if ( onDidTriggerItemButton ) {
207+ await onDidTriggerItemButton ( item ) ;
208+ }
209+ } ) ,
210+ ) ;
211+ if ( acceptFilterBoxTextAsSelection ) {
212+ disposables . push (
213+ input . onDidAccept ( ( ) => {
214+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
215+ deferred . resolve ( input . value as any ) ;
216+ } ) ,
217+ ) ;
218+ }
219+
220+ try {
221+ return await deferred . promise ;
202222 } finally {
203223 disposables . forEach ( ( d ) => d . dispose ( ) ) ;
204224 }
@@ -283,6 +303,9 @@ export class MultiStepInput<S> implements IMultiStepInput<S> {
283303 if ( err === InputFlowAction . back ) {
284304 this . steps . pop ( ) ;
285305 step = this . steps . pop ( ) ;
306+ if ( step === undefined ) {
307+ throw err ;
308+ }
286309 } else if ( err === InputFlowAction . resume ) {
287310 step = this . steps . pop ( ) ;
288311 } else if ( err === InputFlowAction . cancel ) {
@@ -297,6 +320,7 @@ export class MultiStepInput<S> implements IMultiStepInput<S> {
297320 }
298321 }
299322}
323+
300324export const IMultiStepInputFactory = Symbol ( 'IMultiStepInputFactory' ) ;
301325export interface IMultiStepInputFactory {
302326 create < S > ( ) : IMultiStepInput < S > ;
0 commit comments