From 4b0855f292aa6c76c5720d1dd2caf45f22f94fa5 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 6 Nov 2020 13:21:31 +0100 Subject: [PATCH] Improve the dynamic launch config UI fixes #110009 --- .../debug/browser/debugActionViewItems.ts | 33 +++++++----- .../browser/debugConfigurationManager.ts | 54 ++++++++++++------- .../contrib/debug/browser/debugQuickAccess.ts | 21 +++++++- .../workbench/contrib/debug/common/debug.ts | 4 +- 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index f7fcea5002a..24a6695f824 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -188,23 +188,35 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); + // Only take 3 elements from the recent dynamic configurations to not clutter the dropdown + manager.getRecentDynamicConfigurations().slice(0, 3).forEach(({ name, type }) => { + if (type === manager.selectedConfiguration.type && manager.selectedConfiguration.name === name) { + this.selected = this.options.length; + } + this.options.push({ + label: name, + handler: async () => { + await manager.selectConfiguration(undefined, name, undefined, { type }); + return true; + } + }); + }); + if (this.options.length === 0) { this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); - } else { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); } + this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); + disabledIdxs.push(this.options.length - 1); + this.providers.forEach(p => { - if (p.type === manager.selectedConfiguration.type) { - this.selected = this.options.length; - } this.options.push({ - label: `${p.label}...`, handler: async () => { + label: `${p.label}...`, + handler: async () => { const picked = await p.pick(); if (picked) { - await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, p.type); + await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, { type: p.type }); return true; } return false; @@ -212,11 +224,6 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - if (this.providers.length > 0) { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); - } - manager.getLaunches().filter(l => !l.hidden).forEach(l => { const label = inWorkspace ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration..."); this.options.push({ diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index cfe4b936657..a7ea0a25d3b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -37,7 +37,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { withUndefinedAsNull } from 'vs/base/common/types'; import { sequence } from 'vs/base/common/async'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { flatten } from 'vs/base/common/arrays'; +import { flatten, distinct } from 'vs/base/common/arrays'; import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; @@ -49,6 +49,7 @@ const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; // Debug type is only stored if a dynamic configuration is used for better restore const DEBUG_SELECTED_TYPE = 'debug.selectedtype'; +const DEBUG_RECENT_DYNAMIC_CONFIGURATIONS = 'debug.recentdynamicconfigurations'; interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } @@ -95,9 +96,9 @@ export class ConfigurationManager implements IConfigurationManager { this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { - this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, previousSelectedType); + this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, { type: previousSelectedType }); } else if (this.launches.length > 0) { - this.selectConfiguration(undefined, previousSelectedName, undefined, previousSelectedType); + this.selectConfiguration(undefined, previousSelectedName, undefined, { type: previousSelectedType }); } } @@ -387,6 +388,10 @@ export class ConfigurationManager implements IConfigurationManager { return getVisibleAndSorted(all); } + getRecentDynamicConfigurations(): { name: string, type: string }[] { + return JSON.parse(this.storageService.get(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, StorageScope.WORKSPACE, '[]')); + } + private registerListeners(): void { debuggersExtPoint.setHandler((extensions, delta) => { delta.added.forEach(added => { @@ -522,7 +527,7 @@ export class ConfigurationManager implements IConfigurationManager { return undefined; } - async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise { + async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfig?: { type?: string }): Promise { if (typeof launch === 'undefined') { const rootUri = this.historyService.getLastActiveWorkspaceRoot(); launch = this.getLaunch(rootUri); @@ -542,30 +547,39 @@ export class ConfigurationManager implements IConfigurationManager { } const names = launch ? launch.getConfigurationNames() : []; - if ((name && names.indexOf(name) >= 0) || config) { + if (name && names.indexOf(name) >= 0) { this.setSelectedLaunchName(name); - } else if (!this.selectedName || names.indexOf(this.selectedName) === -1) { - // We could not find the previously used name. We should get all dynamic configurations from providers + } else if (dynamicConfig) { + // We could not find the previously used name and config is not passed. We should get all dynamic configurations from providers // And potentially auto select the previously used dynamic configuration #96293 - const providers = (await this.getDynamicProviders()).filter(p => p.type === type); - const activatedProviders = await Promise.all(providers.map(p => p.getProvider())); - const provider = activatedProviders.find(p => p && p.type === type); - let nameToSet = names.length ? names[0] : undefined; - if (provider && launch && launch.workspace) { - const token = new CancellationTokenSource(); - const dynamicConfigs = await provider.provideDebugConfigurations!(launch.workspace.uri, token.token); - const dynamicConfig = dynamicConfigs.find(c => c.name === name); - if (dynamicConfig) { - config = dynamicConfig; - nameToSet = name; + let nameToSet = config ? config.name : names.length ? names[0] : undefined; + if (!config) { + const providers = (await this.getDynamicProviders()).filter(p => p.type === dynamicConfig.type); + const activatedProviders = await Promise.all(providers.map(p => p.getProvider())); + const provider = activatedProviders.find(p => p && p.type === dynamicConfig.type); + if (provider && launch && launch.workspace) { + const token = new CancellationTokenSource(); + const dynamicConfigs = await provider.provideDebugConfigurations!(launch.workspace.uri, token.token); + const dynamicConfig = dynamicConfigs.find(c => c.name === name); + if (dynamicConfig) { + config = dynamicConfig; + nameToSet = name; + } } } - this.setSelectedLaunchName(nameToSet); + + let recentDynamicProviders = this.getRecentDynamicConfigurations(); + if (name && dynamicConfig.type) { + // We need to store the recently used dynamic configurations to be able to show them in UI #110009 + recentDynamicProviders.unshift({ name, type: dynamicConfig.type }); + recentDynamicProviders = distinct(recentDynamicProviders, t => `${t.name} : ${t.type}`); + this.storageService.store2(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, JSON.stringify(recentDynamicProviders), StorageScope.WORKSPACE, StorageTarget.USER); + } } this.selectedConfig = config; - this.selectedType = type || this.selectedConfig?.type; + this.selectedType = dynamicConfig?.type || this.selectedConfig?.type; this.storageService.store2(DEBUG_SELECTED_TYPE, this.selectedType, StorageScope.WORKSPACE, StorageTarget.MACHINE); const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined); if (configForType) { diff --git a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts index b8aac7951fb..2ddd793d215 100644 --- a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts +++ b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts @@ -64,7 +64,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { - await this.debugService.getConfigurationManager().selectConfiguration(config.launch, config.name); + await configManager.selectConfiguration(config.launch, config.name); try { await this.debugService.startDebugging(config.launch); } catch (error) { @@ -86,6 +86,24 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { + const highlights = matchesFuzzy(filter, name, true); + if (highlights) { + picks.push({ + label: name, + highlights: { label: highlights }, + accept: async () => { + await configManager.selectConfiguration(undefined, name, undefined, { type }); + try { + await this.debugService.startDebugging(configManager.selectedConfiguration.launch, configManager.selectedConfiguration.config); + } catch (error) { + this.notificationService.error(error); + } + } + }); + } + }); + dynamicProviders.forEach(provider => { picks.push({ label: `$(folder) ${provider.label}...`, @@ -93,6 +111,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { const pick = await provider.pick(); if (pick) { + await configManager.selectConfiguration(pick.launch, pick.config.name, pick.config, { type: pick.config.type }); this.debugService.startDebugging(pick.launch, pick.config); } } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 014a65f6266..9be639c16ea 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -677,7 +677,7 @@ export interface IConfigurationManager { type: string | undefined; }; - selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise; + selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfigOptions?: { type?: string }): Promise; getLaunches(): ReadonlyArray; @@ -687,6 +687,8 @@ export interface IConfigurationManager { getAllConfigurations(): { launch: ILaunch, name: string, presentation?: IConfigPresentation }[]; + getRecentDynamicConfigurations(): { name: string, type: string }[]; + /** * Allows to register on change of selected debug configuration. */ -- GitLab