mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-04-01 01:20:31 -04:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
@@ -4,19 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import * as paths from 'vs/base/common/path';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IExternalTerminalConfiguration, IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
|
||||
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ITerminalService as IIntegratedTerminalService, KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal';
|
||||
import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminalService';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
@@ -27,82 +21,61 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
if (env.isWindows) {
|
||||
registerSingleton(IExternalTerminalService, WindowsExternalTerminalService, true);
|
||||
} else if (env.isMacintosh) {
|
||||
registerSingleton(IExternalTerminalService, MacExternalTerminalService, true);
|
||||
} else if (env.isLinux) {
|
||||
registerSingleton(IExternalTerminalService, LinuxExternalTerminalService, true);
|
||||
}
|
||||
|
||||
getDefaultTerminalLinuxReady().then(defaultTerminalLinux => {
|
||||
let configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
id: 'externalTerminal',
|
||||
order: 100,
|
||||
title: nls.localize('terminalConfigurationTitle', "External Terminal"),
|
||||
type: 'object',
|
||||
properties: {
|
||||
'terminal.explorerKind': {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'integrated',
|
||||
'external'
|
||||
],
|
||||
enumDescriptions: [
|
||||
nls.localize('terminal.explorerKind.integrated', "Use VS Code's integrated terminal."),
|
||||
nls.localize('terminal.explorerKind.external', "Use the configured external terminal.")
|
||||
],
|
||||
description: nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."),
|
||||
default: 'integrated'
|
||||
},
|
||||
'terminal.external.windowsExec': {
|
||||
type: 'string',
|
||||
description: nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."),
|
||||
default: getDefaultTerminalWindows(),
|
||||
scope: ConfigurationScope.APPLICATION
|
||||
},
|
||||
'terminal.external.osxExec': {
|
||||
type: 'string',
|
||||
description: nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on macOS."),
|
||||
default: DEFAULT_TERMINAL_OSX,
|
||||
scope: ConfigurationScope.APPLICATION
|
||||
},
|
||||
'terminal.external.linuxExec': {
|
||||
type: 'string',
|
||||
description: nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."),
|
||||
default: defaultTerminalLinux,
|
||||
scope: ConfigurationScope.APPLICATION
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const OPEN_IN_TERMINAL_COMMAND_ID = 'openInTerminal';
|
||||
CommandsRegistry.registerCommand({
|
||||
id: OPEN_IN_TERMINAL_COMMAND_ID,
|
||||
handler: (accessor, resource: uri) => {
|
||||
handler: (accessor, resource: URI) => {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const fileService = accessor.get(IFileService);
|
||||
const terminalService: IExternalTerminalService | undefined = accessor.get(IExternalTerminalService, optional);
|
||||
const integratedTerminalService = accessor.get(IIntegratedTerminalService);
|
||||
const terminalService = accessor.get(IExternalTerminalService);
|
||||
const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService);
|
||||
const remoteAgentService = accessor.get(IRemoteAgentService);
|
||||
|
||||
return fileService.resolveAll(resources.map(r => ({ resource: r }))).then(stats => {
|
||||
const directoriesToOpen = distinct(stats.filter(data => data.success).map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : paths.dirname(stat!.resource.fsPath)));
|
||||
return directoriesToOpen.map(dir => {
|
||||
if (configurationService.getValue<IExternalTerminalConfiguration>().terminal.explorerKind === 'integrated') {
|
||||
const instance = integratedTerminalService.createTerminal({ cwd: dir });
|
||||
if (instance && (resources.length === 1 || !resource || dir === resource.fsPath || dir === paths.dirname(resource.fsPath))) {
|
||||
const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService);
|
||||
return fileService.resolveAll(resources.map(r => ({ resource: r }))).then(async stats => {
|
||||
const targets = distinct(stats.filter(data => data.success));
|
||||
// Always use integrated terminal when using a remote
|
||||
const useIntegratedTerminal = remoteAgentService.getConnection() || configurationService.getValue<IExternalTerminalConfiguration>().terminal.explorerKind === 'integrated';
|
||||
if (useIntegratedTerminal) {
|
||||
|
||||
|
||||
// TODO: Use uri for cwd in createterminal
|
||||
|
||||
|
||||
const opened: { [path: string]: boolean } = {};
|
||||
targets.map(({ stat }) => {
|
||||
const resource = stat!.resource;
|
||||
if (stat!.isDirectory) {
|
||||
return resource;
|
||||
}
|
||||
return URI.from({
|
||||
scheme: resource.scheme,
|
||||
authority: resource.authority,
|
||||
fragment: resource.fragment,
|
||||
query: resource.query,
|
||||
path: paths.dirname(resource.path)
|
||||
});
|
||||
}).forEach(cwd => {
|
||||
if (opened[cwd.path]) {
|
||||
return;
|
||||
}
|
||||
opened[cwd.path] = true;
|
||||
const instance = integratedTerminalService.createTerminal({ cwd });
|
||||
if (instance && (resources.length === 1 || !resource || cwd.path === resource.path || cwd.path === paths.dirname(resource.path))) {
|
||||
integratedTerminalService.setActiveInstance(instance);
|
||||
integratedTerminalService.showPanel(true);
|
||||
}
|
||||
} else {
|
||||
terminalService.openTerminal(dir);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
distinct(targets.map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : paths.dirname(stat!.resource.fsPath))).forEach(cwd => {
|
||||
terminalService!.openTerminal(cwd);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -114,7 +87,31 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
when: KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
handler: (accessor) => {
|
||||
const remoteAgentService = accessor.get(IRemoteAgentService);
|
||||
const historyService = accessor.get(IHistoryService);
|
||||
|
||||
// Open integrated terminal in remote workspaces
|
||||
if (remoteAgentService.getConnection()) {
|
||||
const integratedTerminalService = accessor.get(IIntegratedTerminalService);
|
||||
const root = historyService.getLastActiveWorkspaceRoot(Schemas.vscodeRemote);
|
||||
let cwd: string | undefined;
|
||||
if (root) {
|
||||
cwd = root.path;
|
||||
} else {
|
||||
const activeFile = historyService.getLastActiveFile(Schemas.vscodeRemote);
|
||||
if (activeFile) {
|
||||
cwd = paths.dirname(activeFile.path);
|
||||
}
|
||||
}
|
||||
if (cwd) {
|
||||
const instance = integratedTerminalService.createTerminal({ cwd });
|
||||
integratedTerminalService.setActiveInstance(instance);
|
||||
integratedTerminalService.showPanel(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Open external terminal in local workspaces
|
||||
const terminalService = accessor.get(IExternalTerminalService);
|
||||
const root = historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||
if (root) {
|
||||
@@ -146,10 +143,22 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
|
||||
command: openConsoleCommand,
|
||||
when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
|
||||
group: 'navigation',
|
||||
order: 30,
|
||||
command: openConsoleCommand,
|
||||
when: ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote)
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: 'navigation',
|
||||
order: 30,
|
||||
command: openConsoleCommand,
|
||||
when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: 'navigation',
|
||||
order: 30,
|
||||
command: openConsoleCommand,
|
||||
when: ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote)
|
||||
});
|
||||
|
||||
@@ -4,23 +4,24 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
|
||||
export const IExternalTerminalService = createDecorator<IExternalTerminalService>('nativeTerminalService');
|
||||
|
||||
export interface IExternalTerminalSettings {
|
||||
linuxExec?: string;
|
||||
osxExec?: string;
|
||||
windowsExec?: string;
|
||||
}
|
||||
|
||||
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; }, settings: IExternalTerminalSettings): Promise<number | undefined>;
|
||||
}
|
||||
|
||||
export interface IExternalTerminalConfiguration {
|
||||
terminal: {
|
||||
explorerKind: 'integrated' | 'external',
|
||||
external: {
|
||||
linuxExec: string,
|
||||
osxExec: string,
|
||||
windowsExec: string
|
||||
}
|
||||
external: IExternalTerminalSettings;
|
||||
};
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
|
||||
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>(c => {
|
||||
if (env.isLinux) {
|
||||
Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv || Promise.resolve(undefined)]).then(([isDebian]) => {
|
||||
if (isDebian) {
|
||||
c('x-terminal-emulator');
|
||||
} else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') {
|
||||
c('gnome-terminal');
|
||||
} else if (process.env.DESKTOP_SESSION === 'kde-plasma') {
|
||||
c('konsole');
|
||||
} else if (process.env.COLORTERM) {
|
||||
c(process.env.COLORTERM);
|
||||
} else if (process.env.TERM) {
|
||||
c(process.env.TERM);
|
||||
} else {
|
||||
c('xterm');
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
c('xterm');
|
||||
});
|
||||
}
|
||||
return _DEFAULT_TERMINAL_LINUX_READY;
|
||||
}
|
||||
|
||||
export const DEFAULT_TERMINAL_OSX = 'Terminal.app';
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -7,19 +7,20 @@ import * as cp from 'child_process';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as processes from 'vs/base/node/processes';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IExternalTerminalService, IExternalTerminalConfiguration } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
|
||||
import { IExternalTerminalService, IExternalTerminalConfiguration, IExternalTerminalSettings } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
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 { optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
|
||||
const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console");
|
||||
|
||||
enum WinSpawnType {
|
||||
CMD,
|
||||
CMDER
|
||||
}
|
||||
export const DEFAULT_TERMINAL_OSX = 'Terminal.app';
|
||||
|
||||
export class WindowsExternalTerminalService implements IExternalTerminalService {
|
||||
public _serviceBrand: any;
|
||||
@@ -27,23 +28,22 @@ export class WindowsExternalTerminalService implements IExternalTerminalService
|
||||
private static readonly CMD = 'cmd.exe';
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
@optional(IConfigurationService) private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
}
|
||||
|
||||
public openTerminal(cwd?: string): void {
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
|
||||
this.spawnTerminal(cp, configuration, processes.getWindowsShell(), cwd);
|
||||
if (this._configurationService) {
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
this.spawnTerminal(cp, configuration, processes.getWindowsShell(), cwd);
|
||||
}
|
||||
}
|
||||
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise<number | undefined> {
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, settings: IExternalTerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const exec = terminalConfig.windowsExec || getDefaultTerminalWindows();
|
||||
const exec = settings.windowsExec || WindowsExternalTerminalService.getDefaultTerminalWindows();
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
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
|
||||
@@ -65,16 +65,17 @@ 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);
|
||||
});
|
||||
}
|
||||
|
||||
private spawnTerminal(spawner: typeof cp, configuration: IExternalTerminalConfiguration, command: string, cwd?: string): Promise<void> {
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const exec = terminalConfig.windowsExec || getDefaultTerminalWindows();
|
||||
const spawnType = this.getSpawnType(exec);
|
||||
const exec = terminalConfig.windowsExec || WindowsExternalTerminalService.getDefaultTerminalWindows();
|
||||
|
||||
// Make the drive letter uppercase on Windows (see #9448)
|
||||
if (cwd && cwd[1] === ':') {
|
||||
@@ -83,7 +84,8 @@ export class WindowsExternalTerminalService implements IExternalTerminalService
|
||||
|
||||
// cmder ignores the environment cwd and instead opts to always open in %USERPROFILE%
|
||||
// unless otherwise specified
|
||||
if (spawnType === WinSpawnType.CMDER) {
|
||||
const basename = path.basename(exec).toLowerCase();
|
||||
if (basename === 'cmder' || basename === 'cmder.exe') {
|
||||
spawner.spawn(exec, cwd ? [cwd] : undefined);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
@@ -104,12 +106,14 @@ export class WindowsExternalTerminalService implements IExternalTerminalService
|
||||
});
|
||||
}
|
||||
|
||||
private getSpawnType(exec: string): WinSpawnType {
|
||||
const basename = path.basename(exec).toLowerCase();
|
||||
if (basename === 'cmder' || basename === 'cmder.exe') {
|
||||
return WinSpawnType.CMDER;
|
||||
private static _DEFAULT_TERMINAL_WINDOWS: string;
|
||||
|
||||
public static getDefaultTerminalWindows(): string {
|
||||
if (!WindowsExternalTerminalService._DEFAULT_TERMINAL_WINDOWS) {
|
||||
const isWoW64 = !!process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
|
||||
WindowsExternalTerminalService._DEFAULT_TERMINAL_WINDOWS = `${process.env.windir ? process.env.windir : 'C:\\Windows'}\\${isWoW64 ? 'Sysnative' : 'System32'}\\cmd.exe`;
|
||||
}
|
||||
return WinSpawnType.CMD;
|
||||
return WindowsExternalTerminalService._DEFAULT_TERMINAL_WINDOWS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,22 +123,21 @@ export class MacExternalTerminalService implements IExternalTerminalService {
|
||||
private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
@optional(IConfigurationService) private readonly _configurationService: IConfigurationService
|
||||
) { }
|
||||
|
||||
public openTerminal(cwd?: string): void {
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
|
||||
this.spawnTerminal(cp, configuration, cwd);
|
||||
if (this._configurationService) {
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
this.spawnTerminal(cp, configuration, cwd);
|
||||
}
|
||||
}
|
||||
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise<number | undefined> {
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, settings: IExternalTerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX;
|
||||
const terminalApp = settings.osxExec || DEFAULT_TERMINAL_OSX;
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
if (terminalApp === DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') {
|
||||
|
||||
@@ -142,7 +145,7 @@ export class MacExternalTerminalService implements IExternalTerminalService {
|
||||
// and then launches the program inside that window.
|
||||
|
||||
const script = terminalApp === DEFAULT_TERMINAL_OSX ? 'TerminalHelper' : 'iTermHelper';
|
||||
const scriptpath = getPathFromAmdModule(require, `vs/workbench/contrib/externalTerminal/electron-browser/${script}.scpt`);
|
||||
const scriptpath = getPathFromAmdModule(require, `vs/workbench/contrib/externalTerminal/node/${script}.scpt`);
|
||||
|
||||
const osaArgs = [
|
||||
scriptpath,
|
||||
@@ -170,24 +173,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)));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -214,23 +219,21 @@ export class LinuxExternalTerminalService implements IExternalTerminalService {
|
||||
private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue...");
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
@optional(IConfigurationService) private readonly _configurationService: IConfigurationService
|
||||
) { }
|
||||
|
||||
|
||||
public openTerminal(cwd?: string): void {
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
|
||||
this.spawnTerminal(cp, configuration, cwd);
|
||||
if (this._configurationService) {
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
this.spawnTerminal(cp, configuration, cwd);
|
||||
}
|
||||
}
|
||||
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise<number | undefined> {
|
||||
public runInTerminal(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, settings: IExternalTerminalSettings): Promise<number | undefined> {
|
||||
|
||||
const configuration = this._configurationService.getValue<IExternalTerminalConfiguration>();
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const execPromise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady();
|
||||
const execPromise = settings.linuxExec ? Promise.resolve(settings.linuxExec) : LinuxExternalTerminalService.getDefaultTerminalLinuxReady();
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
let termArgs: string[] = [];
|
||||
//termArgs.push('--title');
|
||||
@@ -260,19 +263,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)));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -282,7 +287,7 @@ export class LinuxExternalTerminalService implements IExternalTerminalService {
|
||||
|
||||
private spawnTerminal(spawner: typeof cp, configuration: IExternalTerminalConfiguration, cwd?: string): Promise<void> {
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const execPromise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady();
|
||||
const execPromise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : LinuxExternalTerminalService.getDefaultTerminalLinuxReady();
|
||||
|
||||
return new Promise<void>((c, e) => {
|
||||
execPromise.then(exec => {
|
||||
@@ -293,6 +298,46 @@ export class LinuxExternalTerminalService implements IExternalTerminalService {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static _DEFAULT_TERMINAL_LINUX_READY: Promise<string>;
|
||||
|
||||
public static getDefaultTerminalLinuxReady(): Promise<string> {
|
||||
if (!LinuxExternalTerminalService._DEFAULT_TERMINAL_LINUX_READY) {
|
||||
LinuxExternalTerminalService._DEFAULT_TERMINAL_LINUX_READY = new Promise<string>(c => {
|
||||
if (env.isLinux) {
|
||||
Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv || Promise.resolve(undefined)]).then(([isDebian]) => {
|
||||
if (isDebian) {
|
||||
c('x-terminal-emulator');
|
||||
} else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') {
|
||||
c('gnome-terminal');
|
||||
} else if (process.env.DESKTOP_SESSION === 'kde-plasma') {
|
||||
c('konsole');
|
||||
} else if (process.env.COLORTERM) {
|
||||
c(process.env.COLORTERM);
|
||||
} else if (process.env.TERM) {
|
||||
c(process.env.TERM);
|
||||
} else {
|
||||
c('xterm');
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
c('xterm');
|
||||
});
|
||||
}
|
||||
return LinuxExternalTerminalService._DEFAULT_TERMINAL_LINUX_READY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to turn OS errors into more meaningful error messages
|
||||
*/
|
||||
function improveError(err: Error): Error {
|
||||
if ('errno' in err && err['errno'] === 'ENOENT' && 'path' in err && typeof err['path'] === 'string') {
|
||||
return new Error(nls.localize('ext.term.app.not.found', "can't find terminal application '{0}'", err['path']));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,3 +355,54 @@ function quote(args: string[]): string {
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
if (env.isWindows) {
|
||||
registerSingleton(IExternalTerminalService, WindowsExternalTerminalService, true);
|
||||
} else if (env.isMacintosh) {
|
||||
registerSingleton(IExternalTerminalService, MacExternalTerminalService, true);
|
||||
} else if (env.isLinux) {
|
||||
registerSingleton(IExternalTerminalService, LinuxExternalTerminalService, true);
|
||||
}
|
||||
|
||||
LinuxExternalTerminalService.getDefaultTerminalLinuxReady().then(defaultTerminalLinux => {
|
||||
let configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
id: 'externalTerminal',
|
||||
order: 100,
|
||||
title: nls.localize('terminalConfigurationTitle', "External Terminal"),
|
||||
type: 'object',
|
||||
properties: {
|
||||
'terminal.explorerKind': {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'integrated',
|
||||
'external'
|
||||
],
|
||||
enumDescriptions: [
|
||||
nls.localize('terminal.explorerKind.integrated', "Use VS Code's integrated terminal."),
|
||||
nls.localize('terminal.explorerKind.external', "Use the configured external terminal.")
|
||||
],
|
||||
description: nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."),
|
||||
default: 'integrated'
|
||||
},
|
||||
'terminal.external.windowsExec': {
|
||||
type: 'string',
|
||||
description: nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."),
|
||||
default: WindowsExternalTerminalService.getDefaultTerminalWindows(),
|
||||
scope: ConfigurationScope.APPLICATION
|
||||
},
|
||||
'terminal.external.osxExec': {
|
||||
type: 'string',
|
||||
description: nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on macOS."),
|
||||
default: DEFAULT_TERMINAL_OSX,
|
||||
scope: ConfigurationScope.APPLICATION
|
||||
},
|
||||
'terminal.external.linuxExec': {
|
||||
type: 'string',
|
||||
description: nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."),
|
||||
default: defaultTerminalLinux,
|
||||
scope: ConfigurationScope.APPLICATION
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -4,8 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { deepEqual, equal } from 'assert';
|
||||
import { WindowsExternalTerminalService, LinuxExternalTerminalService, MacExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminalService';
|
||||
import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal';
|
||||
import { WindowsExternalTerminalService, LinuxExternalTerminalService, MacExternalTerminalService, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService';
|
||||
|
||||
suite('ExternalTerminalService', () => {
|
||||
let mockOnExit: Function;
|
||||
@@ -59,7 +58,7 @@ suite('ExternalTerminalService', () => {
|
||||
let mockSpawner = {
|
||||
spawn: (command: any, args: any, opts: any) => {
|
||||
// assert
|
||||
equal(args[args.length - 1], getDefaultTerminalWindows(), 'terminal should equal expected');
|
||||
equal(args[args.length - 1], WindowsExternalTerminalService.getDefaultTerminalWindows(), 'terminal should equal expected');
|
||||
done();
|
||||
return {
|
||||
on: (evt: any) => evt
|
||||
@@ -195,7 +194,7 @@ suite('ExternalTerminalService', () => {
|
||||
});
|
||||
|
||||
test(`LinuxTerminalService - uses default terminal when configuration.terminal.external.linuxExec is undefined`, done => {
|
||||
getDefaultTerminalLinuxReady().then(defaultTerminalLinux => {
|
||||
LinuxExternalTerminalService.getDefaultTerminalLinuxReady().then(defaultTerminalLinux => {
|
||||
let testCwd = 'path/to/workspace';
|
||||
let mockSpawner = {
|
||||
spawn: (command: any, args: any, opts: any) => {
|
||||
|
||||
Reference in New Issue
Block a user