mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Merge from master
This commit is contained in:
@@ -24,9 +24,31 @@ export interface IConfigurationResolverService {
|
||||
resolveAny(folder: IWorkspaceFolder, config: any, commandValueMapping?: IStringDictionary<string>): any;
|
||||
|
||||
/**
|
||||
* Recursively resolves all variables (including commands) in the given config and returns a copy of it with substituted values.
|
||||
* If a "variables" dictionary (with names -> command ids) is given,
|
||||
* command variables are first mapped through it before being resolved.
|
||||
* Recursively resolves all variables (including commands and user input) in the given config and returns a copy of it with substituted values.
|
||||
* If a "variables" dictionary (with names -> command ids) is given, command variables are first mapped through it before being resolved.
|
||||
* @param folder
|
||||
* @param config
|
||||
* @param section For example, 'tasks' or 'debug'. Used for resolving inputs.
|
||||
* @param variables Aliases for commands.
|
||||
*/
|
||||
resolveWithCommands(folder: IWorkspaceFolder, config: any, variables?: IStringDictionary<string>): TPromise<any>;
|
||||
resolveWithInteractionReplace(folder: IWorkspaceFolder, config: any, section?: string, variables?: IStringDictionary<string>): TPromise<any>;
|
||||
|
||||
/**
|
||||
* Similar to resolveWithInteractionReplace, except without the replace. Returns a map of variables and their resolution.
|
||||
* Keys in the map will be of the format input:variableName or command:variableName.
|
||||
*/
|
||||
resolveWithInteraction(folder: IWorkspaceFolder, config: any, section?: string, variables?: IStringDictionary<string>): TPromise<Map<string, string>>;
|
||||
}
|
||||
|
||||
export const enum ConfiguredInputType {
|
||||
PromptString,
|
||||
PickString
|
||||
}
|
||||
|
||||
export interface ConfiguredInput {
|
||||
id: string;
|
||||
description: string;
|
||||
default?: string;
|
||||
type: ConfiguredInputType;
|
||||
options?: string[];
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
export const inputsSchema: IJSONSchema = {
|
||||
definitions: {
|
||||
inputs: {
|
||||
type: 'array',
|
||||
description: nls.localize('JsonSchema.inputs', 'User inputs. Used for defining user input prompts, such as free string input or a choice from several options.'),
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['id', 'type', 'description'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: nls.localize('JsonSchema.input.id', "The input\'s id is used to specify inputs as ${input:id}.")
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
description: nls.localize('JsonSchema.input.type', 'The promptString type opens an input box to ask the user for input. The pickString type shows a selection list.'),
|
||||
enum: ['promptString', 'pickString']
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: nls.localize('JsonSchema.input.description', 'The description is shown when the user is prompted for input.'),
|
||||
},
|
||||
default: {
|
||||
type: 'string',
|
||||
description: nls.localize('JsonSchema.input.default', 'The default value for the input.'),
|
||||
},
|
||||
options: {
|
||||
type: 'array',
|
||||
description: nls.localize('JsonSchema.input.options', 'An array of strings that defines the options for a quick pick.'),
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as nls from 'vs/nls';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
export function applyDeprecatedVariableMessage(schema: IJSONSchema) {
|
||||
schema.pattern = schema.pattern || '^(?!.*\\$\\{(env|config|command)\\.)';
|
||||
schema.patternErrorMessage = schema.patternErrorMessage ||
|
||||
nls.localize('deprecatedVariables', "'env.', 'config.' and 'command.' are deprecated, use 'env:', 'config:' and 'command:' instead.");
|
||||
}
|
||||
@@ -3,24 +3,27 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { sequence } from 'vs/base/common/async';
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import { IStringDictionary, size } from 'vs/base/common/collections';
|
||||
import { IStringDictionary, forEach, fromMap } from 'vs/base/common/collections';
|
||||
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 { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceFolder, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { IQuickInputService, IInputOptions, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { ConfiguredInput, ConfiguredInputType } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
|
||||
export class ConfigurationResolverService extends AbstractVariableResolverService {
|
||||
|
||||
@@ -28,9 +31,10 @@ export class ConfigurationResolverService extends AbstractVariableResolverServic
|
||||
envVariables: platform.IProcessEnvironment,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@ICommandService private commandService: ICommandService,
|
||||
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService
|
||||
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
|
||||
@IQuickInputService private quickInputService: IQuickInputService
|
||||
) {
|
||||
super({
|
||||
getFolderUri: (folderName: string): uri => {
|
||||
@@ -79,72 +83,82 @@ export class ConfigurationResolverService extends AbstractVariableResolverServic
|
||||
}, envVariables);
|
||||
}
|
||||
|
||||
public resolveWithCommands(folder: IWorkspaceFolder, config: any, variables?: IStringDictionary<string>): TPromise<any> {
|
||||
|
||||
// then substitute remaining variables in VS Code core
|
||||
public resolveWithInteractionReplace(folder: IWorkspaceFolder, config: any, section?: string, variables?: IStringDictionary<string>): TPromise<any> {
|
||||
// resolve any non-interactive variables
|
||||
config = this.resolveAny(folder, config);
|
||||
|
||||
// now evaluate command variables (which might have a UI)
|
||||
return this.executeCommandVariables(config, variables).then(commandValueMapping => {
|
||||
|
||||
if (!commandValueMapping) { // cancelled by user
|
||||
return null;
|
||||
}
|
||||
|
||||
// resolve input variables in the order in which they are encountered
|
||||
return this.resolveWithInteraction(folder, config, section, variables).then(mapping => {
|
||||
// finally substitute evaluated command variables (if there are any)
|
||||
if (size<string>(commandValueMapping) > 0) {
|
||||
return this.resolveAny(folder, config, commandValueMapping);
|
||||
if (!mapping) {
|
||||
return null;
|
||||
} else if (mapping.size > 0) {
|
||||
return this.resolveAny(folder, config, fromMap(mapping));
|
||||
} else {
|
||||
return config;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public resolveWithInteraction(folder: IWorkspaceFolder, config: any, section?: string, variables?: IStringDictionary<string>): TPromise<Map<string, string>> {
|
||||
// resolve any non-interactive variables
|
||||
const resolved = this.resolveAnyMap(folder, config);
|
||||
config = resolved.newConfig;
|
||||
const allVariableMapping: Map<string, string> = resolved.resolvedVariables;
|
||||
|
||||
// resolve input variables in the order in which they are encountered
|
||||
return this.resolveWithInputs(folder, config, section).then(inputMapping => {
|
||||
if (!this.updateMapping(inputMapping, allVariableMapping)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// resolve commands in the order in which they are encountered
|
||||
return this.resolveWithCommands(config, variables).then(commandMapping => {
|
||||
if (!this.updateMapping(commandMapping, allVariableMapping)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return allVariableMapping;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all items from newMapping to fullMapping. Returns false if newMapping is undefined.
|
||||
*/
|
||||
private updateMapping(newMapping: IStringDictionary<string>, fullMapping: Map<string, string>): boolean {
|
||||
if (!newMapping) {
|
||||
return false;
|
||||
}
|
||||
forEach(newMapping, (entry) => {
|
||||
fullMapping.set(entry.key, entry.value);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and executes all command variables in the given configuration and returns their values as a dictionary.
|
||||
* Please note: this method does not substitute the command variables (so the configuration is not modified).
|
||||
* The returned dictionary can be passed to "resolvePlatform" for the substitution.
|
||||
* See #6569.
|
||||
* @param configuration
|
||||
* @param variableToCommandMap Aliases for commands
|
||||
*/
|
||||
private executeCommandVariables(configuration: any, variableToCommandMap: IStringDictionary<string>): TPromise<IStringDictionary<string>> {
|
||||
|
||||
private resolveWithCommands(configuration: any, variableToCommandMap: IStringDictionary<string>): TPromise<IStringDictionary<string>> {
|
||||
if (!configuration) {
|
||||
return TPromise.as(null);
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
|
||||
// use an array to preserve order of first appearance
|
||||
const commands: string[] = [];
|
||||
|
||||
const cmd_var = /\${command:(.*?)}/g;
|
||||
|
||||
const findCommandVariables = (object: any) => {
|
||||
Object.keys(object).forEach(key => {
|
||||
const value = object[key];
|
||||
if (value && typeof value === 'object') {
|
||||
findCommandVariables(value);
|
||||
} else if (typeof value === 'string') {
|
||||
let matches;
|
||||
while ((matches = cmd_var.exec(value)) !== null) {
|
||||
if (matches.length === 2) {
|
||||
const command = matches[1];
|
||||
if (commands.indexOf(command) < 0) {
|
||||
commands.push(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
findCommandVariables(configuration);
|
||||
|
||||
const commands: string[] = [];
|
||||
this.findVariables(cmd_var, configuration, commands);
|
||||
let cancelled = false;
|
||||
const commandValueMapping: IStringDictionary<string> = Object.create(null);
|
||||
|
||||
const factory: { (): TPromise<any> }[] = commands.map(commandVariable => {
|
||||
return () => {
|
||||
|
||||
let commandId = variableToCommandMap ? variableToCommandMap[commandVariable] : null;
|
||||
let commandId = variableToCommandMap ? variableToCommandMap[commandVariable] : undefined;
|
||||
if (!commandId) {
|
||||
// Just launch any command if the interactive variable is not contributed by the adapter #12735
|
||||
commandId = commandVariable;
|
||||
@@ -152,8 +166,8 @@ export class ConfigurationResolverService extends AbstractVariableResolverServic
|
||||
|
||||
return this.commandService.executeCommand<string>(commandId, configuration).then(result => {
|
||||
if (typeof result === 'string') {
|
||||
commandValueMapping[commandVariable] = result;
|
||||
} else if (isUndefinedOrNull(result)) {
|
||||
commandValueMapping['command:' + commandVariable] = result;
|
||||
} else if (Types.isUndefinedOrNull(result)) {
|
||||
cancelled = true;
|
||||
} else {
|
||||
throw new Error(nls.localize('stringsOnlySupported', "Command '{0}' did not return a string result. Only strings are supported as results for commands used for variable substitution.", commandVariable));
|
||||
@@ -162,6 +176,160 @@ export class ConfigurationResolverService extends AbstractVariableResolverServic
|
||||
};
|
||||
});
|
||||
|
||||
return sequence(factory).then(() => cancelled ? null : commandValueMapping);
|
||||
return sequence(factory).then(() => cancelled ? undefined : commandValueMapping);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all inputs in a configuration and returns a map that maps the unresolved input to the resolved input.
|
||||
* Does not do replacement of inputs.
|
||||
* @param folder
|
||||
* @param config
|
||||
* @param section
|
||||
*/
|
||||
public resolveWithInputs(folder: IWorkspaceFolder, config: any, section: string): Promise<IStringDictionary<string>> {
|
||||
if (!config) {
|
||||
return Promise.resolve(undefined);
|
||||
} else if (folder && section) {
|
||||
// Get all the possible inputs
|
||||
let result = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY
|
||||
? Objects.deepClone(this.configurationService.getValue<any>(section, { resource: folder.uri }))
|
||||
: undefined;
|
||||
let inputsArray = result ? this.parseConfigurationInputs(result.inputs) : undefined;
|
||||
const inputs = new Map<string, ConfiguredInput>();
|
||||
if (inputsArray) {
|
||||
inputsArray.forEach(input => {
|
||||
inputs.set(input.id, input);
|
||||
});
|
||||
|
||||
// use an array to preserve order of first appearance
|
||||
const input_var = /\${input:(.*?)}/g;
|
||||
const commands: string[] = [];
|
||||
this.findVariables(input_var, config, commands);
|
||||
let cancelled = false;
|
||||
const commandValueMapping: IStringDictionary<string> = Object.create(null);
|
||||
|
||||
const factory: { (): Promise<any> }[] = commands.map(commandVariable => {
|
||||
return () => {
|
||||
return this.showUserInput(commandVariable, inputs).then(resolvedValue => {
|
||||
if (resolvedValue) {
|
||||
commandValueMapping['input:' + commandVariable] = resolvedValue;
|
||||
} else {
|
||||
cancelled = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
}, reason => {
|
||||
return Promise.reject(reason);
|
||||
});
|
||||
|
||||
return sequence(factory).then(() => cancelled ? undefined : commandValueMapping);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(Object.create(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the provided input info and shows the quick pick so the user can provide the value for the input
|
||||
* @param commandVariable Name of the input.
|
||||
* @param inputs Information about each possible input.
|
||||
* @param commandValueMapping
|
||||
*/
|
||||
private showUserInput(commandVariable: string, inputs: Map<string, ConfiguredInput>): Promise<string> {
|
||||
if (inputs && inputs.has(commandVariable)) {
|
||||
const input = inputs.get(commandVariable);
|
||||
if (input.type === ConfiguredInputType.PromptString) {
|
||||
let inputOptions: IInputOptions = { prompt: input.description };
|
||||
if (input.default) {
|
||||
inputOptions.value = input.default;
|
||||
}
|
||||
|
||||
return this.quickInputService.input(inputOptions).then(resolvedInput => {
|
||||
return resolvedInput ? resolvedInput : undefined;
|
||||
});
|
||||
} else { // input.type === ConfiguredInputType.pick
|
||||
let picks = new Array<IQuickPickItem>();
|
||||
if (input.options) {
|
||||
input.options.forEach(pickOption => {
|
||||
let item: IQuickPickItem = { label: pickOption };
|
||||
if (input.default && (pickOption === input.default)) {
|
||||
item.description = nls.localize('defaultInputValue', "Default");
|
||||
picks.unshift(item);
|
||||
} else {
|
||||
picks.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
let pickOptions: IPickOptions<IQuickPickItem> = { placeHolder: input.description };
|
||||
return this.quickInputService.pick(picks, pickOptions, undefined).then(resolvedInput => {
|
||||
return resolvedInput ? resolvedInput.label : undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.reject(new Error(nls.localize('undefinedInputVariable', "Undefined input variable {0} encountered. Remove or define {0} to continue.", commandVariable)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all variables in object using cmdVar and pushes them into commands.
|
||||
* @param cmdVar Regex to use for finding variables.
|
||||
* @param object object is searched for variables.
|
||||
* @param commands All found variables are returned in commands.
|
||||
*/
|
||||
private findVariables(cmdVar: RegExp, object: any, commands: string[]) {
|
||||
if (!object) {
|
||||
return;
|
||||
} else if (typeof object === 'string') {
|
||||
let matches;
|
||||
while ((matches = cmdVar.exec(object)) !== null) {
|
||||
if (matches.length === 2) {
|
||||
const command = matches[1];
|
||||
if (commands.indexOf(command) < 0) {
|
||||
commands.push(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Types.isArray(object)) {
|
||||
object.forEach(value => {
|
||||
this.findVariables(cmdVar, value, commands);
|
||||
});
|
||||
} else {
|
||||
Object.keys(object).forEach(key => {
|
||||
const value = object[key];
|
||||
this.findVariables(cmdVar, value, commands);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of inputs into an actaul array of typed, ConfiguredInputs.
|
||||
* @param object Array of something that should look like inputs.
|
||||
*/
|
||||
private parseConfigurationInputs(object: any[]): ConfiguredInput[] | undefined {
|
||||
let inputs = new Array<ConfiguredInput>();
|
||||
if (object) {
|
||||
object.forEach(item => {
|
||||
if (Types.isString(item.id) && Types.isString(item.description) && Types.isString(item.type)) {
|
||||
let type: ConfiguredInputType;
|
||||
switch (item.type) {
|
||||
case 'promptString': type = ConfiguredInputType.PromptString; break;
|
||||
case 'pickString': type = ConfiguredInputType.PickString; break;
|
||||
default: {
|
||||
throw new Error(nls.localize('unknownInputTypeProvided', "Input '{0}' can only be of type 'promptString' or 'pickString'.", item.id));
|
||||
}
|
||||
}
|
||||
let options: string[];
|
||||
if (type === ConfiguredInputType.PickString) {
|
||||
if (Types.isStringArray(item.options)) {
|
||||
options = item.options;
|
||||
} else {
|
||||
throw new Error(nls.localize('pickStringRequiresOptions', "Input '{0}' is of type 'pickString' and must include 'options'.", item.id));
|
||||
}
|
||||
}
|
||||
inputs.push({ id: item.id, description: item.description, type, default: item.default, options });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return inputs;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import { relative } from 'path';
|
||||
import { IProcessEnvironment, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
import { localize } from 'vs/nls';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
@@ -51,7 +51,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
||||
return this.recursiveResolve(root ? root.uri : undefined, value);
|
||||
}
|
||||
|
||||
public resolveAny(workspaceFolder: IWorkspaceFolder, config: any, commandValueMapping?: IStringDictionary<string>): any {
|
||||
public resolveAnyBase(workspaceFolder: IWorkspaceFolder, config: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): any {
|
||||
|
||||
const result = objects.deepClone(config) as any;
|
||||
|
||||
@@ -70,35 +70,58 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
||||
delete result.linux;
|
||||
|
||||
// substitute all variables recursively in string values
|
||||
return this.recursiveResolve(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping);
|
||||
return this.recursiveResolve(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables);
|
||||
}
|
||||
|
||||
public resolveWithCommands(folder: IWorkspaceFolder, config: any): TPromise<any> {
|
||||
throw new Error('resolveWithCommands not implemented.');
|
||||
public resolveAny(workspaceFolder: IWorkspaceFolder, config: any, commandValueMapping?: IStringDictionary<string>): any {
|
||||
return this.resolveAnyBase(workspaceFolder, config, commandValueMapping);
|
||||
}
|
||||
|
||||
private recursiveResolve(folderUri: uri, value: any, commandValueMapping?: IStringDictionary<string>): any {
|
||||
public resolveAnyMap(workspaceFolder: IWorkspaceFolder, config: any, commandValueMapping?: IStringDictionary<string>): { newConfig: any, resolvedVariables: Map<string, string> } {
|
||||
const resolvedVariables = new Map<string, string>();
|
||||
const newConfig = this.resolveAnyBase(workspaceFolder, config, commandValueMapping, resolvedVariables);
|
||||
return { newConfig, resolvedVariables };
|
||||
}
|
||||
|
||||
public resolveWithInteractionReplace(folder: IWorkspaceFolder, config: any): TPromise<any> {
|
||||
throw new Error('resolveWithInteractionReplace not implemented.');
|
||||
}
|
||||
|
||||
public resolveWithInteraction(folder: IWorkspaceFolder, config: any): TPromise<any> {
|
||||
throw new Error('resolveWithInteraction not implemented.');
|
||||
}
|
||||
|
||||
private recursiveResolve(folderUri: uri, value: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): any {
|
||||
if (types.isString(value)) {
|
||||
return this.resolveString(folderUri, value, commandValueMapping);
|
||||
const resolved = this.resolveString(folderUri, value, commandValueMapping);
|
||||
if (resolvedVariables) {
|
||||
resolvedVariables.set(resolved.variableName, resolved.resolvedValue);
|
||||
}
|
||||
return resolved.replaced;
|
||||
} else if (types.isArray(value)) {
|
||||
return value.map(s => this.recursiveResolve(folderUri, s, commandValueMapping));
|
||||
return value.map(s => this.recursiveResolve(folderUri, s, commandValueMapping, resolvedVariables));
|
||||
} else if (types.isObject(value)) {
|
||||
let result: IStringDictionary<string | IStringDictionary<string> | string[]> = Object.create(null);
|
||||
Object.keys(value).forEach(key => {
|
||||
const resolvedKey = this.resolveString(folderUri, key, commandValueMapping);
|
||||
result[resolvedKey] = this.recursiveResolve(folderUri, value[key], commandValueMapping);
|
||||
if (resolvedVariables) {
|
||||
resolvedVariables.set(resolvedKey.variableName, resolvedKey.resolvedValue);
|
||||
}
|
||||
result[resolvedKey.replaced] = this.recursiveResolve(folderUri, value[key], commandValueMapping, resolvedVariables);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private resolveString(folderUri: uri, value: string, commandValueMapping: IStringDictionary<string>): string {
|
||||
private resolveString(folderUri: uri, value: string, commandValueMapping: IStringDictionary<string>): { replaced: string, variableName: string, resolvedValue: string } {
|
||||
|
||||
const filePath = this._context.getFilePath();
|
||||
let variableName: string;
|
||||
let resolvedValue: string;
|
||||
const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => {
|
||||
|
||||
return value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => {
|
||||
|
||||
variableName = variable;
|
||||
let argument: string;
|
||||
const parts = variable.split(':');
|
||||
if (parts && parts.length > 1) {
|
||||
@@ -115,10 +138,10 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
||||
}
|
||||
const env = this._envVariables[argument];
|
||||
if (types.isString(env)) {
|
||||
return env;
|
||||
return resolvedValue = env;
|
||||
}
|
||||
// For `env` we should do the same as a normal shell does - evaluates missing envs to an empty string #46436
|
||||
return '';
|
||||
return resolvedValue = '';
|
||||
}
|
||||
throw new Error(localize('missingEnvVarName', "'{0}' can not be resolved because no environment variable name is given.", match));
|
||||
|
||||
@@ -131,19 +154,14 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
||||
if (types.isObject(config)) {
|
||||
throw new Error(localize('configNoString', "'{0}' can not be resolved because '{1}' is a structured value.", match, argument));
|
||||
}
|
||||
return config;
|
||||
return resolvedValue = config;
|
||||
}
|
||||
throw new Error(localize('missingConfigName', "'{0}' can not be resolved because no settings name is given.", match));
|
||||
|
||||
case 'command':
|
||||
if (argument && commandValueMapping) {
|
||||
const v = commandValueMapping[argument];
|
||||
if (typeof v === 'string') {
|
||||
return v;
|
||||
}
|
||||
throw new Error(localize('noValueForCommand', "'{0}' can not be resolved because the command has no value.", match));
|
||||
}
|
||||
return match;
|
||||
return resolvedValue = this.resolveFromMap(match, argument, commandValueMapping, 'command');
|
||||
case 'input':
|
||||
return resolvedValue = this.resolveFromMap(match, argument, commandValueMapping, 'input');
|
||||
|
||||
default: {
|
||||
|
||||
@@ -192,63 +210,75 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
||||
switch (variable) {
|
||||
case 'workspaceRoot':
|
||||
case 'workspaceFolder':
|
||||
return normalizeDriveLetter(folderUri.fsPath);
|
||||
return resolvedValue = normalizeDriveLetter(folderUri.fsPath);
|
||||
|
||||
case 'cwd':
|
||||
return folderUri ? normalizeDriveLetter(folderUri.fsPath) : process.cwd();
|
||||
return resolvedValue = (folderUri ? normalizeDriveLetter(folderUri.fsPath) : process.cwd());
|
||||
|
||||
case 'workspaceRootFolderName':
|
||||
case 'workspaceFolderBasename':
|
||||
return paths.basename(folderUri.fsPath);
|
||||
return resolvedValue = paths.basename(folderUri.fsPath);
|
||||
|
||||
case 'lineNumber':
|
||||
const lineNumber = this._context.getLineNumber();
|
||||
if (lineNumber) {
|
||||
return lineNumber;
|
||||
return resolvedValue = lineNumber;
|
||||
}
|
||||
throw new Error(localize('canNotResolveLineNumber', "'{0}' can not be resolved. Make sure to have a line selected in the active editor.", match));
|
||||
|
||||
case 'selectedText':
|
||||
const selectedText = this._context.getSelectedText();
|
||||
if (selectedText) {
|
||||
return selectedText;
|
||||
return resolvedValue = selectedText;
|
||||
}
|
||||
throw new Error(localize('canNotResolveSelectedText', "'{0}' can not be resolved. Make sure to have some text selected in the active editor.", match));
|
||||
|
||||
case 'file':
|
||||
return filePath;
|
||||
return resolvedValue = filePath;
|
||||
|
||||
case 'relativeFile':
|
||||
if (folderUri) {
|
||||
return paths.normalize(relative(folderUri.fsPath, filePath));
|
||||
return resolvedValue = paths.normalize(relative(folderUri.fsPath, filePath));
|
||||
}
|
||||
return filePath;
|
||||
return resolvedValue = filePath;
|
||||
|
||||
case 'fileDirname':
|
||||
return paths.dirname(filePath);
|
||||
return resolvedValue = paths.dirname(filePath);
|
||||
|
||||
case 'fileExtname':
|
||||
return paths.extname(filePath);
|
||||
return resolvedValue = paths.extname(filePath);
|
||||
|
||||
case 'fileBasename':
|
||||
return paths.basename(filePath);
|
||||
return resolvedValue = paths.basename(filePath);
|
||||
|
||||
case 'fileBasenameNoExtension':
|
||||
const basename = paths.basename(filePath);
|
||||
return basename.slice(0, basename.length - paths.extname(basename).length);
|
||||
return resolvedValue = (basename.slice(0, basename.length - paths.extname(basename).length));
|
||||
|
||||
case 'execPath':
|
||||
const ep = this._context.getExecPath();
|
||||
if (ep) {
|
||||
return ep;
|
||||
return resolvedValue = ep;
|
||||
}
|
||||
return match;
|
||||
return resolvedValue = match;
|
||||
|
||||
default:
|
||||
return match;
|
||||
return resolvedValue = match;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return { replaced, variableName, resolvedValue };
|
||||
}
|
||||
|
||||
private resolveFromMap(match: string, argument: string, commandValueMapping: IStringDictionary<string>, prefix: string): string {
|
||||
if (argument && commandValueMapping) {
|
||||
const v = commandValueMapping[prefix + ':' + argument];
|
||||
if (typeof v === 'string') {
|
||||
return v;
|
||||
}
|
||||
throw new Error(localize('noValueForCommand', "'{0}' can not be resolved because the command has no value.", match));
|
||||
}
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IConfigurationService, getConfigurationValue, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
@@ -15,6 +14,9 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestEnvironmentService, TestEditorService, TestContextService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IQuickInputService, IQuickPickItem, QuickPickInput, IPickOptions, Omit, IInputOptions, IQuickInputButton, IQuickPick, IInputBox, IQuickNavigateConfiguration } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationToken } from 'vscode';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
|
||||
suite('Configuration Resolver Service', () => {
|
||||
let configurationResolverService: IConfigurationResolverService;
|
||||
@@ -22,17 +24,19 @@ suite('Configuration Resolver Service', () => {
|
||||
let mockCommandService: MockCommandService;
|
||||
let editorService: TestEditorService;
|
||||
let workspace: IWorkspaceFolder;
|
||||
let quickInputService: MockQuickInputService;
|
||||
|
||||
setup(() => {
|
||||
mockCommandService = new MockCommandService();
|
||||
editorService = new TestEditorService();
|
||||
quickInputService = new MockQuickInputService();
|
||||
workspace = {
|
||||
uri: uri.parse('file:///VSCode/workspaceLocation'),
|
||||
name: 'hey',
|
||||
index: 0,
|
||||
toResource: () => null
|
||||
};
|
||||
configurationResolverService = new ConfigurationResolverService(envVariables, editorService, TestEnvironmentService, new TestConfigurationService(), mockCommandService, new TestContextService());
|
||||
configurationResolverService = new ConfigurationResolverService(envVariables, editorService, TestEnvironmentService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(), quickInputService);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
@@ -117,7 +121,7 @@ suite('Configuration Resolver Service', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService());
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService);
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz');
|
||||
});
|
||||
|
||||
@@ -134,7 +138,7 @@ suite('Configuration Resolver Service', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService());
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService);
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz');
|
||||
});
|
||||
|
||||
@@ -151,7 +155,7 @@ suite('Configuration Resolver Service', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService());
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService);
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${workspaceFolder} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for key1 xyz');
|
||||
} else {
|
||||
@@ -172,7 +176,7 @@ suite('Configuration Resolver Service', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService());
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService);
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(service.resolve(workspace, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceFolder} - ${workspaceFolder} ${env:key1} - ${env:key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for key1 - Value for key2');
|
||||
} else {
|
||||
@@ -206,7 +210,7 @@ suite('Configuration Resolver Service', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService());
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService);
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz');
|
||||
});
|
||||
|
||||
@@ -216,7 +220,7 @@ suite('Configuration Resolver Service', () => {
|
||||
editor: {}
|
||||
});
|
||||
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService());
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService);
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${unknownVariable} xyz'), 'abc ${unknownVariable} xyz');
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${env:unknownVariable} xyz'), 'abc xyz');
|
||||
});
|
||||
@@ -229,7 +233,7 @@ suite('Configuration Resolver Service', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService());
|
||||
let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService);
|
||||
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${env} xyz'));
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${env:} xyz'));
|
||||
@@ -252,7 +256,7 @@ suite('Configuration Resolver Service', () => {
|
||||
'outDir': null
|
||||
};
|
||||
|
||||
return configurationResolverService.resolveWithCommands(undefined, configuration).then(result => {
|
||||
return configurationResolverService.resolveWithInteractionReplace(undefined, configuration).then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
@@ -281,7 +285,7 @@ suite('Configuration Resolver Service', () => {
|
||||
const commandVariables = Object.create(null);
|
||||
commandVariables['commandVariable1'] = 'command1';
|
||||
|
||||
return configurationResolverService.resolveWithCommands(undefined, configuration, commandVariables).then(result => {
|
||||
return configurationResolverService.resolveWithInteractionReplace(undefined, configuration, undefined, commandVariables).then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
@@ -314,7 +318,7 @@ suite('Configuration Resolver Service', () => {
|
||||
const commandVariables = Object.create(null);
|
||||
commandVariables['commandVariable1'] = 'command1';
|
||||
|
||||
return configurationResolverService.resolveWithCommands(undefined, configuration, commandVariables).then(result => {
|
||||
return configurationResolverService.resolveWithInteractionReplace(undefined, configuration, undefined, commandVariables).then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
@@ -345,7 +349,7 @@ suite('Configuration Resolver Service', () => {
|
||||
const commandVariables = Object.create(null);
|
||||
commandVariables['commandVariable1'] = 'command1';
|
||||
|
||||
return configurationResolverService.resolveWithCommands(undefined, configuration, commandVariables).then(result => {
|
||||
return configurationResolverService.resolveWithInteractionReplace(undefined, configuration, undefined, commandVariables).then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
@@ -355,6 +359,87 @@ suite('Configuration Resolver Service', () => {
|
||||
'value': 'Value for key1'
|
||||
});
|
||||
|
||||
assert.equal(1, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
test('a single prompt input variable', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': '${input:input1}',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
};
|
||||
|
||||
return configurationResolverService.resolveWithInteractionReplace(workspace, configuration, 'tasks').then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': 'resolvedEnterinput1',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
});
|
||||
|
||||
assert.equal(0, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
test('a single pick input variable', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': '${input:input2}',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
};
|
||||
|
||||
return configurationResolverService.resolveWithInteractionReplace(workspace, configuration, 'tasks').then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': 'selectedPick',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
});
|
||||
|
||||
assert.equal(0, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
test('several input variables and command', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': '${input:input3}',
|
||||
'type': '${command:command1}',
|
||||
'request': '${input:input1}',
|
||||
'processId': '${input:input2}',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
};
|
||||
|
||||
return configurationResolverService.resolveWithInteractionReplace(workspace, configuration, 'tasks').then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'resolvedEnterinput3',
|
||||
'type': 'command1-result',
|
||||
'request': 'resolvedEnterinput1',
|
||||
'processId': 'selectedPick',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
});
|
||||
|
||||
assert.equal(1, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
@@ -381,7 +466,7 @@ class MockConfigurationService implements IConfigurationService {
|
||||
|
||||
return object;
|
||||
}
|
||||
public updateValue(): TPromise<void> { return null; }
|
||||
public updateValue(): Promise<void> { return null; }
|
||||
public getConfigurationData(): any { return null; }
|
||||
public onDidChangeConfiguration() { return { dispose() { } }; }
|
||||
public reloadConfiguration() { return null; }
|
||||
@@ -393,7 +478,7 @@ class MockCommandService implements ICommandService {
|
||||
public callCount = 0;
|
||||
|
||||
onWillExecuteCommand = () => Disposable.None;
|
||||
public executeCommand(commandId: string, ...args: any[]): TPromise<any> {
|
||||
public executeCommand(commandId: string, ...args: any[]): Promise<any> {
|
||||
this.callCount++;
|
||||
|
||||
let result = `${commandId}-result`;
|
||||
@@ -403,6 +488,90 @@ class MockCommandService implements ICommandService {
|
||||
}
|
||||
}
|
||||
|
||||
return TPromise.as(result);
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
|
||||
class MockQuickInputService implements IQuickInputService {
|
||||
_serviceBrand: any;
|
||||
|
||||
public pick<T extends IQuickPickItem>(picks: Thenable<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: IPickOptions<T> & { canPickMany: true }, token?: CancellationToken): Promise<T[]>;
|
||||
public pick<T extends IQuickPickItem>(picks: Thenable<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: IPickOptions<T> & { canPickMany: false }, token?: CancellationToken): Promise<T>;
|
||||
public pick<T extends IQuickPickItem>(picks: Thenable<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: Omit<IPickOptions<T>, 'canPickMany'>, token?: CancellationToken): Promise<T> {
|
||||
if (Types.isArray(picks)) {
|
||||
return Promise.resolve(<T>{ label: 'selectedPick', description: 'pick description' });
|
||||
} else {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public input(options?: IInputOptions, token?: CancellationToken): Promise<string> {
|
||||
return Promise.resolve('resolved' + options.prompt);
|
||||
}
|
||||
|
||||
backButton: IQuickInputButton;
|
||||
|
||||
createQuickPick<T extends IQuickPickItem>(): IQuickPick<T> {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
createInputBox(): IInputBox {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
toggle(): void {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
accept(): Promise<void> {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
back(): Promise<void> {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
|
||||
cancel(): Promise<void> {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
class MockInputsConfigurationService extends TestConfigurationService {
|
||||
public getValue(arg1?: any, arg2?: any): any {
|
||||
let configuration;
|
||||
if (arg1 === 'tasks') {
|
||||
configuration = {
|
||||
inputs: [
|
||||
{
|
||||
id: 'input1',
|
||||
type: 'promptString',
|
||||
description: 'Enterinput1',
|
||||
default: 'default input1'
|
||||
},
|
||||
{
|
||||
id: 'input2',
|
||||
type: 'pickString',
|
||||
description: 'Enterinput1',
|
||||
default: 'option2',
|
||||
options: ['option1', 'option2', 'option3']
|
||||
},
|
||||
{
|
||||
id: 'input3',
|
||||
type: 'promptString',
|
||||
description: 'Enterinput3',
|
||||
default: 'default input3'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user