Skip to content

Commit

Permalink
Merge pull request #129207 from microsoft/tyriar/116113
Browse files Browse the repository at this point in the history
xterm.js node target and serialize addon
  • Loading branch information
Tyriar authored Aug 12, 2021
2 parents 699084f + d22c9a0 commit 3bec131
Show file tree
Hide file tree
Showing 14 changed files with 552 additions and 34 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.60.0",
"distro": "8a46e44036ace8089e2e1bc6c1af11dad15825b1",
"distro": "20426c9ab21f26752e1640818c3a5d148863c78e",
"author": {
"name": "Microsoft Corporation"
},
Expand Down Expand Up @@ -88,6 +88,7 @@
"xterm-addon-search": "0.9.0-beta.3",
"xterm-addon-unicode11": "0.3.0-beta.5",
"xterm-addon-webgl": "0.12.0-beta.5",
"xterm-headless": "4.14.0-beta.3",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
Expand Down
1 change: 1 addition & 0 deletions remote/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"xterm-addon-search": "0.9.0-beta.3",
"xterm-addon-unicode11": "0.3.0-beta.5",
"xterm-addon-webgl": "0.12.0-beta.5",
"xterm-headless": "4.14.0-beta.3",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
Expand Down
5 changes: 5 additions & 0 deletions remote/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,11 @@ xterm-addon-webgl@0.12.0-beta.5:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.5.tgz#97524d1a455d15096c1999b8ce73ab0c6ecc8efa"
integrity sha512-Od9yFikC414E4Fm1W8akhSiNDUWat+RLsEYJMwvcEW4HprUVJije6n2Lrzj4roNl4pnSPUhF89zG6NY4lZXqDg==

xterm-headless@4.14.0-beta.3:
version "4.14.0-beta.3"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.14.0-beta.3.tgz#c737c1c700cc3dd6207294d9c233acf1676970da"
integrity sha512-DVzUVTJBHQ906p08fsLkVyWA+UC7faKaudgjmGpMOcJjQTXNGmcQQhpoUq8mYCukD4ONjmJ9UOq5snBTASR+mw==

