Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
@@ -0,0 +1,357 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { SideBySideEditor, EditorResourceAccessor } from 'vs/workbench/common/editor';
|
||||
import { IStringDictionary, forEach, fromMap } from 'vs/base/common/collections';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IConfigurationService, IConfigurationOverrides, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
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/common/variableResolver';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IQuickInputService, IInputOptions, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { ConfiguredInput, IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService {
|
||||
|
||||
static readonly INPUT_OR_COMMAND_VARIABLES_PATTERN = /\${((input|command):(.*?))}/g;
|
||||
|
||||
constructor(
|
||||
context: { getExecPath: () => string | undefined },
|
||||
envVariables: IProcessEnvironment,
|
||||
editorService: IEditorService,
|
||||
private readonly configurationService: IConfigurationService,
|
||||
private readonly commandService: ICommandService,
|
||||
private readonly workspaceContextService: IWorkspaceContextService,
|
||||
private readonly quickInputService: IQuickInputService,
|
||||
private readonly labelService: ILabelService
|
||||
) {
|
||||
super({
|
||||
getFolderUri: (folderName: string): uri | undefined => {
|
||||
const folder = workspaceContextService.getWorkspace().folders.filter(f => f.name === folderName).pop();
|
||||
return folder ? folder.uri : undefined;
|
||||
},
|
||||
getWorkspaceFolderCount: (): number => {
|
||||
return workspaceContextService.getWorkspace().folders.length;
|
||||
},
|
||||
getConfigurationValue: (folderUri: uri, suffix: string): string | undefined => {
|
||||
return configurationService.getValue<string>(suffix, folderUri ? { resource: folderUri } : {});
|
||||
},
|
||||
getExecPath: (): string | undefined => {
|
||||
return context.getExecPath();
|
||||
},
|
||||
getFilePath: (): string | undefined => {
|
||||
const fileResource = EditorResourceAccessor.getOriginalUri(editorService.activeEditor, {
|
||||
supportSideBySide: SideBySideEditor.PRIMARY,
|
||||
filterByScheme: [Schemas.file, Schemas.userData, Schemas.vscodeRemote]
|
||||
});
|
||||
if (!fileResource) {
|
||||
return undefined;
|
||||
}
|
||||
return this.labelService.getUriLabel(fileResource, { noPrefix: true });
|
||||
},
|
||||
getSelectedText: (): string | undefined => {
|
||||
const activeTextEditorControl = editorService.activeTextEditorControl;
|
||||
if (isCodeEditor(activeTextEditorControl)) {
|
||||
const editorModel = activeTextEditorControl.getModel();
|
||||
const editorSelection = activeTextEditorControl.getSelection();
|
||||
if (editorModel && editorSelection) {
|
||||
return editorModel.getValueInRange(editorSelection);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getLineNumber: (): string | undefined => {
|
||||
const activeTextEditorControl = editorService.activeTextEditorControl;
|
||||
if (isCodeEditor(activeTextEditorControl)) {
|
||||
const selection = activeTextEditorControl.getSelection();
|
||||
if (selection) {
|
||||
const lineNumber = selection.positionLineNumber;
|
||||
return String(lineNumber);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}, labelService, envVariables);
|
||||
}
|
||||
|
||||
public async resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<any> {
|
||||
// resolve any non-interactive variables and any contributed variables
|
||||
config = this.resolveAny(folder, config);
|
||||
|
||||
// resolve input variables in the order in which they are encountered
|
||||
return this.resolveWithInteraction(folder, config, section, variables, target).then(mapping => {
|
||||
// finally substitute evaluated command variables (if there are any)
|
||||
if (!mapping) {
|
||||
return null;
|
||||
} else if (mapping.size > 0) {
|
||||
return this.resolveAny(folder, config, fromMap(mapping));
|
||||
} else {
|
||||
return config;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<Map<string, string> | undefined> {
|
||||
// resolve any non-interactive variables and any contributed variables
|
||||
const resolved = await this.resolveAnyMap(folder, config);
|
||||
config = resolved.newConfig;
|
||||
const allVariableMapping: Map<string, string> = resolved.resolvedVariables;
|
||||
|
||||
// resolve input and command variables in the order in which they are encountered
|
||||
return this.resolveWithInputAndCommands(folder, config, variables, section, target).then(inputOrCommandMapping => {
|
||||
if (this.updateMapping(inputOrCommandMapping, allVariableMapping)) {
|
||||
return allVariableMapping;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all items from newMapping to fullMapping. Returns false if newMapping is undefined.
|
||||
*/
|
||||
private updateMapping(newMapping: IStringDictionary<string> | undefined, fullMapping: Map<string, string>): boolean {
|
||||
if (!newMapping) {
|
||||
return false;
|
||||
}
|
||||
forEach(newMapping, (entry) => {
|
||||
fullMapping.set(entry.key, entry.value);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and executes all input and command variables in the given configuration and returns their values as a dictionary.
|
||||
* Please note: this method does not substitute the input or command variables (so the configuration is not modified).
|
||||
* The returned dictionary can be passed to "resolvePlatform" for the actual substitution.
|
||||
* See #6569.
|
||||
*
|
||||
* @param variableToCommandMap Aliases for commands
|
||||
*/
|
||||
private async resolveWithInputAndCommands(folder: IWorkspaceFolder | undefined, configuration: any, variableToCommandMap?: IStringDictionary<string>, section?: string, target?: ConfigurationTarget): Promise<IStringDictionary<string> | undefined> {
|
||||
|
||||
if (!configuration) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// get all "inputs"
|
||||
let inputs: ConfiguredInput[] = [];
|
||||
if (this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && section) {
|
||||
const overrides: IConfigurationOverrides = folder ? { resource: folder.uri } : {};
|
||||
let result = this.configurationService.inspect(section, overrides);
|
||||
if (result && (result.userValue || result.workspaceValue || result.workspaceFolderValue)) {
|
||||
switch (target) {
|
||||
case ConfigurationTarget.USER: inputs = (<any>result.userValue)?.inputs; break;
|
||||
case ConfigurationTarget.WORKSPACE: inputs = (<any>result.workspaceValue)?.inputs; break;
|
||||
default: inputs = (<any>result.workspaceFolderValue)?.inputs;
|
||||
}
|
||||
} else {
|
||||
const valueResult = this.configurationService.getValue<any>(section, overrides);
|
||||
if (valueResult) {
|
||||
inputs = valueResult.inputs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extract and dedupe all "input" and "command" variables and preserve their order in an array
|
||||
const variables: string[] = [];
|
||||
this.findVariables(configuration, variables);
|
||||
|
||||
const variableValues: IStringDictionary<string> = Object.create(null);
|
||||
|
||||
for (const variable of variables) {
|
||||
|
||||
const [type, name] = variable.split(':', 2);
|
||||
|
||||
let result: string | undefined;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case 'input':
|
||||
result = await this.showUserInput(name, inputs);
|
||||
break;
|
||||
|
||||
case 'command':
|
||||
// use the name as a command ID #12735
|
||||
const commandId = (variableToCommandMap ? variableToCommandMap[name] : undefined) || name;
|
||||
result = await this.commandService.executeCommand(commandId, configuration);
|
||||
if (typeof result !== 'string' && !Types.isUndefinedOrNull(result)) {
|
||||
throw new Error(nls.localize('commandVariable.noStringType', "Cannot substitute command variable '{0}' because command did not return a result of type string.", commandId));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Try to resolve it as a contributed variable
|
||||
if (this._contributedVariables.has(variable)) {
|
||||
result = await this._contributedVariables.get(variable)!();
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof result === 'string') {
|
||||
variableValues[variable] = result;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return variableValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively finds all command or input variables in object and pushes them into variables.
|
||||
* @param object object is searched for variables.
|
||||
* @param variables All found variables are returned in variables.
|
||||
*/
|
||||
private findVariables(object: any, variables: string[]) {
|
||||
if (typeof object === 'string') {
|
||||
let matches;
|
||||
while ((matches = BaseConfigurationResolverService.INPUT_OR_COMMAND_VARIABLES_PATTERN.exec(object)) !== null) {
|
||||
if (matches.length === 4) {
|
||||
const command = matches[1];
|
||||
if (variables.indexOf(command) < 0) {
|
||||
variables.push(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._contributedVariables.forEach((value, contributed: string) => {
|
||||
if ((variables.indexOf(contributed) < 0) && (object.indexOf('${' + contributed + '}') >= 0)) {
|
||||
variables.push(contributed);
|
||||
}
|
||||
});
|
||||
} else if (Types.isArray(object)) {
|
||||
object.forEach(value => {
|
||||
this.findVariables(value, variables);
|
||||
});
|
||||
} else if (object) {
|
||||
Object.keys(object).forEach(key => {
|
||||
const value = object[key];
|
||||
this.findVariables(value, variables);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the provided input info and shows the quick pick so the user can provide the value for the input
|
||||
* @param variable Name of the input variable.
|
||||
* @param inputInfos Information about each possible input variable.
|
||||
*/
|
||||
private showUserInput(variable: string, inputInfos: ConfiguredInput[]): Promise<string | undefined> {
|
||||
|
||||
if (!inputInfos) {
|
||||
return Promise.reject(new Error(nls.localize('inputVariable.noInputSection', "Variable '{0}' must be defined in an '{1}' section of the debug or task configuration.", variable, 'input')));
|
||||
}
|
||||
|
||||
// find info for the given input variable
|
||||
const info = inputInfos.filter(item => item.id === variable).pop();
|
||||
if (info) {
|
||||
|
||||
const missingAttribute = (attrName: string) => {
|
||||
throw new Error(nls.localize('inputVariable.missingAttribute', "Input variable '{0}' is of type '{1}' and must include '{2}'.", variable, info.type, attrName));
|
||||
};
|
||||
|
||||
switch (info.type) {
|
||||
|
||||
case 'promptString': {
|
||||
if (!Types.isString(info.description)) {
|
||||
missingAttribute('description');
|
||||
}
|
||||
const inputOptions: IInputOptions = { prompt: info.description, ignoreFocusLost: true };
|
||||
if (info.default) {
|
||||
inputOptions.value = info.default;
|
||||
}
|
||||
if (info.password) {
|
||||
inputOptions.password = info.password;
|
||||
}
|
||||
return this.quickInputService.input(inputOptions).then(resolvedInput => {
|
||||
return resolvedInput;
|
||||
});
|
||||
}
|
||||
|
||||
case 'pickString': {
|
||||
if (!Types.isString(info.description)) {
|
||||
missingAttribute('description');
|
||||
}
|
||||
if (Types.isArray(info.options)) {
|
||||
info.options.forEach(pickOption => {
|
||||
if (!Types.isString(pickOption) && !Types.isString(pickOption.value)) {
|
||||
missingAttribute('value');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
missingAttribute('options');
|
||||
}
|
||||
interface PickStringItem extends IQuickPickItem {
|
||||
value: string;
|
||||
}
|
||||
const picks = new Array<PickStringItem>();
|
||||
info.options.forEach(pickOption => {
|
||||
const value = Types.isString(pickOption) ? pickOption : pickOption.value;
|
||||
const label = Types.isString(pickOption) ? undefined : pickOption.label;
|
||||
|
||||
// If there is no label defined, use value as label
|
||||
const item: PickStringItem = {
|
||||
label: label ? `${label}: ${value}` : value,
|
||||
value: value
|
||||
};
|
||||
|
||||
if (value === info.default) {
|
||||
item.description = nls.localize('inputVariable.defaultInputValue', "(Default)");
|
||||
picks.unshift(item);
|
||||
} else {
|
||||
picks.push(item);
|
||||
}
|
||||
});
|
||||
const pickOptions: IPickOptions<PickStringItem> = { placeHolder: info.description, matchOnDetail: true, ignoreFocusLost: true };
|
||||
return this.quickInputService.pick(picks, pickOptions, undefined).then(resolvedInput => {
|
||||
if (resolvedInput) {
|
||||
return resolvedInput.value;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
case 'command': {
|
||||
if (!Types.isString(info.command)) {
|
||||
missingAttribute('command');
|
||||
}
|
||||
return this.commandService.executeCommand<string>(info.command, info.args).then(result => {
|
||||
if (typeof result === 'string' || Types.isUndefinedOrNull(result)) {
|
||||
return result;
|
||||
}
|
||||
throw new Error(nls.localize('inputVariable.command.noStringType', "Cannot substitute input variable '{0}' because command '{1}' did not return a result of type string.", variable, info.command));
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(nls.localize('inputVariable.unknownType', "Input variable '{0}' can only be of type 'promptString', 'pickString', or 'command'.", variable));
|
||||
}
|
||||
}
|
||||
return Promise.reject(new Error(nls.localize('inputVariable.undefinedVariable', "Undefined input variable '{0}' encountered. Remove or define '{0}' to continue.", variable)));
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigurationResolverService extends BaseConfigurationResolverService {
|
||||
|
||||
constructor(
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@ICommandService commandService: ICommandService,
|
||||
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@ILabelService labelService: ILabelService
|
||||
) {
|
||||
super({ getExecPath: () => undefined }, Object.create(null), editorService, configurationService, commandService, workspaceContextService, quickInputService, labelService);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true);
|
||||
@@ -0,0 +1,71 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export const IConfigurationResolverService = createDecorator<IConfigurationResolverService>('configurationResolverService');
|
||||
|
||||
export interface IConfigurationResolverService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
resolve(folder: IWorkspaceFolder | undefined, value: string): string;
|
||||
resolve(folder: IWorkspaceFolder | undefined, value: string[]): string[];
|
||||
resolve(folder: IWorkspaceFolder | undefined, value: IStringDictionary<string>): IStringDictionary<string>;
|
||||
|
||||
/**
|
||||
* Recursively resolves all variables in the given config and returns a copy of it with substituted values.
|
||||
* Command variables are only substituted if a "commandValueMapping" dictionary is given and if it contains an entry for the command.
|
||||
*/
|
||||
resolveAny(folder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>): any;
|
||||
|
||||
/**
|
||||
* 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 section For example, 'tasks' or 'debug'. Used for resolving inputs.
|
||||
* @param variables Aliases for commands.
|
||||
*/
|
||||
resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<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 | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<Map<string, string> | undefined>;
|
||||
|
||||
/**
|
||||
* Contributes a variable that can be resolved later. Consumers that use resolveAny, resolveWithInteraction,
|
||||
* and resolveWithInteractionReplace will have contributed variables resolved.
|
||||
*/
|
||||
contributeVariable(variable: string, resolution: () => Promise<string | undefined>): void;
|
||||
}
|
||||
|
||||
export interface PromptStringInputInfo {
|
||||
id: string;
|
||||
type: 'promptString';
|
||||
description: string;
|
||||
default?: string;
|
||||
password?: boolean;
|
||||
}
|
||||
|
||||
export interface PickStringInputInfo {
|
||||
id: string;
|
||||
type: 'pickString';
|
||||
description: string;
|
||||
options: (string | { value: string, label?: string })[];
|
||||
default?: string;
|
||||
}
|
||||
|
||||
export interface CommandInputInfo {
|
||||
id: string;
|
||||
type: 'command';
|
||||
command: string;
|
||||
args?: any;
|
||||
}
|
||||
|
||||
export type ConfiguredInput = PromptStringInputInfo | PickStringInputInfo | CommandInputInfo;
|
||||
@@ -0,0 +1,149 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
const idDescription = nls.localize('JsonSchema.input.id', "The input's id is used to associate an input with a variable of the form ${input:id}.");
|
||||
const typeDescription = nls.localize('JsonSchema.input.type', "The type of user input prompt to use.");
|
||||
const descriptionDescription = nls.localize('JsonSchema.input.description', "The description is shown when the user is prompted for input.");
|
||||
const defaultDescription = nls.localize('JsonSchema.input.default', "The default value for the input.");
|
||||
|
||||
|
||||
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: {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'object',
|
||||
required: ['id', 'type', 'description'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: idDescription
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
description: typeDescription,
|
||||
enum: ['promptString'],
|
||||
enumDescriptions: [
|
||||
nls.localize('JsonSchema.input.type.promptString', "The 'promptString' type opens an input box to ask the user for input."),
|
||||
]
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: descriptionDescription
|
||||
},
|
||||
default: {
|
||||
type: 'string',
|
||||
description: defaultDescription
|
||||
},
|
||||
password: {
|
||||
type: 'boolean',
|
||||
description: nls.localize('JsonSchema.input.password', "Controls if a password input is shown. Password input hides the typed text."),
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['id', 'type', 'description', 'options'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: idDescription
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
description: typeDescription,
|
||||
enum: ['pickString'],
|
||||
enumDescriptions: [
|
||||
nls.localize('JsonSchema.input.type.pickString', "The 'pickString' type shows a selection list."),
|
||||
]
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: descriptionDescription
|
||||
},
|
||||
default: {
|
||||
type: 'string',
|
||||
description: defaultDescription
|
||||
},
|
||||
options: {
|
||||
type: 'array',
|
||||
description: nls.localize('JsonSchema.input.options', "An array of strings that defines the options for a quick pick."),
|
||||
items: {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['value'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
description: nls.localize('JsonSchema.input.pickString.optionLabel', "Label for the option.")
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
description: nls.localize('JsonSchema.input.pickString.optionValue', "Value for the option.")
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['id', 'type', 'command'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: idDescription
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
description: typeDescription,
|
||||
enum: ['command'],
|
||||
enumDescriptions: [
|
||||
nls.localize('JsonSchema.input.type.command', "The 'command' type executes a command."),
|
||||
]
|
||||
},
|
||||
command: {
|
||||
type: 'string',
|
||||
description: nls.localize('JsonSchema.input.command.command', "The command to execute for this input variable.")
|
||||
},
|
||||
args: {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'object',
|
||||
description: nls.localize('JsonSchema.input.command.args', "Optional arguments passed to the command.")
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
description: nls.localize('JsonSchema.input.command.args', "Optional arguments passed to the command.")
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
description: nls.localize('JsonSchema.input.command.args', "Optional arguments passed to the command.")
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. 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.");
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as paths from 'vs/base/common/path';
|
||||
import * as process from 'vs/base/common/process';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { IProcessEnvironment, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
import { localize } from 'vs/nls';
|
||||
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 { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
export interface IVariableResolveContext {
|
||||
getFolderUri(folderName: string): uri | undefined;
|
||||
getWorkspaceFolderCount(): number;
|
||||
getConfigurationValue(folderUri: uri, section: string): string | undefined;
|
||||
getExecPath(): string | undefined;
|
||||
getFilePath(): string | undefined;
|
||||
getSelectedText(): string | undefined;
|
||||
getLineNumber(): string | undefined;
|
||||
}
|
||||
|
||||
export class AbstractVariableResolverService implements IConfigurationResolverService {
|
||||
|
||||
static readonly VARIABLE_REGEXP = /\$\{(.*?)\}/g;
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private _context: IVariableResolveContext;
|
||||
private _labelService?: ILabelService;
|
||||
private _envVariables?: IProcessEnvironment;
|
||||
protected _contributedVariables: Map<string, () => Promise<string | undefined>> = new Map();
|
||||
|
||||
|
||||
constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariables?: IProcessEnvironment, private _ignoreEditorVariables = false) {
|
||||
this._context = _context;
|
||||
this._labelService = _labelService;
|
||||
if (_envVariables) {
|
||||
if (isWindows) {
|
||||
// windows env variables are case insensitive
|
||||
const ev: IProcessEnvironment = Object.create(null);
|
||||
this._envVariables = ev;
|
||||
Object.keys(_envVariables).forEach(key => {
|
||||
ev[key.toLowerCase()] = _envVariables[key];
|
||||
});
|
||||
} else {
|
||||
this._envVariables = _envVariables;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public resolve(root: IWorkspaceFolder | undefined, value: string): string;
|
||||
public resolve(root: IWorkspaceFolder | undefined, value: string[]): string[];
|
||||
public resolve(root: IWorkspaceFolder | undefined, value: IStringDictionary<string>): IStringDictionary<string>;
|
||||
public resolve(root: IWorkspaceFolder | undefined, value: any): any {
|
||||
return this.recursiveResolve(root ? root.uri : undefined, value);
|
||||
}
|
||||
|
||||
public resolveAnyBase(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): any {
|
||||
|
||||
const result = objects.deepClone(config) as any;
|
||||
|
||||
// hoist platform specific attributes to top level
|
||||
if (isWindows && result.windows) {
|
||||
Object.keys(result.windows).forEach(key => result[key] = result.windows[key]);
|
||||
} else if (isMacintosh && result.osx) {
|
||||
Object.keys(result.osx).forEach(key => result[key] = result.osx[key]);
|
||||
} else if (isLinux && result.linux) {
|
||||
Object.keys(result.linux).forEach(key => result[key] = result.linux[key]);
|
||||
}
|
||||
|
||||
// delete all platform specific sections
|
||||
delete result.windows;
|
||||
delete result.osx;
|
||||
delete result.linux;
|
||||
|
||||
// substitute all variables recursively in string values
|
||||
return this.recursiveResolve(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables);
|
||||
}
|
||||
|
||||
public resolveAny(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary<string>): any {
|
||||
return this.resolveAnyBase(workspaceFolder, config, commandValueMapping);
|
||||
}
|
||||
|
||||
public resolveAnyMap(workspaceFolder: IWorkspaceFolder | undefined, 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 | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<any> {
|
||||
throw new Error('resolveWithInteractionReplace not implemented.');
|
||||
}
|
||||
|
||||
public resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<Map<string, string> | undefined> {
|
||||
throw new Error('resolveWithInteraction not implemented.');
|
||||
}
|
||||
|
||||
public contributeVariable(variable: string, resolution: () => Promise<string | undefined>): void {
|
||||
if (this._contributedVariables.has(variable)) {
|
||||
throw new Error('Variable ' + variable + ' is contributed twice.');
|
||||
} else {
|
||||
this._contributedVariables.set(variable, resolution);
|
||||
}
|
||||
}
|
||||
|
||||
private recursiveResolve(folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): any {
|
||||
if (types.isString(value)) {
|
||||
return this.resolveString(folderUri, value, commandValueMapping, resolvedVariables);
|
||||
} else if (types.isArray(value)) {
|
||||
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 replaced = this.resolveString(folderUri, key, commandValueMapping, resolvedVariables);
|
||||
result[replaced] = this.recursiveResolve(folderUri, value[key], commandValueMapping, resolvedVariables);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private resolveString(folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary<string> | undefined, resolvedVariables?: Map<string, string>): string {
|
||||
|
||||
// loop through all variables occurrences in 'value'
|
||||
const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => {
|
||||
|
||||
let resolvedValue = this.evaluateSingleVariable(match, variable, folderUri, commandValueMapping);
|
||||
|
||||
if (resolvedVariables) {
|
||||
resolvedVariables.set(variable, resolvedValue);
|
||||
}
|
||||
|
||||
return resolvedValue;
|
||||
});
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
private fsPath(displayUri: uri): string {
|
||||
return this._labelService ? this._labelService.getUriLabel(displayUri, { noPrefix: true }) : displayUri.fsPath;
|
||||
}
|
||||
|
||||
private evaluateSingleVariable(match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary<string> | undefined): string {
|
||||
|
||||
// try to separate variable arguments from variable name
|
||||
let argument: string | undefined;
|
||||
const parts = variable.split(':');
|
||||
if (parts.length > 1) {
|
||||
variable = parts[0];
|
||||
argument = parts[1];
|
||||
}
|
||||
|
||||
// common error handling for all variables that require an open editor
|
||||
const getFilePath = (): string => {
|
||||
|
||||
const filePath = this._context.getFilePath();
|
||||
if (filePath) {
|
||||
return filePath;
|
||||
}
|
||||
throw new Error(localize('canNotResolveFile', "'{0}' can not be resolved. Please open an editor.", match));
|
||||
};
|
||||
|
||||
// common error handling for all variables that require an open folder and accept a folder name argument
|
||||
const getFolderUri = (withArg = true): uri => {
|
||||
|
||||
if (withArg && argument) {
|
||||
const folder = this._context.getFolderUri(argument);
|
||||
if (folder) {
|
||||
return folder;
|
||||
}
|
||||
throw new Error(localize('canNotFindFolder', "'{0}' can not be resolved. No such folder '{1}'.", match, argument));
|
||||
}
|
||||
|
||||
if (folderUri) {
|
||||
return folderUri;
|
||||
}
|
||||
|
||||
if (this._context.getWorkspaceFolderCount() > 1) {
|
||||
throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "'{0}' can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match));
|
||||
}
|
||||
throw new Error(localize('canNotResolveWorkspaceFolder', "'{0}' can not be resolved. Please open a folder.", match));
|
||||
};
|
||||
|
||||
|
||||
switch (variable) {
|
||||
|
||||
case 'env':
|
||||
if (argument) {
|
||||
if (this._envVariables) {
|
||||
const env = this._envVariables[isWindows ? argument.toLowerCase() : argument];
|
||||
if (types.isString(env)) {
|
||||
return env;
|
||||
}
|
||||
}
|
||||
// For `env` we should do the same as a normal shell does - evaluates undefined envs to an empty string #46436
|
||||
return '';
|
||||
}
|
||||
throw new Error(localize('missingEnvVarName', "'{0}' can not be resolved because no environment variable name is given.", match));
|
||||
|
||||
case 'config':
|
||||
if (argument) {
|
||||
const config = this._context.getConfigurationValue(getFolderUri(false), argument);
|
||||
if (types.isUndefinedOrNull(config)) {
|
||||
throw new Error(localize('configNotFound', "'{0}' can not be resolved because setting '{1}' not found.", match, argument));
|
||||
}
|
||||
if (types.isObject(config)) {
|
||||
throw new Error(localize('configNoString', "'{0}' can not be resolved because '{1}' is a structured value.", match, argument));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
throw new Error(localize('missingConfigName', "'{0}' can not be resolved because no settings name is given.", match));
|
||||
|
||||
case 'command':
|
||||
return this.resolveFromMap(match, argument, commandValueMapping, 'command');
|
||||
|
||||
case 'input':
|
||||
return this.resolveFromMap(match, argument, commandValueMapping, 'input');
|
||||
|
||||
default: {
|
||||
|
||||
switch (variable) {
|
||||
case 'workspaceRoot':
|
||||
case 'workspaceFolder':
|
||||
return normalizeDriveLetter(this.fsPath(getFolderUri()));
|
||||
|
||||
case 'cwd':
|
||||
return ((folderUri || argument) ? normalizeDriveLetter(this.fsPath(getFolderUri())) : process.cwd());
|
||||
|
||||
case 'workspaceRootFolderName':
|
||||
case 'workspaceFolderBasename':
|
||||
return paths.basename(this.fsPath(getFolderUri()));
|
||||
|
||||
case 'lineNumber':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
const lineNumber = this._context.getLineNumber();
|
||||
if (lineNumber) {
|
||||
return 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':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
const selectedText = this._context.getSelectedText();
|
||||
if (selectedText) {
|
||||
return 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':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
return getFilePath();
|
||||
|
||||
case 'relativeFile':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
if (folderUri || argument) {
|
||||
return paths.relative(this.fsPath(getFolderUri()), getFilePath());
|
||||
}
|
||||
return getFilePath();
|
||||
|
||||
case 'relativeFileDirname':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
const dirname = paths.dirname(getFilePath());
|
||||
if (folderUri || argument) {
|
||||
const relative = paths.relative(this.fsPath(getFolderUri()), dirname);
|
||||
return relative.length === 0 ? '.' : relative;
|
||||
}
|
||||
return dirname;
|
||||
|
||||
case 'fileDirname':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
return paths.dirname(getFilePath());
|
||||
|
||||
case 'fileExtname':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
return paths.extname(getFilePath());
|
||||
|
||||
case 'fileBasename':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
return paths.basename(getFilePath());
|
||||
|
||||
case 'fileBasenameNoExtension':
|
||||
if (this._ignoreEditorVariables) {
|
||||
return match;
|
||||
}
|
||||
const basename = paths.basename(getFilePath());
|
||||
return (basename.slice(0, basename.length - paths.extname(basename).length));
|
||||
|
||||
case 'execPath':
|
||||
const ep = this._context.getExecPath();
|
||||
if (ep) {
|
||||
return ep;
|
||||
}
|
||||
return match;
|
||||
|
||||
default:
|
||||
try {
|
||||
return this.resolveFromMap(match, variable, commandValueMapping, undefined);
|
||||
} catch (error) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private resolveFromMap(match: string, argument: string | undefined, commandValueMapping: IStringDictionary<string> | undefined, prefix: string | undefined): string {
|
||||
if (argument && commandValueMapping) {
|
||||
const v = (prefix === undefined) ? commandValueMapping[argument] : 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
|
||||
import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
export class ConfigurationResolverService extends BaseConfigurationResolverService {
|
||||
|
||||
constructor(
|
||||
@IEditorService editorService: IEditorService,
|
||||
@INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@ICommandService commandService: ICommandService,
|
||||
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@ILabelService labelService: ILabelService
|
||||
) {
|
||||
super({
|
||||
getExecPath: (): string | undefined => {
|
||||
return environmentService.execPath;
|
||||
}
|
||||
}, process.env as IProcessEnvironment, editorService, configurationService, commandService, workspaceContextService, quickInputService, labelService);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true);
|
||||
@@ -0,0 +1,757 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { normalize } from 'vs/base/common/path';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
|
||||
import { Workspace, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestEditorService, TestProductService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { TestWorkbenchConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { Disposable, IDisposable } 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 'vs/base/common/cancellation';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
import { EditorType } from 'vs/editor/common/editorCommon';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService';
|
||||
import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
import { IFormatterChangeEvent, ILabelService, ResourceLabelFormatter } from 'vs/platform/label/common/label';
|
||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
||||
const mockLineNumber = 10;
|
||||
class TestEditorServiceWithActiveEditor extends TestEditorService {
|
||||
get activeTextEditorControl(): any {
|
||||
return {
|
||||
getEditorType() {
|
||||
return EditorType.ICodeEditor;
|
||||
},
|
||||
getSelection() {
|
||||
return new Selection(mockLineNumber, 1, mockLineNumber, 10);
|
||||
}
|
||||
};
|
||||
}
|
||||
get activeEditor(): any {
|
||||
return {
|
||||
get resource(): any {
|
||||
return uri.parse('file:///VSCode/workspaceLocation/file');
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class TestConfigurationResolverService extends BaseConfigurationResolverService {
|
||||
|
||||
}
|
||||
|
||||
suite('Configuration Resolver Service', () => {
|
||||
let configurationResolverService: IConfigurationResolverService | null;
|
||||
let envVariables: { [key: string]: string } = { key1: 'Value for key1', key2: 'Value for key2' };
|
||||
let environmentService: MockWorkbenchEnvironmentService;
|
||||
let mockCommandService: MockCommandService;
|
||||
let editorService: TestEditorServiceWithActiveEditor;
|
||||
let containingWorkspace: Workspace;
|
||||
let workspace: IWorkspaceFolder;
|
||||
let quickInputService: MockQuickInputService;
|
||||
let labelService: MockLabelService;
|
||||
|
||||
setup(() => {
|
||||
mockCommandService = new MockCommandService();
|
||||
editorService = new TestEditorServiceWithActiveEditor();
|
||||
quickInputService = new MockQuickInputService();
|
||||
environmentService = new MockWorkbenchEnvironmentService(envVariables);
|
||||
labelService = new MockLabelService();
|
||||
containingWorkspace = testWorkspace(uri.parse('file:///VSCode/workspaceLocation'));
|
||||
workspace = containingWorkspace.folders[0];
|
||||
configurationResolverService = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, editorService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(containingWorkspace), quickInputService, labelService);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
configurationResolverService = null;
|
||||
});
|
||||
|
||||
test('substitute one', () => {
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${workspaceFolder} xyz'), 'abc \\VSCode\\workspaceLocation xyz');
|
||||
} else {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${workspaceFolder} xyz'), 'abc /VSCode/workspaceLocation xyz');
|
||||
}
|
||||
});
|
||||
|
||||
test('workspace folder with argument', () => {
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${workspaceFolder:workspaceLocation} xyz'), 'abc \\VSCode\\workspaceLocation xyz');
|
||||
} else {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${workspaceFolder:workspaceLocation} xyz'), 'abc /VSCode/workspaceLocation xyz');
|
||||
}
|
||||
});
|
||||
|
||||
test('workspace folder with invalid argument', () => {
|
||||
assert.throws(() => configurationResolverService!.resolve(workspace, 'abc ${workspaceFolder:invalidLocation} xyz'));
|
||||
});
|
||||
|
||||
test('workspace folder with undefined workspace folder', () => {
|
||||
assert.throws(() => configurationResolverService!.resolve(undefined, 'abc ${workspaceFolder} xyz'));
|
||||
});
|
||||
|
||||
test('workspace folder with argument and undefined workspace folder', () => {
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(configurationResolverService!.resolve(undefined, 'abc ${workspaceFolder:workspaceLocation} xyz'), 'abc \\VSCode\\workspaceLocation xyz');
|
||||
} else {
|
||||
assert.strictEqual(configurationResolverService!.resolve(undefined, 'abc ${workspaceFolder:workspaceLocation} xyz'), 'abc /VSCode/workspaceLocation xyz');
|
||||
}
|
||||
});
|
||||
|
||||
test('workspace folder with invalid argument and undefined workspace folder', () => {
|
||||
assert.throws(() => configurationResolverService!.resolve(undefined, 'abc ${workspaceFolder:invalidLocation} xyz'));
|
||||
});
|
||||
|
||||
test('workspace root folder name', () => {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${workspaceRootFolderName} xyz'), 'abc workspaceLocation xyz');
|
||||
});
|
||||
|
||||
test('current selected line number', () => {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${lineNumber} xyz'), `abc ${mockLineNumber} xyz`);
|
||||
});
|
||||
|
||||
test('relative file', () => {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${relativeFile} xyz'), 'abc file xyz');
|
||||
});
|
||||
|
||||
test('relative file with argument', () => {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${relativeFile:workspaceLocation} xyz'), 'abc file xyz');
|
||||
});
|
||||
|
||||
test('relative file with invalid argument', () => {
|
||||
assert.throws(() => configurationResolverService!.resolve(workspace, 'abc ${relativeFile:invalidLocation} xyz'));
|
||||
});
|
||||
|
||||
test('relative file with undefined workspace folder', () => {
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(configurationResolverService!.resolve(undefined, 'abc ${relativeFile} xyz'), 'abc \\VSCode\\workspaceLocation\\file xyz');
|
||||
} else {
|
||||
assert.strictEqual(configurationResolverService!.resolve(undefined, 'abc ${relativeFile} xyz'), 'abc /VSCode/workspaceLocation/file xyz');
|
||||
}
|
||||
});
|
||||
|
||||
test('relative file with argument and undefined workspace folder', () => {
|
||||
assert.strictEqual(configurationResolverService!.resolve(undefined, 'abc ${relativeFile:workspaceLocation} xyz'), 'abc file xyz');
|
||||
});
|
||||
|
||||
test('relative file with invalid argument and undefined workspace folder', () => {
|
||||
assert.throws(() => configurationResolverService!.resolve(undefined, 'abc ${relativeFile:invalidLocation} xyz'));
|
||||
});
|
||||
|
||||
test('substitute many', () => {
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, '${workspaceFolder} - ${workspaceFolder}'), '\\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation');
|
||||
} else {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, '${workspaceFolder} - ${workspaceFolder}'), '/VSCode/workspaceLocation - /VSCode/workspaceLocation');
|
||||
}
|
||||
});
|
||||
|
||||
test('substitute one env variable', () => {
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${workspaceFolder} ${env:key1} xyz'), 'abc \\VSCode\\workspaceLocation Value for key1 xyz');
|
||||
} else {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, 'abc ${workspaceFolder} ${env:key1} xyz'), 'abc /VSCode/workspaceLocation Value for key1 xyz');
|
||||
}
|
||||
});
|
||||
|
||||
test('substitute many env variable', () => {
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, '${workspaceFolder} - ${workspaceFolder} ${env:key1} - ${env:key2}'), '\\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for key1 - Value for key2');
|
||||
} else {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, '${workspaceFolder} - ${workspaceFolder} ${env:key1} - ${env:key2}'), '/VSCode/workspaceLocation - /VSCode/workspaceLocation Value for key1 - Value for key2');
|
||||
}
|
||||
});
|
||||
|
||||
// test('substitute keys and values in object', () => {
|
||||
// const myObject = {
|
||||
// '${workspaceRootFolderName}': '${lineNumber}',
|
||||
// 'hey ${env:key1} ': '${workspaceRootFolderName}'
|
||||
// };
|
||||
// assert.deepEqual(configurationResolverService!.resolve(workspace, myObject), {
|
||||
// 'workspaceLocation': `${editorService.mockLineNumber}`,
|
||||
// 'hey Value for key1 ': 'workspaceLocation'
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
test('substitute one env variable using platform case sensitivity', () => {
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, '${env:key1} - ${env:Key1}'), 'Value for key1 - Value for key1');
|
||||
} else {
|
||||
assert.strictEqual(configurationResolverService!.resolve(workspace, '${env:key1} - ${env:Key1}'), 'Value for key1 - ');
|
||||
}
|
||||
});
|
||||
|
||||
test('substitute one configuration variable', () => {
|
||||
let configurationService: IConfigurationService = new TestConfigurationService({
|
||||
editor: {
|
||||
fontFamily: 'foo'
|
||||
},
|
||||
terminal: {
|
||||
integrated: {
|
||||
fontFamily: 'bar'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz');
|
||||
});
|
||||
|
||||
test('substitute many configuration variables', () => {
|
||||
let configurationService: IConfigurationService;
|
||||
configurationService = new TestConfigurationService({
|
||||
editor: {
|
||||
fontFamily: 'foo'
|
||||
},
|
||||
terminal: {
|
||||
integrated: {
|
||||
fontFamily: 'bar'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz');
|
||||
});
|
||||
|
||||
test('substitute one env variable and a configuration variable', () => {
|
||||
let configurationService: IConfigurationService;
|
||||
configurationService = new TestConfigurationService({
|
||||
editor: {
|
||||
fontFamily: 'foo'
|
||||
},
|
||||
terminal: {
|
||||
integrated: {
|
||||
fontFamily: 'bar'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
|
||||
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 {
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${workspaceFolder} ${env:key1} xyz'), 'abc foo /VSCode/workspaceLocation Value for key1 xyz');
|
||||
}
|
||||
});
|
||||
|
||||
test('substitute many env variable and a configuration variable', () => {
|
||||
let configurationService: IConfigurationService;
|
||||
configurationService = new TestConfigurationService({
|
||||
editor: {
|
||||
fontFamily: 'foo'
|
||||
},
|
||||
terminal: {
|
||||
integrated: {
|
||||
fontFamily: 'bar'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
|
||||
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 {
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
test('mixed types of configuration variables', () => {
|
||||
let configurationService: IConfigurationService;
|
||||
configurationService = new TestConfigurationService({
|
||||
editor: {
|
||||
fontFamily: 'foo',
|
||||
lineNumbers: 123,
|
||||
insertSpaces: false
|
||||
},
|
||||
terminal: {
|
||||
integrated: {
|
||||
fontFamily: 'bar'
|
||||
}
|
||||
},
|
||||
json: {
|
||||
schemas: [
|
||||
{
|
||||
fileMatch: [
|
||||
'/myfile',
|
||||
'/myOtherfile'
|
||||
],
|
||||
url: 'schemaURL'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz');
|
||||
});
|
||||
|
||||
test('uses original variable as fallback', () => {
|
||||
let configurationService: IConfigurationService;
|
||||
configurationService = new TestConfigurationService({
|
||||
editor: {}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${unknownVariable} xyz'), 'abc ${unknownVariable} xyz');
|
||||
assert.strictEqual(service.resolve(workspace, 'abc ${env:unknownVariable} xyz'), 'abc xyz');
|
||||
});
|
||||
|
||||
test('configuration variables with invalid accessor', () => {
|
||||
let configurationService: IConfigurationService;
|
||||
configurationService = new TestConfigurationService({
|
||||
editor: {
|
||||
fontFamily: 'foo'
|
||||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
|
||||
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${env} xyz'));
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${env:} xyz'));
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${config} xyz'));
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${config:} xyz'));
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${config:editor} xyz'));
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${config:editor..fontFamily} xyz'));
|
||||
assert.throws(() => service.resolve(workspace, 'abc ${config:editor.none.none2} xyz'));
|
||||
});
|
||||
|
||||
test('a single command variable', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': '${command:command1}',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
};
|
||||
|
||||
return configurationResolverService!.resolveWithInteractionReplace(undefined, configuration).then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': 'command1-result',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
});
|
||||
|
||||
assert.equal(1, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
|
||||
test('an old style command variable', () => {
|
||||
const configuration = {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': '${command:commandVariable1}',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
};
|
||||
const commandVariables = Object.create(null);
|
||||
commandVariables['commandVariable1'] = 'command1';
|
||||
|
||||
return configurationResolverService!.resolveWithInteractionReplace(undefined, configuration, undefined, commandVariables).then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': 'command1-result',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
});
|
||||
|
||||
assert.equal(1, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
|
||||
test('multiple new and old-style command variables', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': '${command:commandVariable1}',
|
||||
'pid': '${command:command2}',
|
||||
'sourceMaps': false,
|
||||
'outDir': 'src/${command:command2}',
|
||||
'env': {
|
||||
'processId': '__${command:command2}__',
|
||||
}
|
||||
};
|
||||
const commandVariables = Object.create(null);
|
||||
commandVariables['commandVariable1'] = 'command1';
|
||||
|
||||
return configurationResolverService!.resolveWithInteractionReplace(undefined, configuration, undefined, commandVariables).then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': 'command1-result',
|
||||
'pid': 'command2-result',
|
||||
'sourceMaps': false,
|
||||
'outDir': 'src/command2-result',
|
||||
'env': {
|
||||
'processId': '__command2-result__',
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(2, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
|
||||
test('a command variable that relies on resolved env vars', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': '${command:commandVariable1}',
|
||||
'value': '${env:key1}'
|
||||
};
|
||||
const commandVariables = Object.create(null);
|
||||
commandVariables['commandVariable1'] = 'command1';
|
||||
|
||||
return configurationResolverService!.resolveWithInteractionReplace(undefined, configuration, undefined, commandVariables).then(result => {
|
||||
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': 'Value for key1',
|
||||
'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('a single command input variable', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': '${input:input4}',
|
||||
'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': 'arg for command',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
});
|
||||
|
||||
assert.equal(1, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
|
||||
test('several input variables and command', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': '${input:input3}',
|
||||
'type': '${command:command1}',
|
||||
'request': '${input:input1}',
|
||||
'processId': '${input:input2}',
|
||||
'command': '${input:input4}',
|
||||
'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',
|
||||
'command': 'arg for command',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
});
|
||||
|
||||
assert.equal(2, mockCommandService.callCount);
|
||||
});
|
||||
});
|
||||
|
||||
test('input variable with undefined workspace folder', () => {
|
||||
|
||||
const configuration = {
|
||||
'name': 'Attach to Process',
|
||||
'type': 'node',
|
||||
'request': 'attach',
|
||||
'processId': '${input:input1}',
|
||||
'port': 5858,
|
||||
'sourceMaps': false,
|
||||
'outDir': null
|
||||
};
|
||||
|
||||
return configurationResolverService!.resolveWithInteractionReplace(undefined, 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('contributed variable', () => {
|
||||
const buildTask = 'npm: compile';
|
||||
const variable = 'defaultBuildTask';
|
||||
const configuration = {
|
||||
'name': '${' + variable + '}',
|
||||
};
|
||||
configurationResolverService!.contributeVariable(variable, async () => { return buildTask; });
|
||||
return configurationResolverService!.resolveWithInteractionReplace(workspace, configuration).then(result => {
|
||||
assert.deepEqual(result, {
|
||||
'name': `${buildTask}`
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
class MockCommandService implements ICommandService {
|
||||
|
||||
public _serviceBrand: undefined;
|
||||
public callCount = 0;
|
||||
|
||||
onWillExecuteCommand = () => Disposable.None;
|
||||
onDidExecuteCommand = () => Disposable.None;
|
||||
public executeCommand(commandId: string, ...args: any[]): Promise<any> {
|
||||
this.callCount++;
|
||||
|
||||
let result = `${commandId}-result`;
|
||||
if (args.length >= 1) {
|
||||
if (args[0] && args[0].value) {
|
||||
result = args[0].value;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
|
||||
class MockQuickInputService implements IQuickInputService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
readonly onShow = Event.None;
|
||||
readonly onHide = Event.None;
|
||||
|
||||
readonly quickAccess = undefined!;
|
||||
|
||||
public pick<T extends IQuickPickItem>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: IPickOptions<T> & { canPickMany: true }, token?: CancellationToken): Promise<T[]>;
|
||||
public pick<T extends IQuickPickItem>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: IPickOptions<T> & { canPickMany: false }, token?: CancellationToken): Promise<T>;
|
||||
public pick<T extends IQuickPickItem>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: Omit<IPickOptions<T>, 'canPickMany'>, token?: CancellationToken): Promise<T | undefined> {
|
||||
if (Types.isArray(picks)) {
|
||||
return Promise.resolve(<any>{ label: 'selectedPick', description: 'pick description', value: 'selectedPick' });
|
||||
} else {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public input(options?: IInputOptions, token?: CancellationToken): Promise<string> {
|
||||
return Promise.resolve(options ? 'resolved' + options.prompt : 'resolved');
|
||||
}
|
||||
|
||||
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 MockLabelService implements ILabelService {
|
||||
_serviceBrand: undefined;
|
||||
getUriLabel(resource: uri, options?: { relative?: boolean | undefined; noPrefix?: boolean | undefined; endWithSeparator?: boolean | undefined; }): string {
|
||||
return normalize(resource.fsPath);
|
||||
}
|
||||
getUriBasenameLabel(resource: uri): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getWorkspaceLabel(workspace: uri | IWorkspaceIdentifier | IWorkspace, options?: { verbose: boolean; }): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getHostLabel(scheme: string, authority?: string): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getSeparator(scheme: string, authority?: string): '/' | '\\' {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
registerFormatter(formatter: ResourceLabelFormatter): IDisposable {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
onDidChangeFormatters: Event<IFormatterChangeEvent> = new Emitter<IFormatterChangeEvent>().event;
|
||||
}
|
||||
|
||||
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',
|
||||
password: true
|
||||
},
|
||||
{
|
||||
id: 'input4',
|
||||
type: 'command',
|
||||
command: 'command1',
|
||||
args: {
|
||||
value: 'arg for command'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
class MockWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService {
|
||||
|
||||
constructor(public userEnv: platform.IProcessEnvironment) {
|
||||
super({ ...TestWorkbenchConfiguration, userEnv }, TestProductService);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user