Skip to content

Commit 3be6ca6

Browse files
authored
Merge pull request microsoft#203736 from microsoft/tyriar/203663
Reduce code duplication in pty impls
2 parents 1dcd560 + 9a8ca92 commit 3be6ca6

File tree

3 files changed

+139
-200
lines changed

3 files changed

+139
-200
lines changed

src/vs/workbench/contrib/terminal/browser/remotePty.ts

Lines changed: 10 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -4,54 +4,22 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { Barrier } from 'vs/base/common/async';
7-
import { Emitter } from 'vs/base/common/event';
8-
import { Disposable } from 'vs/base/common/lifecycle';
9-
import { mark } from 'vs/base/common/performance';
10-
import { URI } from 'vs/base/common/uri';
11-
import { IPtyHostProcessReplayEvent, ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
12-
import { IProcessDataEvent, ITerminalChildProcess, ITerminalLaunchError, IProcessProperty, IProcessPropertyMap, ProcessPropertyType, IProcessReadyEvent, ITerminalLogService } from 'vs/platform/terminal/common/terminal';
7+
import { IProcessPropertyMap, ITerminalChildProcess, ITerminalLaunchError, ITerminalLogService, ProcessPropertyType } from 'vs/platform/terminal/common/terminal';
8+
import { BasePty } from 'vs/workbench/contrib/terminal/common/basePty';
139
import { RemoteTerminalChannelClient } from 'vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel';
1410
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
1511

16-
export class RemotePty extends Disposable implements ITerminalChildProcess {
12+
export class RemotePty extends BasePty implements ITerminalChildProcess {
1713
private readonly _startBarrier: Barrier;
18-
private readonly _properties: IProcessPropertyMap = {
19-
cwd: '',
20-
initialCwd: '',
21-
fixedDimensions: { cols: undefined, rows: undefined },
22-
title: '',
23-
shellType: undefined,
24-
hasChildProcesses: true,
25-
resolvedShellLaunchConfig: {},
26-
overrideDimensions: undefined,
27-
failedShellIntegrationActivation: false,
28-
usedShellIntegrationInjection: undefined
29-
};
30-
private readonly _lastDimensions: { cols: number; rows: number } = { cols: -1, rows: -1 };
31-
32-
private _inReplay = false;
33-
34-
private readonly _onProcessData = this._register(new Emitter<string | IProcessDataEvent>());
35-
readonly onProcessData = this._onProcessData.event;
36-
private readonly _onProcessReplayComplete = this._register(new Emitter<void>());
37-
readonly onProcessReplayComplete = this._onProcessReplayComplete.event;
38-
private readonly _onProcessReady = this._register(new Emitter<IProcessReadyEvent>());
39-
readonly onProcessReady = this._onProcessReady.event;
40-
private readonly _onDidChangeProperty = this._register(new Emitter<IProcessProperty<any>>());
41-
readonly onDidChangeProperty = this._onDidChangeProperty.event;
42-
private readonly _onProcessExit = this._register(new Emitter<number | undefined>());
43-
readonly onProcessExit = this._onProcessExit.event;
44-
private readonly _onRestoreCommands = this._register(new Emitter<ISerializedCommandDetectionCapability>());
45-
readonly onRestoreCommands = this._onRestoreCommands.event;
4614

4715
constructor(
48-
readonly id: number,
49-
readonly shouldPersist: boolean,
16+
id: number,
17+
shouldPersist: boolean,
5018
private readonly _remoteTerminalChannel: RemoteTerminalChannelClient,
5119
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
5220
@ITerminalLogService private readonly _logService: ITerminalLogService
5321
) {
54-
super();
22+
super(id, shouldPersist);
5523
this._startBarrier = new Barrier();
5624
}
5725

@@ -97,6 +65,10 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
9765
});
9866
}
9967

