From 42f8432c0346d6ec82e5a9af42bc5f7723098263 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Dec 2016 14:52:01 +0100 Subject: [PATCH] debug debt: eventing around launch.json updates and error handling fixes #16615 fixes #16622 --- .../parts/debug/browser/debugActionItems.ts | 32 ++++----- src/vs/workbench/parts/debug/common/debug.ts | 6 ++ .../debug/electron-browser/debugService.ts | 23 +++--- .../debug/node/debugConfigurationManager.ts | 72 ++++++++++++------- 4 files changed, 82 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts index 6c0dd58dd84..cea9a2bede6 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -13,7 +13,7 @@ import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { SelectActionItem, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { EventEmitter } from 'vs/base/common/eventEmitter'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IDebugService, IGlobalConfig, NO_CONFIGURATIONS_LABEL, State } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, NO_CONFIGURATIONS_LABEL } from 'vs/workbench/parts/debug/common/debug'; const $ = dom.$; @@ -38,7 +38,9 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem { private registerListeners(): void { this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => { - this.updateOptions(); + if (e.sourceConfig.launch) { + this.updateOptions(); + } })); this.toDispose.push(this.selectBox.onDidSelect(configurationName => { this.debugService.getViewModel().setSelectedConfigurationName(configurationName); @@ -85,7 +87,7 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem { } public isEnabled(): boolean { - return this.debugService.state !== State.Inactive; + return this.selectBox.enabled; } public focus(): void { @@ -100,24 +102,22 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem { this.toDispose = lifecycle.dispose(this.toDispose); } - private updateOptions(): void { - const config = this.configurationService.getConfiguration('launch'); - if (!config || !config.configurations || config.configurations.length === 0) { + private setEnabled(enabled: boolean): void { + this.selectBox.enabled = enabled; + if (!enabled) { this.selectBox.setOptions([NO_CONFIGURATIONS_LABEL], 0); - this.selectBox.enabled = false; - } else { - const options = config.configurations.filter(cfg => typeof cfg.name === 'string').map(cfg => cfg.name); - if (config.compounds) { - options.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length) - .map(compound => compound.name)); - } + } + } + private updateOptions(): void { + const options = this.debugService.getConfigurationManager().getConfigurationNames(); + if (options.length === 0) { + this.setEnabled(false); + } else { + this.setEnabled(true); const selected = options.indexOf(this.debugService.getViewModel().selectedConfigurationName); this.selectBox.setOptions(options, selected); - this.selectBox.enabled = true; } - - this.debugService.getViewModel().setSelectedConfigurationName(this.selectBox.getSelected()); } } diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index fd802b65d55..f0bf97d2d2c 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -342,6 +342,12 @@ export interface IConfigurationManager { */ getConfiguration(nameOrConfig: string | IConfig): TPromise; + /** + * Returns the names of all configurations and compounds. + * Ignores configurations which are invalid. + */ + getConfigurationNames(): string[]; + /** * Returns a compound with the specified name. * Returns null if there is no compound with the specified name. diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 16063ad719c..40b6ce8ee87 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -130,6 +130,15 @@ export class DebugService implements debug.IDebugService { lifecycleService.onShutdown(this.dispose, this); this.toDispose.push(this.windowService.onBroadcast(this.onBroadcast, this)); + this.toDispose.push(this.configurationService.onDidUpdateConfiguration((event) => { + if (event.sourceConfig.launch) { + const names = this.configurationManager.getConfigurationNames(); + if (names.every(name => name !== this.viewModel.selectedConfigurationName)) { + // Current selected configuration no longer exists - take the first configuration instead. + this.viewModel.setSelectedConfigurationName(names.length ? names[0] : undefined); + } + } + })); } private onBroadcast(broadcast: IBroadcast): void { @@ -566,14 +575,6 @@ export class DebugService implements debug.IDebugService { } return this.configurationManager.getConfiguration(configurationOrName).then(configuration => { - if (!configuration) { - return this.configurationManager.openConfigFile(false).then(openend => { - if (openend) { - this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application.")); - } - }); - } - if (!this.configurationManager.getAdapter(configuration.type)) { return configuration.type ? TPromise.wrapError(new Error(nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", configuration.type))) : TPromise.wrapError(errors.create(nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."), @@ -611,6 +612,12 @@ export class DebugService implements debug.IDebugService { actions: [this.taskService.configureAction(), CloseAction] }); }); + }, err => { + return this.configurationManager.openConfigFile(false).then(openend => { + if (openend) { + this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message)); + } + }); }); }))); } diff --git a/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts index dcc02f354cc..12c0f98ba25 100644 --- a/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts @@ -262,45 +262,63 @@ export class ConfigurationManager implements debug.IConfigurationManager { return config.compounds.filter(compound => compound.name === name).pop(); } + public getConfigurationNames(): string[] { + const config = this.configurationService.getConfiguration('launch'); + if (!config || !config.configurations) { + return []; + } else { + const names = config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name); + if (names.length > 0 && config.compounds) { + if (config.compounds) { + names.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length) + .map(compound => compound.name)); + } + } + + return names; + } + } + public getConfiguration(nameOrConfig: string | debug.IConfig): TPromise { const config = this.configurationService.getConfiguration('launch'); - let result: debug.IConfig = null; + let result: debug.IConfig; if (types.isObject(nameOrConfig)) { result = objects.deepClone(nameOrConfig) as debug.IConfig; } else { - if (!config || !config.configurations) { - return TPromise.as(null); + if (!nameOrConfig || !config || !config.configurations || !config.configurations.length) { + return TPromise.wrapError(new Error()); } - // if the configuration name is not set yet, take the first launch config (can happen if debug viewlet has not been opened yet). - const filtered = config.configurations.filter(cfg => cfg.name === nameOrConfig); - result = filtered.length === 1 ? filtered[0] : config.configurations[0]; - result = objects.deepClone(result); + const filtered = config.configurations.filter(cfg => cfg && cfg.name === nameOrConfig); + if (filtered.length !== 1) { + const message = filtered.length === 0 ? nls.localize('configurationDoesNotExist', "Configuration '{0}' does not exist.", nameOrConfig) + : nls.localize('configuraitonNotUnique', "There are multiple configurations with name '{0}'.", nameOrConfig); + return TPromise.wrapError(new Error(message)); + } + + result = objects.deepClone(filtered[0]); } - if (result) { - // Set operating system specific properties #1873 - const setOSProperties = (flag: boolean, osConfig: debug.IEnvConfig) => { - if (flag && osConfig) { - Object.keys(osConfig).forEach(key => { - result[key] = osConfig[key]; - }); - } - }; - setOSProperties(isWindows, result.windows); - setOSProperties(isMacintosh, result.osx); - setOSProperties(isLinux, result.linux); + // Set operating system specific properties #1873 + const setOSProperties = (flag: boolean, osConfig: debug.IEnvConfig) => { + if (flag && osConfig) { + Object.keys(osConfig).forEach(key => { + result[key] = osConfig[key]; + }); + } + }; + setOSProperties(isWindows, result.windows); + setOSProperties(isMacintosh, result.osx); + setOSProperties(isLinux, result.linux); - // massage configuration attributes - append workspace path to relatvie paths, substitute variables in paths. - Object.keys(result).forEach(key => { - result[key] = this.configurationResolverService.resolveAny(result[key]); - }); + // massage configuration attributes - append workspace path to relatvie paths, substitute variables in paths. + Object.keys(result).forEach(key => { + result[key] = this.configurationResolverService.resolveAny(result[key]); + }); - const adapter = this.getAdapter(result.type); - return this.configurationResolverService.resolveInteractiveVariables(result, adapter ? adapter.variables : null); - } - return TPromise.as(null); + const adapter = this.getAdapter(result.type); + return this.configurationResolverService.resolveInteractiveVariables(result, adapter ? adapter.variables : null); } public openConfigFile(sideBySide: boolean): TPromise { -- GitLab