consolidate externalTerminalService: dead wood removal
This commit is contained in:
parent
c0a9bc3988
commit
9f20ed4d6e
|
@ -21,7 +21,7 @@ import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorksp
|
|||
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { getTerminalLauncher, hasChildProcesses, prepareCommand } from 'vs/workbench/contrib/debug/node/terminals';
|
||||
import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
|
||||
import { ExtHostConfiguration, ExtHostConfigProvider } from '../common/extHostConfiguration';
|
||||
|
@ -357,10 +357,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
|
|||
|
||||
} else if (args.kind === 'external') {
|
||||
|
||||
const terminalLauncher = getTerminalLauncher();
|
||||
if (terminalLauncher) {
|
||||
return terminalLauncher.runInTerminal(args, config);
|
||||
}
|
||||
runInExternalTerminal(args, config);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import { IFileService } from 'vs/platform/files/common/files';
|
|||
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, ITerminalSettings, ITerminalLauncher, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IDebugService, IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, ITerminalSettings, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Debugger } from 'vs/workbench/contrib/debug/common/debugger';
|
||||
import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
@ -52,7 +52,6 @@ export class ConfigurationManager implements IConfigurationManager {
|
|||
private configProviders: IDebugConfigurationProvider[];
|
||||
private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[];
|
||||
private debugAdapterFactories = new Map<string, IDebugAdapterFactory>();
|
||||
private terminalLauncher: ITerminalLauncher;
|
||||
private debugConfigurationTypeContext: IContextKey<string>;
|
||||
|
||||
constructor(
|
||||
|
@ -66,8 +65,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
|||
@IStorageService private readonly storageService: IStorageService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IDebugHelperService private readonly debugHelperService: IDebugHelperService
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
this.configProviders = [];
|
||||
this.adapterDescriptorFactories = [];
|
||||
|
@ -111,14 +109,11 @@ export class ConfigurationManager implements IConfigurationManager {
|
|||
}
|
||||
|
||||
runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise<number | undefined> {
|
||||
let tl: ITerminalLauncher | undefined = this.debugAdapterFactories.get(debugType);
|
||||
if (!tl) {
|
||||
if (!this.terminalLauncher) {
|
||||
this.terminalLauncher = this.debugHelperService.createTerminalLauncher(this.instantiationService);
|
||||
}
|
||||
tl = this.terminalLauncher;
|
||||
let tl = this.debugAdapterFactories.get(debugType);
|
||||
if (tl) {
|
||||
return tl.runInTerminal(args, config);
|
||||
}
|
||||
return tl.runInTerminal(args, config);
|
||||
return Promise.resolve(void 0);
|
||||
}
|
||||
|
||||
// debug adapter
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ServiceIdentifier, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITerminalLauncher, IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
@ -13,10 +13,6 @@ export class BrowserDebugHelperService implements IDebugHelperService {
|
|||
|
||||
_serviceBrand: ServiceIdentifier<IDebugHelperService>;
|
||||
|
||||
createTerminalLauncher(instantiationService: IInstantiationService): ITerminalLauncher {
|
||||
throw new Error('Method createTerminalLauncher not implemented.');
|
||||
}
|
||||
|
||||
createTelemetryService(configurationService: IConfigurationService, args: string[]): TelemetryService | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { URI as uri } from 'vs/base/common/uri';
|
|||
import severity from 'vs/base/common/severity';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel as EditorIModel } from 'vs/editor/common/model';
|
||||
import { IEditor, ITextEditor } from 'vs/workbench/common/editor';
|
||||
|
@ -846,7 +846,5 @@ export const IDebugHelperService = createDecorator<IDebugHelperService>(DEBUG_HE
|
|||
export interface IDebugHelperService {
|
||||
_serviceBrand: any;
|
||||
|
||||
createTerminalLauncher(instantiationService: IInstantiationService): ITerminalLauncher;
|
||||
|
||||
createTelemetryService(configurationService: IConfigurationService, args: string[]): TelemetryService | undefined;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalLauncher } from 'vs/workbench/contrib/debug/node/terminalSupport';
|
||||
import { ITerminalLauncher, IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
|
||||
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
|
@ -20,10 +18,6 @@ export class NodeDebugHelperService implements IDebugHelperService {
|
|||
) {
|
||||
}
|
||||
|
||||
createTerminalLauncher(instantiationService: IInstantiationService): ITerminalLauncher {
|
||||
return instantiationService.createInstance(TerminalLauncher);
|
||||
}
|
||||
|
||||
createTelemetryService(configurationService: IConfigurationService, args: string[]): TelemetryService | undefined {
|
||||
|
||||
const client = new TelemetryClient(
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
|
||||
import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { hasChildProcesses, prepareCommand } from 'vs/workbench/contrib/debug/node/terminals';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
|
||||
export class TerminalLauncher implements ITerminalLauncher {
|
||||
|
||||
private integratedTerminalInstance: ITerminalInstance | undefined;
|
||||
private terminalDisposedListener: IDisposable;
|
||||
|
||||
constructor(
|
||||
@ITerminalService private readonly terminalService: ITerminalService,
|
||||
@IExternalTerminalService private readonly externalTerminalService: IExternalTerminalService
|
||||
) {
|
||||
}
|
||||
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise<number | undefined> {
|
||||
|
||||
if (args.kind === 'external') {
|
||||
return this.externalTerminalService.runInTerminal(args.title || '', args.cwd, args.args, <IProcessEnvironment>args.env || {});
|
||||
}
|
||||
|
||||
if (!this.terminalDisposedListener) {
|
||||
// React on terminal disposed and check if that is the debug terminal #12956
|
||||
this.terminalDisposedListener = this.terminalService.onInstanceDisposed(terminal => {
|
||||
if (this.integratedTerminalInstance && this.integratedTerminalInstance.id === terminal.id) {
|
||||
this.integratedTerminalInstance = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let t = this.integratedTerminalInstance;
|
||||
if ((t && (typeof t.processId === 'number') && hasChildProcesses(t.processId)) || !t) {
|
||||
t = this.terminalService.createTerminal({ name: args.title || nls.localize('debug.terminal.title', "debuggee") });
|
||||
this.integratedTerminalInstance = t;
|
||||
}
|
||||
this.terminalService.setActiveInstance(t);
|
||||
this.terminalService.showPanel(true);
|
||||
|
||||
return new Promise<number | undefined>((resolve, error) => {
|
||||
if (t && typeof t.processId === 'number') {
|
||||
// no need to wait
|
||||
resolve(t.processId);
|
||||
}
|
||||
|
||||
// shell not ready: wait for ready event
|
||||
const toDispose = t!.onProcessIdReady(t => {
|
||||
toDispose.dispose();
|
||||
resolve(t.processId);
|
||||
});
|
||||
|
||||
// do not wait longer than 5 seconds
|
||||
setTimeout(_ => {
|
||||
error(new Error('terminal shell timeout'));
|
||||
}, 5000);
|
||||
|
||||
}).then(shellProcessId => {
|
||||
|
||||
const command = prepareCommand(args, config);
|
||||
t!.sendText(command, true);
|
||||
|
||||
return shellProcessId;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,277 +4,30 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { getSystemShell } from 'vs/workbench/contrib/terminal/node/terminal';
|
||||
import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
|
||||
|
||||
const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console");
|
||||
let externalTerminalService: IExternalTerminalService | undefined = undefined;
|
||||
|
||||
let terminalLauncher: ITerminalLauncher | undefined = undefined;
|
||||
|
||||
export function getTerminalLauncher() {
|
||||
if (!terminalLauncher) {
|
||||
export function runInExternalTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): void {
|
||||
if (!externalTerminalService) {
|
||||
if (env.isWindows) {
|
||||
terminalLauncher = new WinTerminalService();
|
||||
externalTerminalService = new WindowsExternalTerminalService(<IConfigurationService><unknown>undefined);
|
||||
} else if (env.isMacintosh) {
|
||||
terminalLauncher = new MacTerminalService();
|
||||
externalTerminalService = new MacExternalTerminalService(<IConfigurationService><unknown>undefined);
|
||||
} else if (env.isLinux) {
|
||||
terminalLauncher = new LinuxTerminalService();
|
||||
externalTerminalService = new LinuxExternalTerminalService(<IConfigurationService><unknown>undefined);
|
||||
}
|
||||
}
|
||||
return terminalLauncher;
|
||||
}
|
||||
|
||||
let _DEFAULT_TERMINAL_LINUX_READY: Promise<string> | null = null;
|
||||
|
||||
export function getDefaultTerminalLinuxReady(): Promise<string> {
|
||||
if (!_DEFAULT_TERMINAL_LINUX_READY) {
|
||||
_DEFAULT_TERMINAL_LINUX_READY = new Promise<string>(resolve => {
|
||||
if (env.isLinux) {
|
||||
Promise.all<any>([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => {
|
||||
if (isDebian) {
|
||||
resolve('x-terminal-emulator');
|
||||
} else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') {
|
||||
resolve('gnome-terminal');
|
||||
} else if (process.env.DESKTOP_SESSION === 'kde-plasma') {
|
||||
resolve('konsole');
|
||||
} else if (process.env.COLORTERM) {
|
||||
resolve(process.env.COLORTERM);
|
||||
} else if (process.env.TERM) {
|
||||
resolve(process.env.TERM);
|
||||
} else {
|
||||
resolve('xterm');
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
resolve('xterm');
|
||||
});
|
||||
}
|
||||
return _DEFAULT_TERMINAL_LINUX_READY;
|
||||
}
|
||||
|
||||
let _DEFAULT_TERMINAL_WINDOWS: string | null = null;
|
||||
|
||||
export function getDefaultTerminalWindows(): string {
|
||||
if (!_DEFAULT_TERMINAL_WINDOWS) {
|
||||
const isWoW64 = !!process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
|
||||
_DEFAULT_TERMINAL_WINDOWS = `${process.env.windir ? process.env.windir : 'C:\\Windows'}\\${isWoW64 ? 'Sysnative' : 'System32'}\\cmd.exe`;
|
||||
}
|
||||
return _DEFAULT_TERMINAL_WINDOWS;
|
||||
}
|
||||
|
||||
abstract class TerminalLauncher implements ITerminalLauncher {
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise<number | undefined> {
|
||||
return this.runInTerminal0(args.title!, args.cwd, args.args, args.env || {}, config);
|
||||
}
|
||||
|
||||
abstract runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment | {}, config: ITerminalSettings): Promise<number | undefined>;
|
||||
}
|
||||
|
||||
class WinTerminalService extends TerminalLauncher {
|
||||
|
||||
private static readonly CMD = 'cmd.exe';
|
||||
|
||||
runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const exec = configuration.external.windowsExec || getDefaultTerminalWindows();
|
||||
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
const title = `"${dir} - ${TERMINAL_TITLE}"`;
|
||||
const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code
|
||||
|
||||
const cmdArgs = [
|
||||
'/c', 'start', title, '/wait', exec, '/c', command
|
||||
];
|
||||
|
||||
// merge environment variables into a copy of the process.env
|
||||
const env = assign({}, process.env, envVars);
|
||||
|
||||
// delete environment variables that have a null value
|
||||
Object.keys(env).filter(v => env[v] === null).forEach(key => delete env[key]);
|
||||
|
||||
const options: any = {
|
||||
cwd: dir,
|
||||
env: env,
|
||||
windowsVerbatimArguments: true
|
||||
};
|
||||
|
||||
const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options);
|
||||
cmd.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
|
||||
resolve(undefined);
|
||||
});
|
||||
if (externalTerminalService) {
|
||||
externalTerminalService.runInTerminal(args.title!, args.cwd, args.args, args.env || {}, config);
|
||||
}
|
||||
}
|
||||
|
||||
class MacTerminalService extends TerminalLauncher {
|
||||
|
||||
private static readonly DEFAULT_TERMINAL_OSX = 'Terminal.app';
|
||||
private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X
|
||||
|
||||
runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const terminalApp = configuration.external.osxExec || MacTerminalService.DEFAULT_TERMINAL_OSX;
|
||||
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
if (terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') {
|
||||
|
||||
// On OS X we launch an AppleScript that creates (or reuses) a Terminal window
|
||||
// and then launches the program inside that window.
|
||||
|
||||
const script = terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX ? 'TerminalHelper' : 'iTermHelper';
|
||||
const scriptpath = getPathFromAmdModule(require, `vs/workbench/contrib/externalTerminal/node/${script}.scpt`);
|
||||
|
||||
const osaArgs = [
|
||||
scriptpath,
|
||||
'-t', title || TERMINAL_TITLE,
|
||||
'-w', dir,
|
||||
];
|
||||
|
||||
for (let a of args) {
|
||||
osaArgs.push('-a');
|
||||
osaArgs.push(a);
|
||||
}
|
||||
|
||||
if (envVars) {
|
||||
for (let key in envVars) {
|
||||
const value = envVars[key];
|
||||
if (value === null) {
|
||||
osaArgs.push('-u');
|
||||
osaArgs.push(key);
|
||||
} else {
|
||||
osaArgs.push('-e');
|
||||
osaArgs.push(`${key}=${value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let stderr = '';
|
||||
const osa = cp.spawn(MacTerminalService.OSASCRIPT, osaArgs);
|
||||
osa.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
osa.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
osa.on('exit', (code: number) => {
|
||||
if (code === 0) { // OK
|
||||
resolve(undefined);
|
||||
} else {
|
||||
if (stderr) {
|
||||
const lines = stderr.split('\n', 1);
|
||||
reject(new Error(lines[0]));
|
||||
} else {
|
||||
reject(new Error(nls.localize('mac.terminal.script.failed', "Script '{0}' failed with exit code {1}", script, code)));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
reject(new Error(nls.localize('mac.terminal.type.not.supported', "'{0}' not supported", terminalApp)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class LinuxTerminalService extends TerminalLauncher {
|
||||
|
||||
private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue...");
|
||||
|
||||
runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const terminalConfig = configuration.external;
|
||||
const execThenable: Promise<string> = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady();
|
||||
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
let termArgs: string[] = [];
|
||||
//termArgs.push('--title');
|
||||
//termArgs.push(`"${TERMINAL_TITLE}"`);
|
||||
execThenable.then(exec => {
|
||||
if (exec.indexOf('gnome-terminal') >= 0) {
|
||||
termArgs.push('-x');
|
||||
} else {
|
||||
termArgs.push('-e');
|
||||
}
|
||||
termArgs.push('bash');
|
||||
termArgs.push('-c');
|
||||
|
||||
const bashCommand = `${quote(args)}; echo; read -p "${LinuxTerminalService.WAIT_MESSAGE}" -n1;`;
|
||||
termArgs.push(`''${bashCommand}''`); // wrapping argument in two sets of ' because node is so "friendly" that it removes one set...
|
||||
|
||||
// merge environment variables into a copy of the process.env
|
||||
const env = assign({}, process.env, envVars);
|
||||
|
||||
// delete environment variables that have a null value
|
||||
Object.keys(env).filter(v => env[v] === null).forEach(key => delete env[key]);
|
||||
|
||||
const options: any = {
|
||||
cwd: dir,
|
||||
env: env
|
||||
};
|
||||
|
||||
let stderr = '';
|
||||
const cmd = cp.spawn(exec, termArgs, options);
|
||||
cmd.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
cmd.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
cmd.on('exit', (code: number) => {
|
||||
if (code === 0) { // OK
|
||||
resolve(undefined);
|
||||
} else {
|
||||
if (stderr) {
|
||||
const lines = stderr.split('\n', 1);
|
||||
reject(new Error(lines[0]));
|
||||
} else {
|
||||
reject(new Error(nls.localize('linux.term.failed', "'{0}' failed with exit code {1}", exec, code)));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to turn OS errors into more meaningful error messages
|
||||
*/
|
||||
function improveError(err: Error): Error {
|
||||
if (err['errno'] === 'ENOENT' && err['path']) {
|
||||
return new Error(nls.localize('ext.term.app.not.found', "can't find terminal application '{0}'", err['path']));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote args if necessary and combine into a space separated string.
|
||||
*/
|
||||
function quote(args: string[]): string {
|
||||
let r = '';
|
||||
for (let a of args) {
|
||||
if (a.indexOf(' ') >= 0) {
|
||||
r += '"' + a + '"';
|
||||
} else {
|
||||
r += a;
|
||||
}
|
||||
r += ' ';
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
export function hasChildProcesses(processId: number): boolean {
|
||||
if (processId) {
|
||||
try {
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug';
|
||||
|
||||
export const IExternalTerminalService = createDecorator<IExternalTerminalService>('nativeTerminalService');
|
||||
|
||||
export interface IExternalTerminalService {
|
||||
_serviceBrand: any;
|
||||
openTerminal(path: string): void;
|
||||
runInTerminal(title: string, cwd: string, args: string[], env: IProcessEnvironment): Promise<number | undefined>;
|
||||
runInTerminal(title: string, cwd: string, args: string[], env: { [key: string]: string | null; }, configuration: ITerminalSettings): Promise<number | undefined>;
|
||||
}
|
||||
|
||||
export interface IExternalTerminalConfiguration {
|
||||
|
|
|
@ -16,11 +16,11 @@ import { getPathFromAmdModule } from 'vs/base/common/amd';
|
|||
import { IConfigurationRegistry, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug';
|
||||
|
||||
|
||||
export const DEFAULT_TERMINAL_OSX = 'Terminal.app';
|
||||
const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console");
|
||||
|
||||
export const DEFAULT_TERMINAL_OSX = 'Terminal.app';
|
||||
|
||||
enum WinSpawnType {
|
||||
CMD,
|
||||
|
@ -43,13 +43,18 @@ export class WindowsExternalTerminalService implements IExternalTerminalService
|
|||
this.spawnTerminal(cp, configuration, processes.getWindowsShell(), cwd);
|
||||
}
|
||||
|
||||
/*
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment): Promise<number | undefined> {
|
||||
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const exec = terminalConfig.windowsExec || getDefaultTerminalWindows();
|
||||
return this.runInTerminal0(title, dir, args, envVars, configuration.terminal);
|
||||
}
|
||||
*/
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const exec = configuration.external.windowsExec || getDefaultTerminalWindows();
|
||||
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
const title = `"${dir} - ${TERMINAL_TITLE}"`;
|
||||
const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code
|
||||
|
@ -71,9 +76,11 @@ export class WindowsExternalTerminalService implements IExternalTerminalService
|
|||
};
|
||||
|
||||
const cmd = cp.spawn(WindowsExternalTerminalService.CMD, cmdArgs, options);
|
||||
cmd.on('error', e);
|
||||
cmd.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
|
||||
c(undefined);
|
||||
resolve(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -134,13 +141,18 @@ export class MacExternalTerminalService implements IExternalTerminalService {
|
|||
this.spawnTerminal(cp, configuration, cwd);
|
||||
}
|
||||
|
||||
/*
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment): Promise<number | undefined> {
|
||||
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX;
|
||||
return this.runInTerminal0(title, dir, args, envVars, configuration.terminal);
|
||||
}
|
||||
*/
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const terminalApp = configuration.external.osxExec || DEFAULT_TERMINAL_OSX;
|
||||
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
if (terminalApp === DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') {
|
||||
|
||||
|
@ -176,24 +188,26 @@ export class MacExternalTerminalService implements IExternalTerminalService {
|
|||
|
||||
let stderr = '';
|
||||
const osa = cp.spawn(MacExternalTerminalService.OSASCRIPT, osaArgs);
|
||||
osa.on('error', e);
|
||||
osa.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
osa.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
osa.on('exit', (code: number) => {
|
||||
if (code === 0) { // OK
|
||||
c(undefined);
|
||||
resolve(undefined);
|
||||
} else {
|
||||
if (stderr) {
|
||||
const lines = stderr.split('\n', 1);
|
||||
e(new Error(lines[0]));
|
||||
reject(new Error(lines[0]));
|
||||
} else {
|
||||
e(new Error(nls.localize('mac.terminal.script.failed', "Script '{0}' failed with exit code {1}", script, code)));
|
||||
reject(new Error(nls.localize('mac.terminal.script.failed', "Script '{0}' failed with exit code {1}", script, code)));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
e(new Error(nls.localize('mac.terminal.type.not.supported', "'{0}' not supported", terminalApp)));
|
||||
reject(new Error(nls.localize('mac.terminal.type.not.supported', "'{0}' not supported", terminalApp)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -223,20 +237,25 @@ export class LinuxExternalTerminalService implements IExternalTerminalService {
|
|||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) { }
|
||||
|
||||
|
||||
public openTerminal(cwd?: string): void {
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
|
||||
this.spawnTerminal(cp, configuration, cwd);
|
||||
}
|
||||
|
||||
/*
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment): Promise<number | undefined> {
|
||||
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
return this.runInTerminal0(title, dir, args, envVars, configuration.terminal);
|
||||
}
|
||||
*/
|
||||
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const terminalConfig = configuration.external;
|
||||
const execPromise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady();
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
let termArgs: string[] = [];
|
||||
//termArgs.push('--title');
|
||||
|
@ -266,19 +285,21 @@ export class LinuxExternalTerminalService implements IExternalTerminalService {
|
|||
|
||||
let stderr = '';
|
||||
const cmd = cp.spawn(exec, termArgs, options);
|
||||
cmd.on('error', e);
|
||||
cmd.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
cmd.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
cmd.on('exit', (code: number) => {
|
||||
if (code === 0) { // OK
|
||||
c(undefined);
|
||||
resolve(undefined);
|
||||
} else {
|
||||
if (stderr) {
|
||||
const lines = stderr.split('\n', 1);
|
||||
e(new Error(lines[0]));
|
||||
reject(new Error(lines[0]));
|
||||
} else {
|
||||
e(new Error(nls.localize('linux.term.failed', "'{0}' failed with exit code {1}", exec, code)));
|
||||
reject(new Error(nls.localize('linux.term.failed', "'{0}' failed with exit code {1}", exec, code)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -301,6 +322,16 @@ export class LinuxExternalTerminalService implements IExternalTerminalService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to turn OS errors into more meaningful error messages
|
||||
*/
|
||||
function improveError(err: Error): Error {
|
||||
if (err['errno'] === 'ENOENT' && err['path']) {
|
||||
return new Error(nls.localize('ext.term.app.not.found', "can't find terminal application '{0}'", err['path']));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote args if necessary and combine into a space separated string.
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue