diff --git a/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts index 8a9adf7fe254d..a8d01a3c210ff 100644 --- a/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts @@ -5,7 +5,6 @@ import path = require('path'); import nls = require('vs/nls'); -import {sequence} from 'vs/base/common/async'; import {TPromise} from 'vs/base/common/winjs.base'; import strings = require('vs/base/common/strings'); import types = require('vs/base/common/types'); @@ -23,7 +22,6 @@ import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonCo import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; import {IFileService} from 'vs/platform/files/common/files'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; -import {ICommandService} from 'vs/platform/commands/common/commands'; import debug = require('vs/workbench/parts/debug/common/debug'); import {Adapter} from 'vs/workbench/parts/debug/node/debugAdapter'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; @@ -184,7 +182,6 @@ export class ConfigurationManager implements debug.IConfigurationManager { @IConfigurationService private configurationService: IConfigurationService, @IEnvironmentService private environmentService: IEnvironmentService, @IQuickOpenService private quickOpenService: IQuickOpenService, - @ICommandService private commandService: ICommandService, @IConfigurationResolverService private configurationResolverService: IConfigurationResolverService ) { this._onDidConfigurationChange = new Emitter(); @@ -266,59 +263,6 @@ export class ConfigurationManager implements debug.IConfigurationManager { return this.adapters.filter(adapter => strings.equalsIgnoreCase(adapter.type, this.configuration.type)).pop(); } - /** - * Resolve all interactive variables in configuration #6569 - */ - public resolveInteractiveVariables(): TPromise { - if (!this.configuration) { - return TPromise.as(null); - } - - // We need a map from interactive variables to keys because we only want to trigger an command once per key - - // even though it might occure multiple times in configuration #7026. - const interactiveVariablesToSubstitutes: { [interactiveVariable: string]: { object: any, key: string }[] } = {}; - const findInteractiveVariables = (object: any) => { - Object.keys(object).forEach(key => { - if (object[key] && typeof object[key] === 'object') { - findInteractiveVariables(object[key]); - } else if (typeof object[key] === 'string') { - const matches = /\${command.(.+)}/.exec(object[key]); - if (matches && matches.length === 2) { - const interactiveVariable = matches[1]; - if (!interactiveVariablesToSubstitutes[interactiveVariable]) { - interactiveVariablesToSubstitutes[interactiveVariable] = []; - } - interactiveVariablesToSubstitutes[interactiveVariable].push({ object, key }); - } - } - }); - }; - findInteractiveVariables(this.configuration); - - const factory: { (): TPromise }[] = Object.keys(interactiveVariablesToSubstitutes).map(interactiveVariable => { - return () => { - let commandId = null; - if (this.adapter !== null) { - commandId = this.adapter.variables ? this.adapter.variables[interactiveVariable] : null; - } - if (!commandId) { - return TPromise.wrapError(nls.localize('interactiveVariableNotFound', "Adapter {0} does not contribute variable {1} that is specified in launch configuration.", this.adapter !== null ? this.adapter.type : null, interactiveVariable)); - } else { - return this.commandService.executeCommand(commandId, this.configuration).then(result => { - if (!result) { - this.configuration.silentlyAbort = true; - } - interactiveVariablesToSubstitutes[interactiveVariable].forEach(substitute => - substitute.object[substitute.key] = substitute.object[substitute.key].replace(`\${command.${interactiveVariable}}`, result) - ); - }); - } - }; - }); - - return sequence(factory).then(() => this.configuration); - } - public setConfiguration(nameOrConfig: string|debug.IConfig): TPromise { return this.loadLaunchConfig().then(config => { if (types.isObject(nameOrConfig)) { @@ -460,4 +404,8 @@ export class ConfigurationManager implements debug.IConfigurationManager { public loadLaunchConfig(): TPromise { return TPromise.as(this.configurationService.getConfiguration('launch')); } + + public resolveInteractiveVariables(): TPromise { + return this.configurationResolverService.resolveInteractiveVariables(this.configuration, this.adapter ? this.adapter.variables : null); + } } diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts index 9aed75fd63107..e796fd11b88ff 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import {TPromise} from 'vs/base/common/winjs.base'; import {IStringDictionary} from 'vs/base/common/collections'; import {createDecorator} from 'vs/platform/instantiation/common/instantiation'; @@ -17,4 +18,5 @@ export interface IConfigurationResolverService { resolve(value: IStringDictionary): IStringDictionary; resolve(value: IStringDictionary>): IStringDictionary>; resolveAny(value: T): T; + resolveInteractiveVariables(configuration: any, interactiveVariablesMap: { [key: string]: string }): TPromise; } diff --git a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts index 09f21a8449248..31f19003d631d 100644 --- a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts @@ -3,13 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import nls = require('vs/nls'); import * as paths from 'vs/base/common/paths'; import * as types from 'vs/base/common/types'; import uri from 'vs/base/common/uri'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {sequence} from 'vs/base/common/async'; import {IStringDictionary} from 'vs/base/common/collections'; import {IConfigurationResolverService} from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import {IEnvironmentService} from 'vs/platform/environment/common/environment'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; +import {ICommandService} from 'vs/platform/commands/common/commands'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; import {asFileEditorInput} from 'vs/workbench/common/editor'; @@ -23,7 +27,8 @@ export class ConfigurationResolverService implements IConfigurationResolverServi envVariables: { [key: string]: string }, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IEnvironmentService private environmentService: IEnvironmentService, - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @ICommandService private commandService: ICommandService ) { this._workspaceRoot = paths.normalize(workspaceRoot ? workspaceRoot.fsPath : '', true); this._execPath = environmentService.execPath; @@ -163,4 +168,56 @@ export class ConfigurationResolverService implements IConfigurationResolverServi private resolveAnyArray(value: any[]): any[] { return value.map(s => this.resolveAny(s)); } + + /** + * Resolve all interactive variables in configuration #6569 + */ + public resolveInteractiveVariables(configuration: any, interactiveVariablesMap: { [key: string]: string }): TPromise { + if (!configuration) { + return TPromise.as(null); + } + + // We need a map from interactive variables to keys because we only want to trigger an command once per key - + // even though it might occure multiple times in configuration #7026. + const interactiveVariablesToSubstitutes: { [interactiveVariable: string]: { object: any, key: string }[] } = {}; + const findInteractiveVariables = (object: any) => { + Object.keys(object).forEach(key => { + if (object[key] && typeof object[key] === 'object') { + findInteractiveVariables(object[key]); + } else if (typeof object[key] === 'string') { + const matches = /\${command.(.+)}/.exec(object[key]); + if (matches && matches.length === 2) { + const interactiveVariable = matches[1]; + if (!interactiveVariablesToSubstitutes[interactiveVariable]) { + interactiveVariablesToSubstitutes[interactiveVariable] = []; + } + interactiveVariablesToSubstitutes[interactiveVariable].push({ object, key }); + } + } + }); + }; + findInteractiveVariables(configuration); + + const factory: { (): TPromise }[] = Object.keys(interactiveVariablesToSubstitutes).map(interactiveVariable => { + return () => { + let commandId = null; + commandId = interactiveVariablesMap ? interactiveVariablesMap[interactiveVariable] : null; + if (!commandId) { + return TPromise.wrapError(nls.localize('interactiveVariableNotFound', "Interactive variable {0} is not contributed but is specified in a configuration.", interactiveVariable)); + } else { + return this.commandService.executeCommand(commandId, configuration).then(result => { + if (!result) { + // TODO@Isidor remove this hack + configuration.silentlyAbort = true; + } + interactiveVariablesToSubstitutes[interactiveVariable].forEach(substitute => + substitute.object[substitute.key] = substitute.object[substitute.key].replace(`\${command.${interactiveVariable}}`, result) + ); + }); + } + }; + }); + + return sequence(factory).then(() => configuration); + } } diff --git a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts index cf17d8f11a9c6..8e68db45bf7a8 100644 --- a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts @@ -9,7 +9,7 @@ import platform = require('vs/base/common/platform'); import {IConfigurationService, getConfigurationValue} from 'vs/platform/configuration/common/configuration'; import {IConfigurationResolverService} from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import {ConfigurationResolverService} from 'vs/workbench/services/configurationResolver/node/configurationResolverService'; -import {TestEnvironmentService, TestConfigurationService, TestEditorService} from 'vs/test/utils/servicesTestUtils'; +import {TestEnvironmentService, TestConfigurationService, TestEditorService, } from 'vs/test/utils/servicesTestUtils'; import {TPromise} from 'vs/base/common/winjs.base'; suite('Configuration Resolver Service', () => { @@ -17,7 +17,7 @@ suite('Configuration Resolver Service', () => { let envVariables: { [key: string]: string } = { key1: 'Value for Key1', key2: 'Value for Key2' }; setup(() => { - configurationResolverService = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, new TestConfigurationService()); + configurationResolverService = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, new TestConfigurationService(), null); }); teardown(() => { @@ -70,7 +70,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService); + let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService, null); assert.strictEqual(service.resolve('abc ${config.editor.fontFamily} xyz'), 'abc foo xyz'); }); @@ -87,7 +87,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService); + let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService, null); assert.strictEqual(service.resolve('abc ${config.editor.fontFamily} ${config.terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz'); }); @@ -104,7 +104,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService); + let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService, null); if (platform.isWindows) { assert.strictEqual(service.resolve('abc ${config.editor.fontFamily} ${config.terminal.integrated.fontFamily} xyz'), 'abc foo \\VSCode\\workspaceLocation bar bar xyz'); } else { @@ -125,7 +125,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService); + let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService, null); if (platform.isWindows) { assert.strictEqual(service.resolve('abc ${config.editor.fontFamily} ${config.terminal.integrated.fontFamily} xyz'), 'abc foo \\VSCode\\workspaceLocation bar bar xyz'); } else { @@ -146,7 +146,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService); + let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService, null); if (platform.isWindows) { assert.strictEqual(service.resolve('abc ${config.editor.fontFamily} ${workspaceRoot} ${env.key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for Key1 xyz'); } else { @@ -167,7 +167,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService); + let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService, null); if (platform.isWindows) { assert.strictEqual(service.resolve('${config.editor.fontFamily} ${config.terminal.integrated.fontFamily} ${workspaceRoot} - ${workspaceRoot} ${env.key1} - ${env.key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for Key1 - Value for Key2'); } else { @@ -201,7 +201,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService); + let service = new ConfigurationResolverService(uri.parse('file:///VSCode/workspaceLocation'), envVariables, new TestEditorService(), TestEnvironmentService, configurationService, null); assert.strictEqual(service.resolve('abc ${config.editor.fontFamily} ${config.editor.lineNumbers} ${config.editor.insertSpaces} ${config.json.schemas[0].fileMatch[1]} xyz'), 'abc foo 123 false {{/myOtherfile}} xyz'); }); });