diff --git a/packages/terminal/src/browser/terminal-preferences.ts b/packages/terminal/src/browser/terminal-preferences.ts index 6da05cabc8ded..8a67f8c38ba3b 100644 --- a/packages/terminal/src/browser/terminal-preferences.ts +++ b/packages/terminal/src/browser/terminal-preferences.ts @@ -107,6 +107,44 @@ export const TerminalConfigSchema: PreferenceSchema = { type: 'number', default: 1 }, + 'terminal.integrated.shell.windows': { + type: ['string', 'null'], + description: 'The path of the shell that the terminal uses on Windows. (default: C:\\Windows\\System32\\cmd.exe).', + markdownDescription: 'The path of the shell that the terminal uses on Windows. (default: C:\\Windows\\System32\\cmd.exe).', + default: undefined + }, + 'terminal.integrated.shell.osx': { + type: ['string', 'null'], + description: `The path of the shell that the terminal uses on macOS (default: ${process.env.SHELL || '/bin/bash'}).`, + markdownDescription: `The path of the shell that the terminal uses on macOS (default: ${process.env.SHELL || '/bin/bash'}).`, + default: undefined + }, + 'terminal.integrated.shell.linux': { + type: ['string', 'null'], + description: `The path of the shell that the terminal uses on Linux (default: ${process.env.SHELL || '/bin/bash'}).`, + markdownDescription: `The path of the shell that the terminal uses on Linux (default: ${process.env.SHELL || '/bin/bash'}).`, + default: undefined + }, + 'terminal.integrated.shellArgs.windows': { + type: 'array', + description: 'The command line arguments to use when on the Windows terminal.', + markdownDescription: 'The command line arguments to use when on the Windows terminal.', + default: [] + }, + 'terminal.integrated.shellArgs.osx': { + type: 'array', + description: 'The command line arguments to use when on the macOS terminal.', + markdownDescription: 'The command line arguments to use when on the macOS terminal.', + default: [ + '-l' + ] + }, + 'terminal.integrated.shellArgs.linux': { + type: 'array', + description: 'The command line arguments to use when on the Linux terminal.', + markdownDescription: 'The command line arguments to use when on the Linux terminal.', + default: [] + }, } }; @@ -126,7 +164,13 @@ export interface TerminalConfiguration { 'terminal.integrated.copyOnSelection': boolean, 'terminal.integrated.cursorBlinking': boolean, 'terminal.integrated.cursorStyle': CursorStyleVSCode, - 'terminal.integrated.cursorWidth': number + 'terminal.integrated.cursorWidth': number, + 'terminal.integrated.shell.windows': string | undefined, + 'terminal.integrated.shell.osx': string | undefined, + 'terminal.integrated.shell.linux': string | undefined, + 'terminal.integrated.shellArgs.windows': string[], + 'terminal.integrated.shellArgs.osx': string[], + 'terminal.integrated.shellArgs.linux': string[], } type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index 06f199c53cdfe..cee47c91c2406 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -21,7 +21,7 @@ import { ContributionProvider, Disposable, Event, Emitter, ILogger, DisposableCo import { Widget, Message, WebSocketConnectionProvider, StatefulWidget, isFirefox, MessageLoop, KeyCode } from '@theia/core/lib/browser'; import { isOSX } from '@theia/core/lib/common'; import { WorkspaceService } from '@theia/workspace/lib/browser'; -import { ShellTerminalServerProxy } from '../common/shell-terminal-protocol'; +import { ShellTerminalServerProxy, IShellTerminalPreferences } from '../common/shell-terminal-protocol'; import { terminalsPath } from '../common/terminal-protocol'; import { IBaseTerminalServer, TerminalProcessInfo } from '../common/base-terminal-protocol'; import { TerminalWatcher } from '../common/terminal-watcher'; @@ -368,6 +368,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget const { cols, rows } = this.term; const terminalId = await this.shellTerminalServer.create({ + shellPreferences: this.shellPreferences, shell: this.options.shellPath, args: this.options.shellArgs, env: this.options.env, @@ -581,6 +582,21 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget return this.preferences['terminal.enablePaste']; } + protected get shellPreferences(): IShellTerminalPreferences { + return { + shell: { + Windows: this.preferences['terminal.integrated.shell.windows'], + Linux: this.preferences['terminal.integrated.shell.linux'], + OSX: this.preferences['terminal.integrated.shell.osx'], + }, + shellArgs: { + Windows: this.preferences['terminal.integrated.shellArgs.windows'], + Linux: this.preferences['terminal.integrated.shellArgs.linux'], + OSX: this.preferences['terminal.integrated.shellArgs.osx'], + } + }; + } + protected customKeyHandler(event: KeyboardEvent): boolean { const keyBindings = KeyCode.createKeyCode(event).toString(); const ctrlCmdCopy = (isOSX && keyBindings === 'meta+c') || (!isOSX && keyBindings === 'ctrl+c'); diff --git a/packages/terminal/src/common/shell-terminal-protocol.ts b/packages/terminal/src/common/shell-terminal-protocol.ts index 88b6ed128d8c3..a81376f34d03d 100644 --- a/packages/terminal/src/common/shell-terminal-protocol.ts +++ b/packages/terminal/src/common/shell-terminal-protocol.ts @@ -16,6 +16,7 @@ import { JsonRpcProxy } from '@theia/core'; import { IBaseTerminalServer, IBaseTerminalServerOptions } from './base-terminal-protocol'; +import { OS } from '@theia/core/lib/common/os'; export const IShellTerminalServer = Symbol('IShellTerminalServer'); @@ -25,7 +26,17 @@ export interface IShellTerminalServer extends IBaseTerminalServer { export const shellTerminalPath = '/services/shell-terminal'; +export type ShellTerminalOSPreferences = { + [key in OS.Type]: T +}; + +export interface IShellTerminalPreferences { + shell: ShellTerminalOSPreferences, + shellArgs: ShellTerminalOSPreferences +}; + export interface IShellTerminalServerOptions extends IBaseTerminalServerOptions { + shellPreferences?: IShellTerminalPreferences, shell?: string, args?: string[], rootURI?: string, diff --git a/packages/terminal/src/node/shell-process.ts b/packages/terminal/src/node/shell-process.ts index 51ae8ce8db4fc..f9118ec7a17ee 100644 --- a/packages/terminal/src/node/shell-process.ts +++ b/packages/terminal/src/node/shell-process.ts @@ -18,16 +18,18 @@ import { injectable, inject, named } from 'inversify'; import * as os from 'os'; import { ILogger } from '@theia/core/lib/common/logger'; import { TerminalProcess, TerminalProcessOptions, ProcessManager, MultiRingBuffer } from '@theia/process/lib/node'; -import { isWindows, isOSX } from '@theia/core/lib/common'; +import { isWindows, isOSX, OS } from '@theia/core/lib/common'; import URI from '@theia/core/lib/common/uri'; import { FileUri } from '@theia/core/lib/node/file-uri'; import { parseArgs } from '@theia/process/lib/node/utils'; +import { IShellTerminalPreferences } from '../common/shell-terminal-protocol'; export const ShellProcessFactory = Symbol('ShellProcessFactory'); export type ShellProcessFactory = (options: ShellProcessOptions) => ShellProcess; export const ShellProcessOptions = Symbol('ShellProcessOptions'); export interface ShellProcessOptions { + shellPreferences?: IShellTerminalPreferences, shell?: string, args?: string[], rootURI?: string, @@ -75,8 +77,8 @@ export class ShellProcess extends TerminalProcess { @inject(ILogger) @named('terminal') logger: ILogger ) { super({ - command: options.shell || ShellProcess.getShellExecutablePath(), - args: options.args || ShellProcess.getShellExecutableArgs(), + command: options.shell || ShellProcess.getShellExecutablePath(options.shellPreferences), + args: options.args || ShellProcess.getShellExecutableArgs(options.shellPreferences), options: { name: 'xterm-color', cols: options.cols || ShellProcess.defaultCols, @@ -87,28 +89,31 @@ export class ShellProcess extends TerminalProcess { }, processManager, ringBuffer, logger); } - public static getShellExecutablePath(): string { + public static getShellExecutablePath(preferences?: IShellTerminalPreferences): string { const shell = process.env.THEIA_SHELL; if (shell) { return shell; } - if (isWindows) { + if (preferences && preferences.shell[OS.type()]) { + return preferences.shell[OS.type()]!; + } else if (isWindows) { return 'cmd.exe'; } else { return process.env.SHELL!; } } - public static getShellExecutableArgs(): string[] { + public static getShellExecutableArgs(preferences?: IShellTerminalPreferences): string[] { const args = process.env.THEIA_SHELL_ARGS; if (args) { return parseArgs(args); } - if (isOSX) { + if (preferences) { + return preferences.shellArgs[OS.type()]; + } else if (isOSX) { return ['-l']; } else { return []; } - } }