提交 8b5b1eb4 编写于 作者: D Dirk Baeumer

Add support to specify a shell when run in terminal.

上级 b0fa6425
......@@ -8,6 +8,7 @@ import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { TerminateResponse } from 'vs/base/common/processes';
import { IEventEmitter } from 'vs/base/common/eventEmitter';
import * as Types from 'vs/base/common/types';
import { ProblemMatcher } from 'vs/platform/markers/common/problemMatcher';
......@@ -85,6 +86,24 @@ export interface CommandOptions {
env?: { [key: string]: string; };
}
export interface ShellConfiguration {
/**
* The shell executable.
*/
executable: string;
/**
* The arguments to be passed to the shell executable.
*/
args?: string[];
}
export namespace ShellConfiguration {
export function is(value: any): value is ShellConfiguration {
let candidate: ShellConfiguration = value;
return candidate && Types.isString(candidate.executable) && (candidate.args === void 0 || Types.isStringArray(candidate.args));
}
}
export interface CommandConfiguration {
/**
* The command to execute
......@@ -94,7 +113,7 @@ export interface CommandConfiguration {
/**
* Whether the command is a shell command or not
*/
isShellCommand?: boolean;
isShellCommand?: boolean | ShellConfiguration;
/**
* Additional command options.
......
......@@ -1307,9 +1307,16 @@ let schema: IJSONSchema =
'description': nls.localize('JsonSchema.command', 'The command to be executed. Can be an external program or a shell command.')
},
'isShellCommand': {
'type': 'boolean',
'default': true,
'description': nls.localize('JsonSchema.shell', 'Specifies whether the command is a shell command or an external program. Defaults to false if omitted.')
'anyOf': [
{
'type': 'boolean',
'default': true,
'description': nls.localize('JsonSchema.shell', 'Specifies whether the command is a shell command or an external program. Defaults to false if omitted.')
},
{
'$ref': '#definitions/shellConfiguration'
}
]
},
'args': {
'type': 'array',
......@@ -1363,6 +1370,23 @@ let schema: IJSONSchema =
}
}
},
'shellConfiguration': {
'type': 'object',
'additionalProperties': false,
'properties': {
'executable': {
'type': 'string',
'description': nls.localize('JsonSchema.shell.executable', 'The shell to be used.')
},
'args': {
'type': 'array',
'description': nls.localize('JsonSchema.shell.args', 'The shell arguments.'),
'items': {
'type': 'string'
}
}
}
},
'commandConfiguration': {
'type': 'object',
'additionalProperties': false,
......@@ -1372,9 +1396,16 @@ let schema: IJSONSchema =
'description': nls.localize('JsonSchema.command', 'The command to be executed. Can be an external program or a shell command.')
},
'isShellCommand': {
'type': 'boolean',
'default': true,
'description': nls.localize('JsonSchema.shell', 'Specifies whether the command is a shell command or an external program. Defaults to false if omitted.')
'anyOf': [
{
'type': 'boolean',
'default': true,
'description': nls.localize('JsonSchema.shell', 'Specifies whether the command is a shell command or an external program. Defaults to false if omitted.')
},
{
'$ref': '#definitions/shellConfiguration'
}
]
},
'args': {
'type': 'array',
......@@ -1402,9 +1433,16 @@ let schema: IJSONSchema =
'description': nls.localize('JsonSchema.command', 'The command to be executed. Can be an external program or a shell command.')
},
'isShellCommand': {
'type': 'boolean',
'default': true,
'description': nls.localize('JsonSchema.shell', 'Specifies whether the command is a shell command or an external program. Defaults to false if omitted.')
'anyOf': [
{
'type': 'boolean',
'default': true,
'description': nls.localize('JsonSchema.shell', 'Specifies whether the command is a shell command or an external program. Defaults to false if omitted.')
},
{
'$ref': '#definitions/shellConfiguration'
}
]
},
'args': {
'type': 'array',
......
......@@ -31,7 +31,10 @@ import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/work
import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors';
import { ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskRunnerConfiguration, TaskDescription, ShowOutput, TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType, CommandOptions } from 'vs/workbench/parts/tasks/common/taskSystem';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskRunnerConfiguration, TaskDescription, ShowOutput,
TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType, CommandOptions, ShellConfiguration
} from 'vs/workbench/parts/tasks/common/taskSystem';
class TerminalDecoder {
// See https://en.wikipedia.org/wiki/ANSI_escape_code & http://stackoverflow.com/questions/25189651/how-to-remove-ansi-control-chars-vt100-from-a-java-string &
......@@ -332,17 +335,32 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
});
shellConfig.env = env;
}
(this.terminalService.configHelper as TerminalConfigHelper).mergeDefaultShellPathAndArgs(shellConfig);
let shellSpecified: boolean = false;
if (ShellConfiguration.is(task.command.isShellCommand)) {
shellConfig.executable = task.command.isShellCommand.executable;
shellSpecified = true;
if (task.command.isShellCommand.args) {
shellConfig.args = task.command.isShellCommand.args.slice();
} else {
shellConfig.args = [];
}
} else {
(this.terminalService.configHelper as TerminalConfigHelper).mergeDefaultShellPathAndArgs(shellConfig);
}
let shellArgs = shellConfig.args.slice(0);
let toAdd: string[] = [];
let commandLine: string;
if (Platform.isWindows) {
let basename = path.basename(shellConfig.executable).toLowerCase();
if (basename === 'powershell.exe') {
toAdd.push('-Command');
if (!shellSpecified) {
toAdd.push('-Command');
}
commandLine = args && args.length > 0 ? `${command} ${args.join(' ')}` : `${command}`;
} else {
toAdd.push('/d', '/c');
if (!shellSpecified) {
toAdd.push('/d', '/c');
}
let quotedCommand: boolean = false;
let quotedArg: boolean = false;
let quoted = this.ensureDoubleQuotes(command);
......@@ -371,7 +389,9 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
}
}
} else {
toAdd.push('-c');
if (!shellSpecified) {
toAdd.push('-c');
}
commandLine = args && args.length > 0 ? `${command} ${args.join(' ')}` : `${command}`;
}
toAdd.forEach(element => {
......
......@@ -343,10 +343,59 @@ namespace CommandOptions {
}
}
interface ShellConfiguration {
executable: string;
args?: string[];
}
namespace ShellConfiguration {
export function is(value: any): value is ShellConfiguration {
let candidate: ShellConfiguration = value;
return candidate && Types.isString(candidate.executable) && (candidate.args === void 0 || Types.isStringArray(candidate.args));
}
export function from(this: void, config: ShellConfiguration, context: ParseContext): TaskSystem.ShellConfiguration {
if (!is(config)) {
return undefined;
}
let result: ShellConfiguration = { executable: config.executable };
if (config.args !== void 0) {
result.args = config.args.slice();
}
return result;
}
export function isEmpty(value: TaskSystem.ShellConfiguration): boolean {
return !value || value.executable === void 0 && (value.args === void 0 || value.args.length === 0);
}
export function merge(target: TaskSystem.ShellConfiguration, source: TaskSystem.ShellConfiguration): TaskSystem.ShellConfiguration {
if (isEmpty(source)) {
return target;
}
if (isEmpty(target)) {
return source;
}
mergeProperty(target, source, 'executable');
mergeProperty(target, source, 'args');
return target;
}
export function fillDefaults(value: TaskSystem.ShellConfiguration): void {
}
export function freeze(value: TaskSystem.ShellConfiguration): void {
if (!value) {
return;
}
Object.freeze(value);
}
}
namespace CommandConfiguration {
interface BaseCommandConfiguationShape {
command?: string;
isShellCommand?: boolean;
isShellCommand?: boolean | ShellConfiguration;
args?: string[];
options?: ProcessConfig.CommandOptions;
echoCommand?: boolean;
......@@ -384,6 +433,14 @@ namespace CommandConfiguration {
}
if (Types.isBoolean(config.isShellCommand)) {
result.isShellCommand = config.isShellCommand;
} else if (ShellConfiguration.is(config.isShellCommand)) {
result.isShellCommand = ShellConfiguration.from(config.isShellCommand, context);
if (!context.isTermnial) {
context.validationStatus.state = ValidationState.Warning;
context.logger.log(nls.localize('ConfigurationParser.noShell', 'Warning: shell configuration is only supported when executing tasks in the terminal.'));
}
} else if (config.isShellCommand !== void 0) {
result.isShellCommand = !!config.isShellCommand;
}
if (config.args !== void 0) {
if (Types.isStringArray(config.args)) {
......@@ -421,7 +478,17 @@ namespace CommandConfiguration {
return source;
}
mergeProperty(target, source, 'name');
mergeProperty(target, source, 'isShellCommand');
// Merge isShellCommand
if (target.isShellCommand === void 0) {
target.isShellCommand = source.isShellCommand;
} if (Types.isBoolean(target.isShellCommand) && Types.isBoolean(source.isShellCommand)) {
mergeProperty(target, source, 'isShellCommand');
} else if (ShellConfiguration.is(target.isShellCommand) && ShellConfiguration.is(source.isShellCommand)) {
ShellConfiguration.merge(target.isShellCommand, source.isShellCommand);
} else if (Types.isBoolean(target.isShellCommand) && ShellConfiguration.is(source.isShellCommand)) {
target.isShellCommand = source.isShellCommand;
}
mergeProperty(target, source, 'echo');
mergeProperty(target, source, 'taskSelector');
if (source.args !== void 0) {
......@@ -461,6 +528,9 @@ namespace CommandConfiguration {
if (value.options) {
CommandOptions.freeze(value.options);
}
if (ShellConfiguration.is(value.isShellCommand)) {
ShellConfiguration.freeze(value.isShellCommand);
}
}
}
......
......@@ -214,7 +214,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
}
args = this.resolveVariables(args);
let command: string = this.resolveVariable(commandConfig.name);
this.childProcess = new LineProcess(command, args, commandConfig.isShellCommand, this.resolveOptions(commandConfig.options));
this.childProcess = new LineProcess(command, args, !!commandConfig.isShellCommand, this.resolveOptions(commandConfig.options));
telemetryEvent.command = this.childProcess.getSanitizedCommand();
// we have no problem matchers defined. So show the output log
if (task.showOutput === ShowOutput.Always || (task.showOutput === ShowOutput.Silent && task.problemMatchers.length === 0)) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册