68+
processBinary(e: string): Promise<void> {
69+
return this._remoteTerminalChannel.processBinary(this.id, e);
70+
}
71+
10072
resize(cols: number, rows: number): void {
10173
if (this._inReplay || this._lastDimensions.cols === cols && this._lastDimensions.rows === rows) {
10274
return;
@@ -134,14 +106,6 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
134106
return this._remoteTerminalChannel.setUnicodeVersion(this.id, version);
135107
}
136108

137-
async getInitialCwd(): Promise<string> {
138-
return this._properties.initialCwd;
139-
}
140-
141-
async getCwd(): Promise<string> {
142-
return this._properties.cwd || this._properties.initialCwd;
143-
}
144-
145109
async refreshProperty<T extends ProcessPropertyType>(type: T): Promise<IProcessPropertyMap[T]> {
146110
return this._remoteTerminalChannel.refreshProperty(this.id, type);
147111
}
@@ -150,62 +114,6 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
150114
return this._remoteTerminalChannel.updateProperty(this.id, type, value);
151115
}
152116

153-
handleData(e: string | IProcessDataEvent) {
154-
this._onProcessData.fire(e);
155-
}
156-
handleExit(e: number | undefined) {
157-
this._onProcessExit.fire(e);
158-
}
159-
processBinary(e: string): Promise<void> {
160-
return this._remoteTerminalChannel.processBinary(this.id, e);
161-
}
162-
handleReady(e: IProcessReadyEvent) {
163-
this._onProcessReady.fire(e);
164-
}
165-
handleDidChangeProperty({ type, value }: IProcessProperty<any>) {
166-
switch (type) {
167-
case ProcessPropertyType.Cwd:
168-
this._properties.cwd = value;
169-
break;
170-
case ProcessPropertyType.InitialCwd:
171-
this._properties.initialCwd = value;
172-
break;
173-
case ProcessPropertyType.ResolvedShellLaunchConfig:
174-
if (value.cwd && typeof value.cwd !== 'string') {
175-
value.cwd = URI.revive(value.cwd);
176-
}
177-
}
178-
this._onDidChangeProperty.fire({ type, value });
179-
}
180-
181-
async handleReplay(e: IPtyHostProcessReplayEvent) {
182-
mark(`code/terminal/willHandleReplay/${this.id}`);
183-
try {
184-
this._inReplay = true;
185-
for (const innerEvent of e.events) {
186-
if (innerEvent.cols !== 0 || innerEvent.rows !== 0) {
187-
// never override with 0x0 as that is a marker for an unknown initial size
188-
this._onDidChangeProperty.fire({ type: ProcessPropertyType.OverrideDimensions, value: { cols: innerEvent.cols, rows: innerEvent.rows, forceExactSize: true } });
189-
}
190-
const e: IProcessDataEvent = { data: innerEvent.data, trackCommit: true };
191-
this._onProcessData.fire(e);
192-
await e.writePromise;
193-
}
194-
} finally {
195-
this._inReplay = false;
196-
}
197-
198-
if (e.commands) {
199-
this._onRestoreCommands.fire(e.commands);
200-
}
201-
202-
// remove size override
203-
this._onDidChangeProperty.fire({ type: ProcessPropertyType.OverrideDimensions, value: undefined });
204-
205-
mark(`code/terminal/didHandleReplay/${this.id}`);
206-
this._onProcessReplayComplete.fire();
207-
}
208-
209117
handleOrphanQuestion() {
210118
this._remoteTerminalChannel.orphanQuestionReply(this.id);
211119
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { Emitter } from 'vs/base/common/event';
7+
import { Disposable } from 'vs/base/common/lifecycle';
8+
import { mark } from 'vs/base/common/performance';
9+
import { URI } from 'vs/base/common/uri';
10+
import type { IPtyHostProcessReplayEvent, ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
11+
import { ProcessPropertyType, type IProcessDataEvent, type IProcessProperty, type IProcessPropertyMap, type IProcessReadyEvent, type ITerminalChildProcess } from 'vs/platform/terminal/common/terminal';
12+
13+
/**
14+
* Responsible for establishing and maintaining a connection with an existing terminal process
15+
* created on the local pty host.
16+
*/
17+
export abstract class BasePty extends Disposable implements Partial<ITerminalChildProcess> {
18+
protected readonly _properties: IProcessPropertyMap = {
19+
cwd: '',
20+
initialCwd: '',
21+
fixedDimensions: { cols: undefined, rows: undefined },
22+
title: '',
23+
shellType: undefined,
24+
hasChildProcesses: true,
25+
resolvedShellLaunchConfig: {},
26+
overrideDimensions: undefined,
27+
failedShellIntegrationActivation: false,
28+
usedShellIntegrationInjection: undefined
29+
};
30+
protected readonly _lastDimensions: { cols: number; rows: number } = { cols: -1, rows: -1 };
31+
protected _inReplay = false;
32+
33+
protected readonly _onProcessData = this._register(new Emitter<IProcessDataEvent | string>());
34+
readonly onProcessData = this._onProcessData.event;
35+
protected readonly _onProcessReplayComplete = this._register(new Emitter<void>());
36+
readonly onProcessReplayComplete = this._onProcessReplayComplete.event;
37+
protected readonly _onProcessReady = this._register(new Emitter<IProcessReadyEvent>());
38+
readonly onProcessReady = this._onProcessReady.event;
39+
protected readonly _onDidChangeProperty = this._register(new Emitter<IProcessProperty<any>>());
40+
readonly onDidChangeProperty = this._onDidChangeProperty.event;
41+
protected readonly _onProcessExit = this._register(new Emitter<number | undefined>());
42+
readonly onProcessExit = this._onProcessExit.event;
43+
protected readonly _onRestoreCommands = this._register(new Emitter<ISerializedCommandDetectionCapability>());
44+
readonly onRestoreCommands = this._onRestoreCommands.event;
45+
46+
constructor(
47+
readonly id: number,
48+
readonly shouldPersist: boolean
49+
) {
50+
super();
51+
}
52+
53+
async getInitialCwd(): Promise<string> {
54+
return this._properties.initialCwd;
55+
}
56+
57+
async getCwd(): Promise<string> {
58+
return this._properties.cwd || this._properties.initialCwd;
59+
}
60+
61+
handleData(e: string | IProcessDataEvent) {
62+
this._onProcessData.fire(e);
63+
}
64+
handleExit(e: number | undefined) {
65+
this._onProcessExit.fire(e);
66+
}
67+
handleReady(e: IProcessReadyEvent) {
68+
this._onProcessReady.fire(e);
69+
}
70+
handleDidChangeProperty({ type, value }: IProcessProperty<any>) {
71+
switch (type) {
72+
case ProcessPropertyType.Cwd:
73+
this._properties.cwd = value;
74+
break;
75+
case ProcessPropertyType.InitialCwd:
76+
this._properties.initialCwd = value;
77+
break;
78+
case ProcessPropertyType.ResolvedShellLaunchConfig:
79+
if (value.cwd && typeof value.cwd !== 'string') {
80+
value.cwd = URI.revive(value.cwd);
81+
}
82+
}
83+
this._onDidChangeProperty.fire({ type, value });
84+
}
85+
async handleReplay(e: IPtyHostProcessReplayEvent) {
86+
mark(`code/terminal/willHandleReplay/${this.id}`);
87+
try {
88+
this._inReplay = true;
89+
for (const innerEvent of e.events) {
90+
if (innerEvent.cols !== 0 || innerEvent.rows !== 0) {
91+
// never override with 0x0 as that is a marker for an unknown initial size
92+
this._onDidChangeProperty.fire({ type: ProcessPropertyType.OverrideDimensions, value: { cols: innerEvent.cols, rows: innerEvent.rows, forceExactSize: true } });
93+
}
94+
const e: IProcessDataEvent = { data: innerEvent.data, trackCommit: true };
95+
this._onProcessData.fire(e);
96+
await e.writePromise;
97+
}
98+
} finally {
99+
this._inReplay = false;
100+
}
101+
102+
if (e.commands) {
103+
this._onRestoreCommands.fire(e.commands);
104+
}
105+
106+
// remove size override
107+
this._onDidChangeProperty.fire({ type: ProcessPropertyType.OverrideDimensions, value: undefined });
108+
109+
mark(`code/terminal/didHandleReplay/${this.id}`);
110+
this._onProcessReplayComplete.fire();
111+
}
112+
}

0 commit comments

Comments
 (0)