Skip to content

Commit 3a9d44e

Browse files
committed
debug: allow a single debug extension to provide multiple configs
Previously if a debug extension provided multiple dynamic configurations, we would just use the first debugger -- whatever that was. This change now shows all configurations for which dynamic configs are registered. I also adjusted the picker to automatically select the first item if there's only a single configuration provided. This works well for the debug terminal, but also means that the user doesn't see the name of the selected item, which might not be desirable. Open to pushback. Together these finish the request for a separate top-level contribution for the terminal in microsoft#98054 Finally, with that adjustment I made a tweak so that the picker shows up in a `busy` state while extensions are activating. Previously you would select a dynamic configuration title and could have a few seconds of delay before the picker came up, which is probably not desirable.
1 parent f2a654c commit 3a9d44e

1 file changed

Lines changed: 51 additions & 22 deletions

File tree

src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ jsonRegistry.registerSchema(launchSchemaId, launchSchema);
4747
const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname';
4848
const DEBUG_SELECTED_ROOT = 'debug.selectedroot';
4949

50+
interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig }
51+
5052
export class ConfigurationManager implements IConfigurationManager {
5153
private debuggers: Debugger[];
5254
private breakpointModeIdsSet = new Set<string>();
@@ -251,23 +253,50 @@ 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 = e.activationEvents.filter(e => e.includes(`${onDebugDynamicConfigurationsName}:`)).map(type => type.slice(onDebugDynamicConfigurationsName.length + 1));
262+
if (explicitTypes.length) {
263+
return [...acc, ...explicitTypes];
264+
}
265+
266+
if (e.activationEvents.includes(onDebugDynamicConfigurationsName)) {
267+
const debuggerType = e.contributes?.debuggers?.[0].type;
268+
return debuggerType ? [...acc, debuggerType] : acc;
269+
}
270+
271+
return acc;
272+
}, [] as string[]);
263273

264274
return debugDynamicExtensionsTypes.map(type => {
265275
return {
266276
label: this.getDebuggerLabel(type)!,
267277
pick: async () => {
278+
const input = this.quickInputService.createQuickPick<IDynamicPickItem>();
279+
input.busy = true;
280+
input.placeholder = nls.localize('selectConfiguration', "Select Launch Configuration");
281+
input.show();
282+
283+
let chosenDidCancel = false;
284+
const chosenPromise = new Promise<IDynamicPickItem | undefined>(resolve => {
285+
input.onDidAccept(() => resolve(input.activeItems[0]));
286+
input.onDidTriggerItemButton(async (context) => {
287+
resolve(undefined);
288+
const { launch, config } = context.item;
289+
await launch.openConfigFile(false, config.type);
290+
// Only Launch have a pin trigger button
291+
await (launch as Launch).writeConfiguration(config);
292+
this.selectConfiguration(launch, config.name);
293+
});
294+
input.onDidHide(() => { chosenDidCancel = true; resolve(); });
295+
});
296+
268297
await this.activateDebuggers(onDebugDynamicConfigurationsName, type);
269298
const token = new CancellationTokenSource();
270-
const picks: Promise<{ label: string, launch: ILaunch, config: IConfig }[]>[] = [];
299+
const picks: Promise<IDynamicPickItem[]>[] = [];
271300
const provider = this.configProviders.filter(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations)[0];
272301
this.getLaunches().forEach(launch => {
273302
if (launch.workspace && provider) {
@@ -282,25 +311,25 @@ export class ConfigurationManager implements IConfigurationManager {
282311
}))));
283312
}
284313
});
285-
const promiseOfPicks = Promise.all(picks).then(result => result.reduce((first, second) => first.concat(second), []));
286314

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) {
315+
const items = await Promise.all(picks).then(result => result.reduce((first, second) => first.concat(second), []));
316+
let chosen: IDynamicPickItem | undefined;
317+
if (items.length === 1 && !chosenDidCancel) {
318+
chosen = items[0];
319+
} else {
320+
input.items = items;
321+
input.busy = false;
322+
chosen = await chosenPromise;
323+
}
324+
325+
input.dispose();
326+
if (!chosen) {
299327
// User canceled quick input we should notify the provider to cancel computing configurations
300328
token.cancel();
329+
return;
301330
}
302331

303-
return result;
332+
return chosen;
304333
}
305334
};
306335
});

0 commit comments

Comments
 (0)