xterm@4.14.0-beta.6:
version "4.14.0-beta.6"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.14.0-beta.6.tgz#f39ec3298059dbfe09f8f5429ad55bc5472fdc4a"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'
import { combinedAppender, ITelemetryAppender, NullAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
import { CustomEndpointTelemetryService } from 'vs/platform/telemetry/node/customEndpointTelemetryService';
import { LocalReconnectConstants, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService';
import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync';
Expand Down Expand Up @@ -276,8 +276,10 @@ class SharedProcessMain extends Disposable {
ILocalPtyService,
this._register(
new PtyHostService({
GraceTime: LocalReconnectConstants.GraceTime,
ShortGraceTime: LocalReconnectConstants.ShortGraceTime
graceTime: LocalReconnectConstants.GraceTime,
shortGraceTime: LocalReconnectConstants.ShortGraceTime,
scrollback: configurationService.getValue<number>(TerminalSettingId.PersistentSessionScrollback) || 1,
useExperimentalSerialization: !!configurationService.getValue<boolean>(TerminalSettingId.PersistentSessionExperimentalSerializer),
},
configurationService,
logService,
Expand Down
8 changes: 6 additions & 2 deletions src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export const enum TerminalSettingId {
LocalEchoExcludePrograms = 'terminal.integrated.localEchoExcludePrograms',
LocalEchoStyle = 'terminal.integrated.localEchoStyle',
EnablePersistentSessions = 'terminal.integrated.enablePersistentSessions',
PersistentSessionScrollback = 'terminal.integrated.persistentSessionScrollback',
PersistentSessionExperimentalSerializer = 'terminal.integrated.persistentSessionExperimentalSerializer',
InheritEnv = 'terminal.integrated.inheritEnv',
ShowLinkHover = 'terminal.integrated.showLinkHover',
}
Expand Down Expand Up @@ -489,8 +491,10 @@ export interface ITerminalChildProcess {
}

export interface IReconnectConstants {
GraceTime: number,
ShortGraceTime: number
graceTime: number;
shortGraceTime: number;
scrollback: number;
useExperimentalSerialization: boolean;
}

export const enum LocalReconnectConstants {
Expand Down
12 changes: 12 additions & 0 deletions src/vs/platform/terminal/common/terminalPlatformConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,18 @@ const terminalPlatformConfiguration: IConfigurationNode = {
type: 'boolean',
default: true
},
[TerminalSettingId.PersistentSessionScrollback]: {
scope: ConfigurationScope.APPLICATION,
markdownDescription: localize('terminal.integrated.persistentSessionScrollback', "Controls the maximum amount of lines that will be restored when reconnecting to a persistent terminal session. Increasing this will restore more lines of scrollback at the cost of more memory and increase the time it takes to connect to terminals on start up. This setting requires a restart to take effect and should be set to a value less than or equal to `#terminal.integrated.scrollback#`."),
type: 'number',
default: 100
},
[TerminalSettingId.PersistentSessionExperimentalSerializer]: {
scope: ConfigurationScope.APPLICATION,
description: localize('terminal.integrated.persistentSessionExperimentalSerializer', "Whether to use a more efficient experimental approach for restoring the terminal's buffer. This setting requires a restart to take effect."),
type: 'boolean',
default: true
},
[TerminalSettingId.ShowLinkHover]: {
scope: ConfigurationScope.APPLICATION,
description: localize('terminal.integrated.showLinkHover', "Whether to show hovers for links in the terminal output."),
Expand Down
12 changes: 9 additions & 3 deletions src/vs/platform/terminal/common/terminalRecorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ export interface IRemoteTerminalProcessReplayEvent {
events: ReplayEntry[];
}

export class TerminalRecorder {
export interface ITerminalSerializer {
handleData(data: string): void;
handleResize(cols: number, rows: number): void;
generateReplayEvent(): IPtyHostProcessReplayEvent;
}

export class TerminalRecorder implements ITerminalSerializer {

private _entries: RecorderEntry[];
private _totalDataLength: number = 0;
Expand All @@ -26,7 +32,7 @@ export class TerminalRecorder {
this._entries = [{ cols, rows, data: [] }];
}

recordResize(cols: number, rows: number): void {
handleResize(cols: number, rows: number): void {
if (this._entries.length > 0) {
const lastEntry = this._entries[this._entries.length - 1];
if (lastEntry.data.length === 0) {
Expand All @@ -52,7 +58,7 @@ export class TerminalRecorder {
this._entries.push({ cols, rows, data: [] });
}

recordData(data: string): void {
handleData(data: string): void {
const lastEntry = this._entries[this._entries.length - 1];
lastEntry.data.push(data);

Expand Down
11 changes: 9 additions & 2 deletions src/vs/platform/terminal/node/ptyHostMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { ConsoleLogger, LogService } from 'vs/platform/log/common/log';
import { LogLevelChannel } from 'vs/platform/log/common/logIpc';
import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { IReconnectConstants, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { HeartbeatService } from 'vs/platform/terminal/node/heartbeatService';
import { PtyService } from 'vs/platform/terminal/node/ptyService';

Expand All @@ -23,9 +23,16 @@ server.registerChannel(TerminalIpcChannels.Log, logChannel);
const heartbeatService = new HeartbeatService();
server.registerChannel(TerminalIpcChannels.Heartbeat, ProxyChannel.fromService(heartbeatService));

const reconnectConstants = { GraceTime: parseInt(process.env.VSCODE_RECONNECT_GRACE_TIME || '0'), ShortGraceTime: parseInt(process.env.VSCODE_RECONNECT_SHORT_GRACE_TIME || '0') };
const reconnectConstants: IReconnectConstants = {
graceTime: parseInt(process.env.VSCODE_RECONNECT_GRACE_TIME || '0'),
shortGraceTime: parseInt(process.env.VSCODE_RECONNECT_SHORT_GRACE_TIME || '0'),
scrollback: parseInt(process.env.VSCODE_RECONNECT_SCROLLBACK || '100'),
useExperimentalSerialization: !!parseInt(process.env.VSCODE_RECONNECT_EXPERIMENTAL_SERIALIZATION || '1')
};
delete process.env.VSCODE_RECONNECT_GRACE_TIME;
delete process.env.VSCODE_RECONNECT_SHORT_GRACE_TIME;
delete process.env.VSCODE_RECONNECT_SCROLLBACK;
delete process.env.VSCODE_RECONNECT_EXPERIMENTAL_SERIALIZATION;

const ptyService = new PtyService(lastPtyId, logService, reconnectConstants);
server.registerChannel(TerminalIpcChannels.PtyHost, ProxyChannel.fromService(ptyService));
Expand Down
7 changes: 5 additions & 2 deletions src/vs/platform/terminal/node/ptyHostService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export class PtyHostService extends Disposable implements IPtyService {
}

private _startPtyHost(): [Client, IPtyService] {
this._logService.info('use experimental serialize ' + (this._reconnectConstants.useExperimentalSerialization ? 1 : 0));
const client = new Client(
FileAccess.asFileUri('bootstrap-fork', require).fsPath,
{
Expand All @@ -117,8 +118,10 @@ export class PtyHostService extends Disposable implements IPtyService {
VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain',
VSCODE_PIPE_LOGGING: 'true',
VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client,
VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.GraceTime,
VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.ShortGraceTime
VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.graceTime,
VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.shortGraceTime,
VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback,
VSCODE_RECONNECT_EXPERIMENTAL_SERIALIZATION: this._reconnectConstants.useExperimentalSerialization ? 1 : 0
}
}
);
Expand Down
65 changes: 53 additions & 12 deletions src/vs/platform/terminal/node/ptyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import { RequestStore } from 'vs/platform/terminal/common/requestStore';
import { IProcessDataEvent, IProcessReadyEvent, IPtyService, IRawTerminalInstanceLayoutInfo, IReconnectConstants, IRequestResolveVariablesEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalInstanceLayoutInfoById, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalTabLayoutInfoById, TerminalIcon, TerminalShellType, TitleEventSource } from 'vs/platform/terminal/common/terminal';
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment';
import { Terminal as XtermTerminal } from 'xterm-headless';
import { SerializeAddon } from 'vs/platform/terminal/node/serializeAddon';
import { IGetTerminalLayoutInfoArgs, IProcessDetails, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs, ITerminalTabLayoutInfoDto } from 'vs/platform/terminal/common/terminalProcess';
import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';
import { ITerminalSerializer, TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';
import { getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment';
import { TerminalProcess } from 'vs/platform/terminal/node/terminalProcess';

Expand Down Expand Up @@ -307,7 +309,6 @@ export class PersistentTerminalProcess extends Disposable {

private readonly _pendingCommands = new Map<number, { resolve: (data: any) => void; reject: (err: any) => void; }>();

private readonly _recorder: TerminalRecorder;
private _isStarted: boolean = false;

private _orphanQuestionBarrier: AutoOpenBarrier | null;
Expand Down Expand Up @@ -337,6 +338,7 @@ export class PersistentTerminalProcess extends Disposable {
private _cwd = '';
private _title: string | undefined;
private _titleSource: TitleEventSource = TitleEventSource.Process;
private _serializer: ITerminalSerializer;

get pid(): number { return this._pid; }
get title(): string { return this._title || this._terminalProcess.currentTitle; }
Expand All @@ -360,25 +362,35 @@ export class PersistentTerminalProcess extends Disposable {
readonly workspaceId: string,
readonly workspaceName: string,
readonly shouldPersistTerminal: boolean,
cols: number, rows: number,
cols: number,
rows: number,
reconnectConstants: IReconnectConstants,
private readonly _logService: ILogService,
private _icon?: TerminalIcon,
private _color?: string
) {
super();
this._logService.trace('persistentTerminalProcess#ctor', _persistentProcessId, arguments);
this._recorder = new TerminalRecorder(cols, rows);

if (reconnectConstants.useExperimentalSerialization) {
this._serializer = new XtermSerializer(
cols,
rows,
reconnectConstants.scrollback
);
} else {
this._serializer = new TerminalRecorder(cols, rows);
}
this._orphanQuestionBarrier = null;
this._orphanQuestionReplyTime = 0;
this._disconnectRunner1 = this._register(new RunOnceScheduler(() => {
this._logService.info(`Persistent process "${this._persistentProcessId}": The reconnection grace time of ${printTime(reconnectConstants.GraceTime)} has expired, shutting down pid "${this._pid}"`);
this._logService.info(`Persistent process "${this._persistentProcessId}": The reconnection grace time of ${printTime(reconnectConstants.graceTime)} has expired, shutting down pid "${this._pid}"`);
this.shutdown(true);
}, reconnectConstants.GraceTime));
}, reconnectConstants.graceTime));
this._disconnectRunner2 = this._register(new RunOnceScheduler(() => {
this._logService.info(`Persistent process "${this._persistentProcessId}": The short reconnection grace time of ${printTime(reconnectConstants.ShortGraceTime)} has expired, shutting down pid ${this._pid}`);
this._logService.info(`Persistent process "${this._persistentProcessId}": The short reconnection grace time of ${printTime(reconnectConstants.shortGraceTime)} has expired, shutting down pid ${this._pid}`);
this.shutdown(true);
}, reconnectConstants.ShortGraceTime));
}, reconnectConstants.shortGraceTime));

this._register(this._terminalProcess.onProcessReady(e => {
this._pid = e.pid;
Expand All @@ -394,7 +406,7 @@ export class PersistentTerminalProcess extends Disposable {
this._register(this._terminalProcess.onProcessExit(() => this._bufferer.stopBuffering(this._persistentProcessId)));

// Data recording for reconnect
this._register(this.onProcessData(e => this._recorder.recordData(e)));
this._register(this.onProcessData(e => this._serializer.handleData(e)));
}

attach(): void {
Expand Down Expand Up @@ -445,7 +457,7 @@ export class PersistentTerminalProcess extends Disposable {
if (this._inReplay) {
return;
}
this._recorder.recordResize(cols, rows);
this._serializer.handleResize(cols, rows);

// Buffered events should flush when a resize occurs
this._bufferer.flushBuffer(this._persistentProcessId);
Expand All @@ -468,12 +480,11 @@ export class PersistentTerminalProcess extends Disposable {
}

triggerReplay(): void {
const ev = this._recorder.generateReplayEvent();
const ev = this._serializer.generateReplayEvent();
let dataLength = 0;
for (const e of ev.events) {
dataLength += e.data.length;
}

this._logService.info(`Persistent process "${this._persistentProcessId}": Replaying ${dataLength} chars and ${ev.events.length} size events`);
this._onProcessReplay.fire(ev);
this._terminalProcess.clearUnacknowledgedChars();
Expand Down Expand Up @@ -530,6 +541,36 @@ export class PersistentTerminalProcess extends Disposable {
}
}

class XtermSerializer implements ITerminalSerializer {
private _xterm: XtermTerminal;
constructor(cols: number, rows: number, scrollback: number) {
this._xterm = new XtermTerminal({ cols, rows, scrollback });
}

handleData(data: string): void {
this._xterm.write(data);
}

handleResize(cols: number, rows: number): void {
this._xterm.resize(cols, rows);
}

generateReplayEvent(): IPtyHostProcessReplayEvent {
const serialize = new SerializeAddon();
this._xterm.loadAddon(serialize);
const serialized = serialize.serialize(this._xterm.getOption('scrollback'));
return {
events: [
{
cols: this._xterm.getOption('cols'),
rows: this._xterm.getOption('rows'),
data: serialized
}
]
};
}
}

function printTime(ms: number): string {
let h = 0;
let m = 0;
Expand Down
Loading

0 comments on commit 3bec131

Please sign in to comment.