diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 8d870a01b05..05f626a8aa4 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -18,7 +19,23 @@ export interface ICommonLocalPtyService { // TODO: Is executableEnv required? Can the main processes' environment be used instead? executableEnv: IProcessEnvironment, windowsEnableConpty: boolean - ): Promise; + ): Promise; + + start(id: number): Promise; + + shutdown(id: number, immediate: boolean): Promise; + + input(id: number, data: string): Promise; + + resize(id: number, cols: number, rows: number): Promise; + + acknowledgeDataEvent(id: number, charCount: number): Promise; + + getInitialCwd(id: number): Promise; + + getCwd(id: number): Promise; + + getLatency(id: number): Promise; } export interface IShellLaunchConfig { @@ -116,3 +133,98 @@ export interface IShellLaunchConfig { export interface ITerminalEnvironment { [key: string]: string | null; } + +export interface ITerminalLaunchError { + message: string; + code?: number; +} + +/** + * An interface representing a raw terminal child process, this contains a subset of the + * child_process.ChildProcess node.js interface. + */ +export interface ITerminalChildProcess { + onProcessData: Event; + onProcessExit: Event; + onProcessReady: Event<{ pid: number, cwd: string }>; + onProcessTitleChanged: Event; + onProcessOverrideDimensions?: Event; + onProcessResolvedShellLaunchConfig?: Event; + + /** + * Starts the process. + * + * @returns undefined when the process was successfully started, otherwise an object containing + * information on what went wrong. + */ + start(): Promise; + + /** + * Shutdown the terminal process. + * + * @param immediate When true the process will be killed immediately, otherwise the process will + * be given some time to make sure no additional data comes through. + */ + shutdown(immediate: boolean): void; + input(data: string): void; + resize(cols: number, rows: number): void; + + /** + * Acknowledge a data event has been parsed by the terminal, this is used to implement flow + * control to ensure remote processes to not get too far ahead of the client and flood the + * connection. + * @param charCount The number of characters being acknowledged. + */ + acknowledgeDataEvent(charCount: number): void; + + getInitialCwd(): Promise; + getCwd(): Promise; + getLatency(): Promise; +} + +export const enum FlowControlConstants { + /** + * The number of _unacknowledged_ chars to have been sent before the pty is paused in order for + * the client to catch up. + */ + HighWatermarkChars = 100000, + /** + * After flow control pauses the pty for the client the catch up, this is the number of + * _unacknowledged_ chars to have been caught up to on the client before resuming the pty again. + * This is used to attempt to prevent pauses in the flowing data; ideally while the pty is + * paused the number of unacknowledged chars would always be greater than 0 or the client will + * appear to stutter. In reality this balance is hard to accomplish though so heavy commands + * will likely pause as latency grows, not flooding the connection is the important thing as + * it's shared with other core functionality. + */ + LowWatermarkChars = 5000, + /** + * The number characters that are accumulated on the client side before sending an ack event. + * This must be less than or equal to LowWatermarkChars or the terminal max never unpause. + */ + CharCountAckSize = 5000 +} + +export interface IProcessDataEvent { + data: string; + sync: boolean; +} + +export interface ITerminalDimensions { + /** + * The columns of the terminal. + */ + readonly cols: number; + + /** + * The rows of the terminal. + */ + readonly rows: number; +} + +export interface ITerminalDimensionsOverride extends ITerminalDimensions { + /** + * indicate that xterm must receive these exact dimensions, even if they overflow the ui! + */ + forceExactSize?: boolean; +} diff --git a/src/vs/platform/terminal/electron-main/localPtyMainService.ts b/src/vs/platform/terminal/electron-main/localPtyMainService.ts index 8f0bde38eea..ea4bab69b9e 100644 --- a/src/vs/platform/terminal/electron-main/localPtyMainService.ts +++ b/src/vs/platform/terminal/electron-main/localPtyMainService.ts @@ -5,16 +5,74 @@ import { IProcessEnvironment } from 'vs/base/common/platform'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ICommonLocalPtyService, IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ICommonLocalPtyService, IShellLaunchConfig, ITerminalChildProcess, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; +import { TerminalProcess } from 'vs/platform/terminal/node/terminalProcess'; export const ILocalPtyMainService = createDecorator('localPtyMainService'); export interface ILocalPtyMainService extends ICommonLocalPtyService { } +let currentLocalPtyId = 0; + export class LocalPtyMainService implements ICommonLocalPtyService { declare readonly _serviceBrand: undefined; - async createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean): Promise { + private readonly _localPtys: Map = new Map(); + + constructor( + @ILogService private readonly _logService: ILogService + ) { + } + + async createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean): Promise { console.log('PtyMainService#test', cwd, cols, rows); + const process = new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, windowsEnableConpty, this._logService); + console.log('created process'); + process.onProcessData((d) => console.log('data: ' + d)); + process.onProcessExit(e => console.log('exit: ' + e)); + const id = ++currentLocalPtyId; + this._localPtys.set(id, process); + return id; + } + + async start(id: number): Promise { + return this._throwIfNoPty(id).start(); + } + + async shutdown(id: number, immediate: boolean): Promise { + return this._throwIfNoPty(id).shutdown(immediate); + } + + async input(id: number, data: string): Promise { + return this._throwIfNoPty(id).input(data); + } + + async resize(id: number, cols: number, rows: number): Promise { + return this._throwIfNoPty(id).resize(cols, rows); + } + + async acknowledgeDataEvent(id: number, charCount: number): Promise { + return this._throwIfNoPty(id).acknowledgeDataEvent(charCount); + } + + async getInitialCwd(id: number): Promise { + return this._throwIfNoPty(id).getInitialCwd(); + } + + async getCwd(id: number): Promise { + return this._throwIfNoPty(id).getCwd(); + } + + async getLatency(id: number): Promise { + return this._throwIfNoPty(id).getLatency(); + } + + private _throwIfNoPty(id: number): ITerminalChildProcess { + const pty = this._localPtys.get(id); + if (!pty) { + throw new Error(`Could not find pty with id "${id}"`); + } + return pty; } } diff --git a/src/vs/platform/terminal/node/terminalEnvironment.ts b/src/vs/platform/terminal/node/terminalEnvironment.ts new file mode 100644 index 00000000000..d5f83c85257 --- /dev/null +++ b/src/vs/platform/terminal/node/terminalEnvironment.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as os from 'os'; +import * as path from 'vs/base/common/path'; +import { exists } from 'vs/base/node/pfs'; +import { isString } from 'vs/base/common/types'; +import { getCaseInsensitive } from 'vs/base/common/objects'; +import { IProcessEnvironment, isWindows } from 'vs/base/common/platform'; + +export function getWindowsBuildNumber(): number { + const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); + let buildNumber: number = 0; + if (osVersion && osVersion.length === 4) { + buildNumber = parseInt(osVersion[3]); + } + return buildNumber; +} + +export async function findExecutable(command: string, cwd?: string, paths?: string[], env: IProcessEnvironment = process.env as IProcessEnvironment): Promise { + // If we have an absolute path then we take it. + if (path.isAbsolute(command)) { + return await exists(command) ? command : undefined; + } + if (cwd === undefined) { + cwd = process.cwd(); + } + const dir = path.dirname(command); + if (dir !== '.') { + // We have a directory and the directory is relative (see above). Make the path absolute + // to the current working directory. + const fullPath = path.join(cwd, command); + return await exists(fullPath) ? fullPath : undefined; + } + const envPath = getCaseInsensitive(env, 'PATH'); + if (paths === undefined && isString(envPath)) { + paths = envPath.split(path.delimiter); + } + // No PATH environment. Make path absolute to the cwd. + if (paths === undefined || paths.length === 0) { + const fullPath = path.join(cwd, command); + return await exists(fullPath) ? fullPath : undefined; + } + // We have a simple file name. We get the path variable from the env + // and try to find the executable on the path. + for (let pathEntry of paths) { + // The path entry is absolute. + let fullPath: string; + if (path.isAbsolute(pathEntry)) { + fullPath = path.join(pathEntry, command); + } else { + fullPath = path.join(cwd, pathEntry, command); + } + + if (await exists(fullPath)) { + return fullPath; + } + if (isWindows) { + let withExtension = fullPath + '.com'; + if (await exists(withExtension)) { + return withExtension; + } + withExtension = fullPath + '.exe'; + if (await exists(withExtension)) { + return withExtension; + } + } + } + const fullPath = path.join(cwd, command); + return await exists(fullPath) ? fullPath : undefined; +} diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts similarity index 97% rename from src/vs/workbench/contrib/terminal/node/terminalProcess.ts rename to src/vs/platform/terminal/node/terminalProcess.ts index 120aa293e80..4f6727ce502 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -9,13 +9,11 @@ import type * as pty from 'node-pty'; import * as fs from 'fs'; import * as os from 'os'; import { Event, Emitter } from 'vs/base/common/event'; -import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, FlowControlConstants } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalLaunchError, FlowControlConstants, ITerminalChildProcess, ITerminalDimensionsOverride } from 'vs/platform/terminal/common/terminal'; import { exec } from 'child_process'; import { ILogService } from 'vs/platform/log/common/log'; -import { findExecutable } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 7a70a997ed3..bba54eda321 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; @@ -17,7 +17,7 @@ import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalDimensions } from 'vs/platform/terminal/common/terminal'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) export class MainThreadTerminalService implements MainThreadTerminalServiceShape { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9cb6d0c8356..aacef8da2d2 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -41,7 +41,6 @@ import * as tasks from 'vs/workbench/api/common/shared/tasks'; import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views'; import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug'; import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder'; -import { ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { ActivationKind, ExtensionActivationError, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as search from 'vs/workbench/services/search/common/search'; @@ -61,7 +60,7 @@ import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensi import { InternalTestItem, InternalTestResults, RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index babcce145eb..393fdb64cfb 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -9,7 +9,7 @@ import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShap import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { ITerminalChildProcess, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalChildProcess, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/platform/terminal/common/terminal'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 2b5ea0209cb..87ea9940af0 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -11,8 +11,7 @@ import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/termi import { IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfiguration, ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { ILogService } from 'vs/platform/log/common/log'; -import { ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; -import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; +import { TerminalProcess } from 'vs/platform/terminal/node/terminalProcess'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; @@ -26,7 +25,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData import { withNullAsUndefined } from 'vs/base/common/types'; import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell'; import { generateUuid } from 'vs/base/common/uuid'; -import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; export class ExtHostTerminalService extends BaseExtHostTerminalService { diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts index c1cc383b9d9..0fe3cfeafd2 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts @@ -14,9 +14,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILogService } from 'vs/platform/log/common/log'; import { IRemoteTerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteTerminalProcessExecCommandEvent, IShellLaunchConfigDto, RemoteTerminalChannelClient, REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; -import { IProcessDataEvent, IRemoteTerminalAttachTarget, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/workbench/contrib/terminal/common/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; export class RemoteTerminalService extends Disposable implements IRemoteTerminalService { public _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 5a1a765222f..723ec9aef72 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -7,14 +7,14 @@ import type { Terminal as XTermTerminal } from 'xterm'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; -import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError, ITerminalNativeWindowsDelegate, LinuxDistro, IRemoteTerminalAttachTarget, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ITerminalTabLayoutInfoById } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IWindowsShellHelper, ITerminalConfigHelper, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalNativeWindowsDelegate, LinuxDistro, IRemoteTerminalAttachTarget, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ITerminalTabLayoutInfoById } from 'vs/workbench/contrib/terminal/common/terminal'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, Platform } from 'vs/base/common/platform'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { URI } from 'vs/base/common/uri'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; export const ITerminalService = createDecorator('terminalService'); export const ITerminalInstanceService = createDecorator('terminalInstanceService'); @@ -36,7 +36,7 @@ export interface ITerminalInstanceService { getXtermUnicode11Constructor(): Promise; getXtermWebglConstructor(): Promise; createWindowsShellHelper(shellProcessId: number, xterm: XTermTerminal): IWindowsShellHelper; - createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess; + createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): Promise; getDefaultShellAndArgs(useAutomationShell: boolean, platformOverride?: Platform): Promise<{ shell: string, args: string[] | string | undefined }>; getMainProcessParentEnv(): Promise; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index c825519e605..40c4a098459 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -25,7 +25,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride, TERMINAL_CREATION_COMMANDS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, TERMINAL_CREATION_COMMANDS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; @@ -47,7 +47,7 @@ import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/ import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index daefe15f7ef..5959fa80cb5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IWindowsShellHelper, ITerminalChildProcess, IDefaultShellAndArgsRequest } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IWindowsShellHelper, IDefaultShellAndArgsRequest } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal as XTermTerminal } from 'xterm'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; @@ -12,6 +12,7 @@ import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ITerminalChildProcess } from 'vs/platform/terminal/common/terminal'; let Terminal: typeof XTermTerminal; let SearchAddon: typeof XTermSearchAddon; @@ -56,7 +57,7 @@ export class TerminalInstanceService implements ITerminalInstanceService { throw new Error('Not implemented'); } - public createTerminalProcess(): ITerminalChildProcess { + public createTerminalProcess(): Promise { throw new Error('Not implemented'); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts index 6fd7e1276a3..ce47964cff8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalProcessExtHostProxy, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessExtHostProxy, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import * as nls from 'vs/nls'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalLaunchError, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalDimensions } from 'vs/platform/terminal/common/terminal'; let hasReceivedResponseFromRemoteExtHost: boolean = false; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index a524d40aca2..7396cd4897e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -6,7 +6,7 @@ import * as platform from 'vs/base/common/platform'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { env as processEnv } from 'vs/base/common/process'; -import { ProcessState, ITerminalProcessManager, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride, FlowControlConstants } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ProcessState, ITerminalProcessManager, ITerminalConfigHelper, IBeforeProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal'; import { ILogService } from 'vs/platform/log/common/log'; import { Emitter, Event } from 'vs/base/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; @@ -27,7 +27,7 @@ import { IEnvironmentVariableService, IMergedEnvironmentVariableCollection, IEnv import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } from 'vs/workbench/contrib/terminal/browser/environmentVariableInfo'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { URI } from 'vs/base/common/uri'; -import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, ITerminalChildProcess, IProcessDataEvent, ITerminalDimensionsOverride } from 'vs/platform/terminal/common/terminal'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -311,7 +311,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const env = await this._setupEnvVariableInfo(activeWorkspaceRootUri, shellLaunchConfig); const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled; - return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); + return await this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); } public setDimensions(cols: number, rows: number): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 560e713eb9b..3c83a0f4962 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -18,14 +18,14 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; import { TerminalConnectionState, IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalTab, TerminalShellType, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; -import { IAvailableShellsRequest, IRemoteTerminalAttachTarget, IShellDefinition, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalLaunchError, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, ITerminalsLayoutInfoById, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, LinuxDistro, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IAvailableShellsRequest, IRemoteTerminalAttachTarget, IShellDefinition, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, ITerminalsLayoutInfoById, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, LinuxDistro, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts index 830bec5bf8c..9358cbf6d6f 100644 --- a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts @@ -13,13 +13,13 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; -import { IRawTerminalTabLayoutInfo, ITerminalConfiguration, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ITerminalTabLayoutInfoById, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IRawTerminalTabLayoutInfo, ITerminalConfiguration, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ITerminalTabLayoutInfoById, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { SideBySideEditor, EditorResourceAccessor } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Schemas } from 'vs/base/common/network'; import { ILabelService } from 'vs/platform/label/common/label'; -import { ITerminalEnvironment } from 'vs/platform/terminal/common/terminal'; +import { ITerminalEnvironment, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; export const REMOTE_TERMINAL_CHANNEL_NAME = 'remoteterminal'; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 066986885dd..43d511e01c5 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { OperatingSystem } from 'vs/base/common/platform'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensions, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; export const TERMINAL_VIEW_ID = 'terminal'; @@ -223,25 +223,6 @@ export interface IShellDefinition { path: string; } -export interface ITerminalDimensions { - /** - * The columns of the terminal. - */ - readonly cols: number; - - /** - * The rows of the terminal. - */ - readonly rows: number; -} - -export interface ITerminalDimensionsOverride extends ITerminalDimensions { - /** - * indicate that xterm must receive these exact dimensions, even if they overflow the ui! - */ - forceExactSize?: boolean; -} - export interface ICommandTracker { scrollToPreviousCommand(): void; scrollToNextCommand(): void; @@ -265,11 +246,6 @@ export interface IBeforeProcessDataEvent { data: string; } -export interface IProcessDataEvent { - data: string; - sync: boolean; -} - export interface ITerminalProcessManager extends IDisposable { readonly processState: ProcessState; readonly ptyProcessReady: Promise; @@ -390,77 +366,6 @@ export interface IWindowsShellHelper extends IDisposable { getShellName(): Promise; } -export interface ITerminalLaunchError { - message: string; - code?: number; -} - -/** - * An interface representing a raw terminal child process, this contains a subset of the - * child_process.ChildProcess node.js interface. - */ -export interface ITerminalChildProcess { - onProcessData: Event; - onProcessExit: Event; - onProcessReady: Event<{ pid: number, cwd: string }>; - onProcessTitleChanged: Event; - onProcessOverrideDimensions?: Event; - onProcessResolvedShellLaunchConfig?: Event; - - /** - * Starts the process. - * - * @returns undefined when the process was successfully started, otherwise an object containing - * information on what went wrong. - */ - start(): Promise; - - /** - * Shutdown the terminal process. - * - * @param immediate When true the process will be killed immediately, otherwise the process will - * be given some time to make sure no additional data comes through. - */ - shutdown(immediate: boolean): void; - input(data: string): void; - resize(cols: number, rows: number): void; - - /** - * Acknowledge a data event has been parsed by the terminal, this is used to implement flow - * control to ensure remote processes to not get too far ahead of the client and flood the - * connection. - * @param charCount The number of characters being acknowledged. - */ - acknowledgeDataEvent(charCount: number): void; - - getInitialCwd(): Promise; - getCwd(): Promise; - getLatency(): Promise; -} - -export const enum FlowControlConstants { - /** - * The number of _unacknowledged_ chars to have been sent before the pty is paused in order for - * the client to catch up. - */ - HighWatermarkChars = 100000, - /** - * After flow control pauses the pty for the client the catch up, this is the number of - * _unacknowledged_ chars to have been caught up to on the client before resuming the pty again. - * This is used to attempt to prevent pauses in the flowing data; ideally while the pty is - * paused the number of unacknowledged chars would always be greater than 0 or the client will - * appear to stutter. In reality this balance is hard to accomplish though so heavy commands - * will likely pause as latency grows, not flooding the connection is the important thing as - * it's shared with other core functionality. - */ - LowWatermarkChars = 5000, - /** - * The number characters that are accumulated on the client side before sending an ack event. - * This must be less than or equal to LowWatermarkChars or the terminal max never unpause. - */ - CharCountAckSize = 5000 -} - export const enum TERMINAL_COMMAND_ID { FIND_NEXT = 'workbench.action.terminal.findNext', FIND_PREVIOUS = 'workbench.action.terminal.findPrevious', diff --git a/src/vs/workbench/contrib/terminal/common/terminalDataBuffering.ts b/src/vs/workbench/contrib/terminal/common/terminalDataBuffering.ts index db8c7f57d9b..ff1e611b93c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalDataBuffering.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalDataBuffering.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IProcessDataEvent } from 'vs/platform/terminal/common/terminal'; interface TerminalDataBuffer extends IDisposable { data: string[]; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index cffc80a93ba..9278da9469b 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -4,11 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IWindowsShellHelper, ITerminalChildProcess, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IWindowsShellHelper, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY } from 'vs/workbench/contrib/terminal/common/terminal'; import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/electron-browser/windowsShellHelper'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, platform, Platform } from 'vs/base/common/platform'; -import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import type { Terminal as XTermTerminal } from 'xterm'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; @@ -23,7 +22,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { ILogService } from 'vs/platform/log/common/log'; import { getSystemShell } from 'vs/base/node/shell'; import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; -import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalChildProcess } from 'vs/platform/terminal/common/terminal'; +import { TerminalProcessMainProxy } from 'vs/workbench/contrib/terminal/electron-browser/terminalProcessMainProxy'; let Terminal: typeof XTermTerminal; let SearchAddon: typeof XTermSearchAddon; @@ -77,9 +77,11 @@ export class TerminalInstanceService implements ITerminalInstanceService { return new WindowsShellHelper(shellProcessId, xterm); } - public createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess { - this._localPtyService.createProcess(shellLaunchConfig, cwd, cols, rows, env, process.env as IProcessEnvironment, windowsEnableConpty); - return this._instantiationService.createInstance(TerminalProcess, shellLaunchConfig, cwd, cols, rows, env, process.env as IProcessEnvironment, windowsEnableConpty); + public async createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): Promise { + const id = await this._localPtyService.createProcess(shellLaunchConfig, cwd, cols, rows, env, process.env as IProcessEnvironment, windowsEnableConpty); + console.log('local pty id ' + id); + return this._instantiationService.createInstance(TerminalProcessMainProxy, id); + // return this._instantiationService.createInstance(TerminalProcess, shellLaunchConfig, cwd, cols, rows, env, process.env as IProcessEnvironment, windowsEnableConpty); } private _isWorkspaceShellAllowed(): boolean { diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts index 91a52764186..9e67808966f 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts @@ -7,7 +7,7 @@ import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { INativeOpenFileRequest } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; -import { getWindowsBuildNumber, linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; +import { linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { execFile } from 'child_process'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -17,6 +17,7 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; export class TerminalNativeContribution extends Disposable implements IWorkbenchContribution { public _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessMainProxy.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessMainProxy.ts new file mode 100644 index 00000000000..825e6538d87 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessMainProxy.ts @@ -0,0 +1,62 @@ +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IShellLaunchConfig, ITerminalLaunchError, ITerminalChildProcess, ITerminalDimensionsOverride } from 'vs/platform/terminal/common/terminal'; +import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; + +/** + * Responsible for establishing and maintaining a connection with an existing terminal process + * created from the main process. + */ +export class TerminalProcessMainProxy extends Disposable implements ITerminalChildProcess { + private readonly _onProcessData = this._register(new Emitter()); + public readonly onProcessData: Event = this._onProcessData.event; + private readonly _onProcessExit = this._register(new Emitter()); + public readonly onProcessExit: Event = this._onProcessExit.event; + private readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>()); + public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } + private readonly _onProcessTitleChanged = this._register(new Emitter()); + public readonly onProcessTitleChanged: Event = this._onProcessTitleChanged.event; + private readonly _onProcessOverrideDimensions = this._register(new Emitter()); + public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } + private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter()); + public get onProcessResolvedShellLaunchConfig(): Event { return this._onProcessResolvedShellLaunchConfig.event; } + + constructor( + private readonly _localPtyId: number, + @ILocalPtyService private readonly _localPtyService: ILocalPtyService + ) { + super(); + } + + start(): Promise { + return this._localPtyService.start(this._localPtyId); + } + + shutdown(immediate: boolean): void { + this._localPtyService.shutdown(this._localPtyId, immediate); + } + + input(data: string): void { + this._localPtyService.input(this._localPtyId, data); + } + + resize(cols: number, rows: number): void { + this._localPtyService.resize(this._localPtyId, cols, rows); + } + + acknowledgeDataEvent(charCount: number): void { + this._localPtyService.acknowledgeDataEvent(this._localPtyId, charCount); + } + + getInitialCwd(): Promise { + return this._localPtyService.getInitialCwd(this._localPtyId); + } + + getCwd(): Promise { + return this._localPtyService.getCwd(this._localPtyId); + } + + getLatency(): Promise { + return this._localPtyService.getLatency(this._localPtyId); + } +} diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index b949c038eac..11e429a69d6 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as os from 'os'; import * as fs from 'fs'; import * as platform from 'vs/base/common/platform'; import { SymlinkSupport } from 'vs/base/node/pfs'; @@ -11,6 +10,7 @@ import { LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/com import { coalesce } from 'vs/base/common/arrays'; import { normalize, basename } from 'vs/base/common/path'; import { enumeratePowerShellInstallations } from 'vs/base/node/powershell'; +import { getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; let detectedDistro = LinuxDistro.Unknown; if (platform.isLinux) { @@ -31,15 +31,6 @@ if (platform.isLinux) { export const linuxDistro = detectedDistro; -export function getWindowsBuildNumber(): number { - const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); - let buildNumber: number = 0; - if (osVersion && osVersion.length === 4) { - buildNumber = parseInt(osVersion[3]); - } - return buildNumber; -} - export function detectAvailableShells(): Promise { return platform.isWindows ? detectAvailableWindowsShells() : detectAvailableUnixShells(); } diff --git a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts index 9a4c932546a..2a3821ab7e9 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts @@ -6,9 +6,6 @@ import * as fs from 'fs'; import * as path from 'vs/base/common/path'; import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { exists } from 'vs/base/node/pfs'; -import { isString } from 'vs/base/common/types'; -import { getCaseInsensitive } from 'vs/base/common/objects'; let mainProcessParentEnv: IProcessEnvironment | undefined; @@ -80,56 +77,3 @@ export async function getMainProcessParentEnv(baseEnvironment: IProcessEnvironme return mainProcessParentEnv!; } - -export async function findExecutable(command: string, cwd?: string, paths?: string[], env: IProcessEnvironment = process.env as IProcessEnvironment): Promise { - // If we have an absolute path then we take it. - if (path.isAbsolute(command)) { - return await exists(command) ? command : undefined; - } - if (cwd === undefined) { - cwd = process.cwd(); - } - const dir = path.dirname(command); - if (dir !== '.') { - // We have a directory and the directory is relative (see above). Make the path absolute - // to the current working directory. - const fullPath = path.join(cwd, command); - return await exists(fullPath) ? fullPath : undefined; - } - const envPath = getCaseInsensitive(env, 'PATH'); - if (paths === undefined && isString(envPath)) { - paths = envPath.split(path.delimiter); - } - // No PATH environment. Make path absolute to the cwd. - if (paths === undefined || paths.length === 0) { - const fullPath = path.join(cwd, command); - return await exists(fullPath) ? fullPath : undefined; - } - // We have a simple file name. We get the path variable from the env - // and try to find the executable on the path. - for (let pathEntry of paths) { - // The path entry is absolute. - let fullPath: string; - if (path.isAbsolute(pathEntry)) { - fullPath = path.join(pathEntry, command); - } else { - fullPath = path.join(cwd, pathEntry, command); - } - - if (await exists(fullPath)) { - return fullPath; - } - if (isWindows) { - let withExtension = fullPath + '.com'; - if (await exists(withExtension)) { - return withExtension; - } - withExtension = fullPath + '.exe'; - if (await exists(withExtension)) { - return withExtension; - } - } - } - const fullPath = path.join(cwd, command); - return await exists(fullPath) ? fullPath : undefined; -}