Skip to content

Commit

Permalink
fix: get the correct terminal instance by terminal short id (#1162)
Browse files Browse the repository at this point in the history
* fix: get proxy terminal from web end

* fix: use generated long id as terminal id

* refactor: transform short id to long id (#1161)

* fix: get clients from terminal controller

* chore: use transform to get real terminal id

* chore: update transform

Co-authored-by: 野声 <[email protected]>
  • Loading branch information
erha19 and bytemain authored Jun 2, 2022
1 parent 1db618d commit 77e7fe0
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 72 deletions.
59 changes: 38 additions & 21 deletions packages/extension/src/browser/vscode/api/main.thread.terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import { IActivationEventService } from '../../types';
@Injectable({ multiple: true })
export class MainThreadTerminal implements IMainThreadTerminal {
private readonly proxy: IExtHostTerminal;

shortId2LongIdMap: Map<string, string> = new Map();

private readonly _terminalProcessProxies = new Map<string, ITerminalProcessExtHostProxy>();
private readonly _profileProviders = new Map<string, IDisposable>();

Expand Down Expand Up @@ -142,33 +145,43 @@ export class MainThreadTerminal implements IMainThreadTerminal {
this._updateDefaultProfile();
}

transform<T>(id: string, cb: (sessionId: string) => T): T {
const sessionId = this.shortId2LongIdMap.get(id);
return cb(sessionId || id);
}

$sendText(id: string, text: string, addNewLine?: boolean) {
this.proxy.$acceptTerminalInteraction(id);
return this.terminalApi.sendText(id, text, addNewLine);
return this.transform(id, (sessionId) => {
this.proxy.$acceptTerminalInteraction(sessionId);
return this.terminalApi.sendText(sessionId, text, addNewLine);
});
}

$show(id: string, preserveFocus?: boolean) {
return this.terminalApi.showTerm(id, preserveFocus);
return this.transform(id, (sessionId) => this.terminalApi.showTerm(sessionId, preserveFocus));
}

$hide(id: string) {
return this.terminalApi.hideTerm(id);
return this.transform(id, (sessionId) => this.terminalApi.hideTerm(sessionId));
}

$dispose(id: string) {
return this.terminalApi.removeTerm(id);
return this.transform(id, (sessionId) => this.terminalApi.removeTerm(sessionId));
}

$getProcessId(id: string) {
return this.terminalApi.getProcessId(id);
return this.transform(id, (sessionId) => this.terminalApi.getProcessId(sessionId));
}

async $createTerminal(options: vscode.TerminalOptions, id: string) {
async $createTerminal(options: vscode.TerminalOptions, shortId: string): Promise<void> {
await this.controller.ready.promise;
const terminal = await this.terminalApi.createTerminal(options, id);
const terminal = await this.terminalApi.createTerminal(options, shortId);
if (!terminal) {
return this.logger.error(`Create Terminal ${id} fail.`);
// 应该要 throw Error
this.logger.error(`Create Terminal ${shortId} fail.`);
return;
}
this.shortId2LongIdMap.set(shortId, terminal.id);
}

private _onRequestStartExtensionTerminal(request: IStartExtensionTerminalRequest): void {
Expand All @@ -193,22 +206,26 @@ export class MainThreadTerminal implements IMainThreadTerminal {
proxy.onRequestInitialCwd(() => this.proxy.$acceptProcessRequestInitialCwd(proxy.terminalId));
}

private _getTerminalProcess(terminalId: string): ITerminalProcessExtHostProxy {
const terminal = this._terminalProcessProxies.get(terminalId);
if (!terminal) {
throw new Error(`Unknown terminal: ${terminalId}`);
}
return terminal;
private _getTerminalProcess(id: string): ITerminalProcessExtHostProxy {
return this.transform(id, (terminalId) => {
const terminal = this._terminalProcessProxies.get(terminalId);
if (!terminal) {
throw new Error(`Unknown terminal: ${terminalId}`);
}
return terminal;
});
}

public $sendProcessTitle(terminalId: string, title: string): void {
const terminalWidgetInstance = this.terminalGroupViewService.getWidget(terminalId);
public $sendProcessTitle(id: string, title: string): void {
return this.transform(id, (terminalId) => {
const terminalWidgetInstance = this.terminalGroupViewService.getWidget(terminalId);

if (terminalWidgetInstance) {
terminalWidgetInstance.rename(title);
if (terminalWidgetInstance) {
terminalWidgetInstance.rename(title);

this.proxy.$acceptTerminalTitleChange(terminalId, title);
}
this.proxy.$acceptTerminalTitleChange(terminalId, title);
}
});
}

public $sendProcessData(terminalId: string, data: string): void {
Expand Down
20 changes: 8 additions & 12 deletions packages/extension/src/hosted/api/vscode/ext.host.terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,11 +642,6 @@ export class Terminal implements vscode.Terminal {
public __id: string;

private _exitStatus: vscode.TerminalExitStatus | undefined;
/**
* FIXME: 这里默认值应该为 { isInteractedWith: false }
* 由于终端重连在前端往后端重新初始化了 Terminal,插件进程获取到的 Terminal 与前端 Terminal 仅有 id 等部分信息关联
* 导致状态丢失
*/
private _state: vscode.TerminalState = { isInteractedWith: false };

private createdPromiseResolve;
Expand Down Expand Up @@ -706,18 +701,19 @@ export class Terminal implements vscode.Terminal {

/**
* 所有插件进程的终端调用都需要指定 id
* 该逻辑用于保障依赖 `vscode.window.onDidOpenTermina` 获取 terminal 实例的相关逻辑
* 该逻辑用于保障依赖 `vscode.window.onDidOpenTerminal` 获取 terminal 实例的相关逻辑
* 让相关 terminal 的值引用一致
* 如 vscode-js-debug 中的 https://github.com/microsoft/vscode-js-debug/blob/a201e735c94b9aeb1e13d8c586b91a1fe1ab62b3/src/ui/debugTerminalUI.ts#L198
*/
async create(options: vscode.TerminalOptions, id: string): Promise<void> {
await this.proxy.$createTerminal(options, id);
this.created(id);
async create(options: vscode.TerminalOptions, shortId: string): Promise<void> {
await this.proxy.$createTerminal(options, shortId);
this.created(shortId);
}

created(id) {
this.id = id;
this.__id = id;
created(shortId: string) {
this.id = shortId;
this.__id = shortId;

this.createdPromiseResolve();
}

Expand Down
48 changes: 24 additions & 24 deletions packages/terminal-next/src/browser/terminal.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ export class TerminalApiService implements ITerminalApiService {
@Autowired(ITerminalNetwork)
protected readonly network: ITerminalNetwork;

protected _entries = new Map<string, ITerminalExternalClient>();

constructor() {
this.controller.onDidOpenTerminal((info) => {
this._onDidOpenTerminal.fire(info);
Expand Down Expand Up @@ -98,55 +96,57 @@ export class TerminalApiService implements ITerminalApiService {
},
dispose: () => {
this.view.removeWidget(client.widget.id);
this._entries.delete(client.id);
this.controller.clients.delete(client.id);
},
};

this._entries.set(client.id, external);

await client.attached.promise;

external.show();

return external;
}

getProcessId(sessionId: string) {
return this.service.getProcessId(sessionId);
async getProcessId(sessionId: string) {
const client = this.controller.clients.get(sessionId);
if (!client) {
return;
}
return client.pid;
}

sendText(id: string, text: string, addNewLine = true) {
this.service.sendText(id, `${text}${addNewLine ? '\r' : ''}`);
sendText(sessionId: string, text: string, addNewLine = true) {
this.service.sendText(sessionId, `${text}${addNewLine ? '\r' : ''}`);
}

showTerm(clientId: string, preserveFocus = true) {
const client = this._entries.get(clientId);

showTerm(sessionId: string, preserveFocus = true) {
const client = this.controller.clients.get(sessionId);
if (!client) {
return;
}

client.show(preserveFocus);
const widget = client.widget;
this.view.selectWidget(widget.id);
this.controller.showTerminalPanel();
if (!preserveFocus) {
setTimeout(() => client.focus());
}
}

hideTerm(clientId: string) {
const client = this._entries.get(clientId);

hideTerm(sessionId: string) {
const client = this.controller.clients.get(sessionId);
if (!client) {
return;
}

client.hide();
this.controller.hideTerminalPanel();
}

removeTerm(clientId: string) {
const client = this._entries.get(clientId);

removeTerm(sessionId: string) {
const client = this.controller.clients.get(sessionId);
if (!client) {
return;
}

client.dispose();
this.view.removeWidget(client.widget.id);
this.controller.clients.delete(sessionId);
}

createWidget(uniqName: string, widgetRenderFunc: (element: HTMLDivElement) => void) {
Expand Down
2 changes: 1 addition & 1 deletion packages/terminal-next/src/browser/terminal.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,6 @@ export class TerminalClient extends Disposable implements ITerminalClient {
config: this.convertTerminalOptionsToLaunchConfig(),
});
}

convertProfileToLaunchConfig(
shellLaunchConfigOrProfile: IShellLaunchConfig | ITerminalProfile | undefined,
cwd?: Uri | string,
Expand Down Expand Up @@ -325,6 +324,7 @@ export class TerminalClient extends Disposable implements ITerminalClient {
// 应该是必定能 resolve 到 profile 的
const defaultProfile = await this.terminalProfileInternalService.resolveDefaultProfile();
options = {
id: this._uid,
config: defaultProfile,
};
}
Expand Down
23 changes: 13 additions & 10 deletions packages/terminal-next/src/browser/terminal.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ export class TerminalController extends WithEventBus implements ITerminalControl
let client: ITerminalClient;

if (!options || (options as ICreateTerminalOptions).config || Object.keys(options).length === 0) {
client = await this.clientFactory2(widget, options);
client = await this.clientFactory2(widget, /** @type ICreateTerminalOptions */ options);
this.logger.log('create client with clientFactory2', client);
} else {
client = await this.clientFactory(widget, options);
client = await this.clientFactory(widget, /** @type TerminalOptions */ options);
this.logger.log('create client with clientFactory', client);
}
return this.setupClient(widget, client);
Expand Down Expand Up @@ -571,14 +571,14 @@ export class TerminalController extends WithEventBus implements ITerminalControl
});
}

/**
* @param options
* @returns
*/
async createClientWithWidget2(options: ICreateClientWithWidgetOptions) {
const widgetId =
this.wsChannelHandler.clientId + TERMINAL_ID_SEPARATOR + options.id || this.service.generateSessionId();
const { group } = this._createOneGroup(convertTerminalOptionsToLaunchConfig(options.terminalOptions));
const widgetId = options.id
? this.wsChannelHandler.clientId + TERMINAL_ID_SEPARATOR + options.id
: this.service.generateSessionId();

const launchConfig = convertTerminalOptionsToLaunchConfig(options.terminalOptions);

const { group } = this._createOneGroup(launchConfig);
const widget = this.terminalView.createWidget(
group,
widgetId,
Expand All @@ -590,7 +590,10 @@ export class TerminalController extends WithEventBus implements ITerminalControl
options.beforeCreate(widgetId);
}

const client = await this._createClient(widget, options.terminalOptions);
const client = await this._createClient(widget, {
id: widgetId,
config: launchConfig,
});

if (options.isTaskExecutor) {
client.isTaskExecutor = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
IShellLaunchConfig,
ITerminalConnection,
IPtyProcessChangeEvent,
TERMINAL_ID_SEPARATOR,
} from '../common';
import { IXTerm } from '../common/xterm';

Expand Down Expand Up @@ -39,8 +40,8 @@ export class TerminalInternalService implements ITerminalInternalService {
return this.service.check ? this.service.check(sessionIds) : Promise.resolve(true);
}

private _getExtHostProxy(id: string) {
return this._processExtHostProxies.get(id);
private _getExtHostProxy(longId: string) {
return this._processExtHostProxies.get(longId);
}

async sendText(sessionId: string, message: string) {
Expand Down
3 changes: 2 additions & 1 deletion packages/terminal-next/src/common/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export interface ITerminalTitleChangeEvent {

export interface ITerminalClient extends Disposable {
/**
* 标识终端客户端的唯一 id
* 标识终端客户端的唯一 id。
* 长 id,由 clientId + "|" + shortId 组成
*/
id: string;

Expand Down
3 changes: 3 additions & 0 deletions packages/terminal-next/src/common/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ export interface ITerminalChildProcess {
}

export interface ITerminalProcessExtHostProxy extends IDisposable {
/**
* 这个 id 是由 clientId 和 shortId 拼接而成的
*/
readonly terminalId: string;

emitData(data: string): void;
Expand Down
3 changes: 2 additions & 1 deletion packages/terminal-next/src/common/pty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,8 @@ export interface IShellLaunchConfig {

export interface ICreateTerminalOptions {
/**
* unique id
* unique long id
* longId = clientId + '|' + shortId(generate by uuid())
*/
id?: string;
/**
Expand Down

0 comments on commit 77e7fe0

Please sign in to comment.