44 *--------------------------------------------------------------------------------------------*/
55
66import * as nls from 'vs/nls' ;
7- import { dispose , IDisposable } from 'vs/base/common/lifecycle' ;
7+ import { dispose , IDisposable , DisposableStore } from 'vs/base/common/lifecycle' ;
88import { Event , Emitter } from 'vs/base/common/event' ;
99import * as strings from 'vs/base/common/strings' ;
1010import * as objects from 'vs/base/common/objects' ;
@@ -37,7 +37,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
3737import { withUndefinedAsNull } from 'vs/base/common/types' ;
3838import { sequence } from 'vs/base/common/async' ;
3939import { IHistoryService } from 'vs/workbench/services/history/common/history' ;
40- import { first } from 'vs/base/common/arrays' ;
40+ import { first , flatten } from 'vs/base/common/arrays' ;
4141import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils' ;
4242import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes' ;
4343
@@ -47,6 +47,8 @@ jsonRegistry.registerSchema(launchSchemaId, launchSchema);
4747const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname' ;
4848const DEBUG_SELECTED_ROOT = 'debug.selectedroot' ;
4949
50+ interface IDynamicPickItem { label : string , launch : ILaunch , config : IConfig }
51+
5052export class ConfigurationManager implements IConfigurationManager {
5153 private debuggers : Debugger [ ] ;
5254 private breakpointModeIdsSet = new Set < string > ( ) ;
@@ -251,23 +253,60 @@ export class ConfigurationManager implements IConfigurationManager {
251253 async getDynamicProviders ( ) : Promise < { label : string , pick : ( ) => Promise < { launch : ILaunch , config : IConfig } | undefined > } [ ] > {
252254 const extensions = await this . extensionService . getExtensions ( ) ;
253255 const onDebugDynamicConfigurationsName = 'onDebugDynamicConfigurations' ;
254- const debugDynamicExtensionsTypes = extensions . map ( e => {
255- const activationEvent = e . activationEvents && e . activationEvents . find ( e => e . includes ( onDebugDynamicConfigurationsName ) ) ;
256- if ( activationEvent ) {
257- const type = activationEvent . substr ( onDebugDynamicConfigurationsName . length + 1 ) ;
258- return type || ( e . contributes && e . contributes . debuggers && e . contributes . debuggers . length ? e . contributes . debuggers [ 0 ] . type : undefined ) ;
256+ const debugDynamicExtensionsTypes = extensions . reduce ( ( acc , e ) => {
257+ if ( ! e . activationEvents ) {
258+ return acc ;
259259 }
260260
261- return undefined ;
262- } ) . filter ( type => typeof type === 'string' && ! ! this . getDebuggerLabel ( type ) ) as string [ ] ;
261+ const explicitTypes : string [ ] = [ ] ;
262+ let hasGenericEvent = false ;
263+ for ( const event of e . activationEvents ) {
264+ if ( event === onDebugDynamicConfigurationsName ) {
265+ hasGenericEvent = true ;
266+ } else if ( event . startsWith ( `${ onDebugDynamicConfigurationsName } :` ) ) {
267+ explicitTypes . push ( event . slice ( onDebugDynamicConfigurationsName . length + 1 ) ) ;
268+ }
269+ }
270+
271+ if ( explicitTypes . length ) {
272+ return acc . concat ( explicitTypes ) ;
273+ }
274+
275+ if ( hasGenericEvent ) {
276+ const debuggerType = e . contributes ?. debuggers ?. [ 0 ] . type ;
277+ return debuggerType ? acc . concat ( debuggerType ) : acc ;
278+ }
279+
280+ return acc ;
281+ } , [ ] as string [ ] ) ;
263282
264283 return debugDynamicExtensionsTypes . map ( type => {
265284 return {
266285 label : this . getDebuggerLabel ( type ) ! ,
267286 pick : async ( ) => {
287+ const disposables = new DisposableStore ( ) ;
288+ const input = disposables . add ( this . quickInputService . createQuickPick < IDynamicPickItem > ( ) ) ;
289+ input . busy = true ;
290+ input . placeholder = nls . localize ( 'selectConfiguration' , "Select Launch Configuration" ) ;
291+ input . show ( ) ;
292+
293+ let chosenDidCancel = false ;
294+ const chosenPromise = new Promise < IDynamicPickItem | undefined > ( resolve => {
295+ disposables . add ( input . onDidAccept ( ( ) => resolve ( input . activeItems [ 0 ] ) ) ) ;
296+ disposables . add ( input . onDidTriggerItemButton ( async ( context ) => {
297+ resolve ( undefined ) ;
298+ const { launch, config } = context . item ;
299+ await launch . openConfigFile ( false , config . type ) ;
300+ // Only Launch have a pin trigger button
301+ await ( launch as Launch ) . writeConfiguration ( config ) ;
302+ this . selectConfiguration ( launch , config . name ) ;
303+ } ) ) ;
304+ disposables . add ( input . onDidHide ( ( ) => { chosenDidCancel = true ; resolve ( ) ; } ) ) ;
305+ } ) ;
306+
268307 await this . activateDebuggers ( onDebugDynamicConfigurationsName , type ) ;
269308 const token = new CancellationTokenSource ( ) ;
270- const picks : Promise < { label : string , launch : ILaunch , config : IConfig } [ ] > [ ] = [ ] ;
309+ const picks : Promise < IDynamicPickItem [ ] > [ ] = [ ] ;
271310 const provider = this . configProviders . filter ( p => p . type === type && p . triggerKind === DebugConfigurationProviderTriggerKind . Dynamic && p . provideDebugConfigurations ) [ 0 ] ;
272311 this . getLaunches ( ) . forEach ( launch => {
273312 if ( launch . workspace && provider ) {
@@ -282,25 +321,30 @@ export class ConfigurationManager implements IConfigurationManager {
282321 } ) ) ) ) ;
283322 }
284323 } ) ;
285- const promiseOfPicks = Promise . all ( picks ) . then ( result => result . reduce ( ( first , second ) => first . concat ( second ) , [ ] ) ) ;
286324
287- const result = await this . quickInputService . pick < { label : string , launch : ILaunch , config : IConfig } > ( promiseOfPicks , {
288- placeHolder : nls . localize ( 'selectConfiguration' , "Select Launch Configuration" ) ,
289- onDidTriggerItemButton : async ( context ) => {
290- await this . quickInputService . cancel ( ) ;
291- const { launch, config } = context . item ;
292- await launch . openConfigFile ( false , config . type ) ;
293- // Only Launch have a pin trigger button
294- await ( launch as Launch ) . writeConfiguration ( config ) ;
295- this . selectConfiguration ( launch , config . name ) ;
296- }
297- } ) ;
298- if ( ! result ) {
325+ const nestedPicks = await Promise . all ( picks ) ;
326+ const items = flatten ( nestedPicks ) ;
327+
328+ let chosen : IDynamicPickItem | undefined ;
329+
330+ // If there's exactly one item to choose from, pick it automatically
331+ if ( items . length === 1 && ! chosenDidCancel ) {
332+ chosen = items [ 0 ] ;
333+ } else {
334+ input . items = items ;
335+ input . busy = false ;
336+ chosen = await chosenPromise ;
337+ }
338+
339+ disposables . dispose ( ) ;
340+
341+ if ( ! chosen ) {
299342 // User canceled quick input we should notify the provider to cancel computing configurations
300343 token . cancel ( ) ;
344+ return ;
301345 }
302346
303- return result ;
347+ return chosen ;
304348 }
305349 } ;
306350 } ) ;
0 commit comments