diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 9fec616de5a..696b8648bff 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1256,6 +1256,46 @@ declare module 'vscode' { //#endregion + //#region Terminal virtual process + + // export function createTerminal(options: TerminalOptions | TerminalVirtualProcessOptions): Terminal; + + export interface TerminalVirtualProcessOptions { + // For a name property for TerminalVirtualProcessOptions. + // Note that this is mandatory here as there's no process/shell to grab the title from + name: string; + + virtualProcess: TerminalVirtualProcess; + + // Allows Windows or non-Windows local link handler to be used based on Live Share host OS + // os?: OperatingSystem; + + // Allows ~ to be resolved in Live Share + // userHome?: string; + } + + interface TerminalVirtualProcess { + // The ext should fire this when they want to write to the terminal + write: Event; + + // Lets the extension override the dimensions of the terminal + overrideDimensions?: Event; + + // Lets the extension exit the process with an exit code, this was not in the TerminalRenderer + // API but it makes sense to include this as it's the main thing missing for a virtual process + // to truly act like a process + exit?: Event; + + // This will be called when the user types + onDidAcceptInput?(text: string): void; + + // This is called fire when window.onDidChangeTerminalDimensions fires as CustomExecution need + // access to the "maximum" dimensions and don't want access to Terminal + onDidChangeDimensions?(dimensions: TerminalDimensions): void; + } + + //#endregion + //#region Joh -> exclusive document filters export interface DocumentFilter { diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index b975524b5f6..5659af48a05 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -76,7 +76,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape // when the extension host process goes down ? } - public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string, cwd?: string | UriComponents, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean, hideFromUser?: boolean): Promise<{ id: number, name: string }> { + public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string, cwd?: string | UriComponents, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean, hideFromUser?: boolean, isVirtualProcess?: boolean): Promise<{ id: number, name: string }> { const shellLaunchConfig: IShellLaunchConfig = { name, executable: shellPath, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6b747ffac41..9d872402909 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -390,7 +390,7 @@ export interface MainThreadProgressShape extends IDisposable { } export interface MainThreadTerminalServiceShape extends IDisposable { - $createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string, cwd?: string | UriComponents, env?: { [key: string]: string | null }, waitOnExit?: boolean, strictEnv?: boolean, hideFromUser?: boolean): Promise<{ id: number, name: string }>; + $createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string, cwd?: string | UriComponents, env?: { [key: string]: string | null }, waitOnExit?: boolean, strictEnv?: boolean, hideFromUser?: boolean, isVirtualProcess?: boolean): Promise<{ id: number, name: string }>; $createTerminalRenderer(name: string): Promise; $dispose(terminalId: number): void; $hide(terminalId: number): void; diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 38fa69dc551..3ca4f816f99 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -116,14 +116,19 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi env?: { [key: string]: string | null }, waitOnExit?: boolean, strictEnv?: boolean, - hideFromUser?: boolean + hideFromUser?: boolean, + isVirtualProcess?: boolean ): void { - this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser).then(terminal => { + this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser, isVirtualProcess).then(terminal => { this._name = terminal.name; this._runQueuedRequests(terminal.id); }); } + public createVirtualProcess(): void { + this.create(undefined, undefined, undefined, undefined, undefined, undefined, undefined, true); + } + public get name(): string { return this._name || ''; } @@ -313,7 +318,11 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, options.name); - terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser); + if ((options).isVirtualProcess) { + terminal.createVirtualProcess(); + } else { + terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser); + } this._terminals.push(terminal); return terminal; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 2f3f3600781..1c00b861138 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -103,32 +103,37 @@ export class TerminalProcessManager implements ITerminalProcessManager { rows: number, isScreenReaderModeEnabled: boolean ): Promise { - const forceExtHostProcess = (this._configHelper.config as any).extHostProcess; - if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') { - this.remoteAuthority = getRemoteAuthority(shellLaunchConfig.cwd); + if (shellLaunchConfig.isVirtualProcess) { + // TODO: Hook up proxy + this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, undefined, cols, rows, this._configHelper); } else { - this.remoteAuthority = this._environmentService.configuration.remoteAuthority; - } - const hasRemoteAuthority = !!this.remoteAuthority; - let launchRemotely = hasRemoteAuthority || forceExtHostProcess; - - this.userHome = this._environmentService.userHome; - this.os = platform.OS; - if (launchRemotely) { - if (hasRemoteAuthority) { - this._remoteAgentService.getEnvironment().then(env => { - if (!env) { - return; - } - this.userHome = env.userHome.path; - this.os = env.os; - }); + const forceExtHostProcess = (this._configHelper.config as any).extHostProcess; + if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') { + this.remoteAuthority = getRemoteAuthority(shellLaunchConfig.cwd); + } else { + this.remoteAuthority = this._environmentService.configuration.remoteAuthority; } + const hasRemoteAuthority = !!this.remoteAuthority; + let launchRemotely = hasRemoteAuthority || forceExtHostProcess; - const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); - this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper); - } else { - this._process = await this._launchProcess(shellLaunchConfig, cols, rows, isScreenReaderModeEnabled); + this.userHome = this._environmentService.userHome; + this.os = platform.OS; + if (launchRemotely) { + if (hasRemoteAuthority) { + this._remoteAgentService.getEnvironment().then(env => { + if (!env) { + return; + } + this.userHome = env.userHome.path; + this.os = env.os; + }); + } + + const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); + this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper); + } else { + this._process = await this._launchProcess(shellLaunchConfig, cols, rows, isScreenReaderModeEnabled); + } } this.processState = ProcessState.LAUNCHING; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 1880511c68b..91fd6e142eb 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -186,9 +186,12 @@ export interface IShellLaunchConfig { /** * When true the terminal will be created with no process. This is primarily used to give * extensions full control over the terminal. + * @deprecated use `isVirtualProcess` */ isRendererOnly?: boolean; + isVirtualProcess?: boolean; + /** * Whether the terminal process environment should be exactly as provided in * `TerminalOptions.env`. When this is false (default), the environment will be based on the