@@ -1466,10 +1466,10 @@ index 0000000000000000000000000000000000000000..6ce56bec114a6d8daf5dd3ded945ea78
14661466+ }
14671467diff --git a/src/vs/server/node/channel.ts b/src/vs/server/node/channel.ts
14681468new file mode 100644
1469- index 0000000000000000000000000000000000000000..91a932b613c473cd13dfddbde2942aeebf4bb84c
1469+ index 0000000000000000000000000000000000000000..cb3a45fda10a6bcbff73275b5734641b3319cc9b
14701470--- /dev/null
14711471+++ b/src/vs/server/node/channel.ts
1472- @@ -0,0 +1,780 @@
1472+ @@ -0,0 +1,828 @@
14731473+ import { field, logger } from '@coder/logger';
14741474+ import { Server } from '@coder/node-browser';
14751475+ import * as os from 'os';
@@ -1884,6 +1884,14 @@ index 0000000000000000000000000000000000000000..91a932b613c473cd13dfddbde2942aee
18841884+ private readonly _onDispose = new Emitter<void>();
18851885+ public get onDispose(): Event<void> { return this._onDispose.event; }
18861886+
1887+ + // These are replayed when a client reconnects.
1888+ + private cols: number;
1889+ + private rows: number;
1890+ + private replayData: string[] = [];
1891+ + // This is based on string length and is pretty arbitrary.
1892+ + private readonly maxReplayData = 10000;
1893+ + private totalReplayData = 0;
1894+ +
18871895+ private buffering = false;
18881896+ private readonly _onEvent = new Emitter<terminal.IRemoteTerminalProcessEvent>({
18891897+ // Don't bind to data until something is listening.
@@ -1894,6 +1902,23 @@ index 0000000000000000000000000000000000000000..91a932b613c473cd13dfddbde2942aee
18941902+ this.bufferer.startBuffering(this.id, this.process.onProcessData);
18951903+ }
18961904+ },
1905+ +
1906+ + // Replay stored events.
1907+ + onFirstListenerDidAdd: () => {
1908+ + if (this.replayData.length === 0) {
1909+ + return;
1910+ + }
1911+ +
1912+ + logger.debug('Terminal replaying', field('id', this.id));
1913+ + this._onEvent.fire({
1914+ + type: 'replay',
1915+ + events: [{
1916+ + cols: this.cols,
1917+ + rows: this.rows,
1918+ + data: this.replayData.join(""),
1919+ + }]
1920+ + });
1921+ + }
18971922+ });
18981923+
18991924+ public get onEvent(): Event<terminal.IRemoteTerminalProcessEvent> { return this._onEvent.event; }
@@ -1904,6 +1929,33 @@ index 0000000000000000000000000000000000000000..91a932b613c473cd13dfddbde2942aee
19041929+ type: 'data',
19051930+ data,
19061931+ });
1932+ +
1933+ + this.replayData.push(data);
1934+ + this.totalReplayData += data.length;
1935+ +
1936+ + let overflow = this.totalReplayData - this.maxReplayData;
1937+ + if (overflow <= 0) {
1938+ + return;
1939+ + }
1940+ +
1941+ + // Drop events until doing so would put us under budget.
1942+ + let deleteCount = 0;
1943+ + for (; deleteCount < this.replayData.length
1944+ + && this.replayData[deleteCount].length <= overflow; ++deleteCount) {
1945+ + overflow -= this.replayData[deleteCount].length;
1946+ + }
1947+ +
1948+ + if (deleteCount > 0) {
1949+ + this.replayData.splice(0, deleteCount);
1950+ + }
1951+ +
1952+ + // Dropping any more events would put us under budget; trim the first event
1953+ + // instead if still over budget.
1954+ + if (overflow > 0 && this.replayData.length > 0) {
1955+ + this.replayData[0] = this.replayData[0].substring(overflow);
1956+ + }
1957+ +
1958+ + this.totalReplayData = this.replayData.reduce((p, c) => p + c.length, 0);
19071959+ });
19081960+
19091961+ public get pid(): number {
@@ -1924,11 +1976,14 @@ index 0000000000000000000000000000000000000000..91a932b613c473cd13dfddbde2942aee
19241976+ this.workspaceId = args.workspaceId;
19251977+ this.workspaceName = args.workspaceName;
19261978+
1979+ + this.cols = args.cols;
1980+ + this.rows = args.rows;
1981+ +
19271982+ this.process = new TerminalProcess(
19281983+ config,
19291984+ config.cwd,
1930- + args .cols,
1931- + args .rows,
1985+ + this .cols,
1986+ + this .rows,
19321987+ env,
19331988+ process.env as platform.IProcessEnvironment, // Environment used for `findExecutable`.
19341989+ false, // windowsEnableConpty: boolean,
@@ -1962,10 +2017,6 @@ index 0000000000000000000000000000000000000000..91a932b613c473cd13dfddbde2942aee
19622017+ this.dispose();
19632018+ });
19642019+
1965- + // TODO: replay event
1966- + // type: 'replay';
1967- + // events: ReplayEntry[];
1968- +
19692020+ // TODO: exec command event
19702021+ // type: 'execCommand';
19712022+ // reqId: number;
@@ -1977,9 +2028,11 @@ index 0000000000000000000000000000000000000000..91a932b613c473cd13dfddbde2942aee
19772028+ }
19782029+
19792030+ public dispose() {
2031+ + logger.debug('Terminal disposing', field('id', this.id));
19802032+ this._onEvent.dispose();
19812033+ this.bufferer.dispose();
19822034+ this.process.dispose();
2035+ + this.process.shutdown(true);
19832036+ this._onDispose.fire();
19842037+ this._onDispose.dispose();
19852038+ }
@@ -2005,6 +2058,8 @@ index 0000000000000000000000000000000000000000..91a932b613c473cd13dfddbde2942aee
20052058+ }
20062059+
20072060+ public resize(cols: number, rows: number): void {
2061+ + this.cols = cols;
2062+ + this.rows = rows;
20082063+ return this.process.resize(cols, rows);
20092064+ }
20102065+ }
0 commit comments