From 77e7fe0b205df3e7dc44ddd002a992a5aa0d8693 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 2 Jun 2022 15:33:04 +0800 Subject: [PATCH] fix: get the correct terminal instance by terminal short id (#1162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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: 野声 --- .../vscode/api/main.thread.terminal.ts | 59 ++++++++++++------- .../hosted/api/vscode/ext.host.terminal.ts | 20 +++---- .../terminal-next/src/browser/terminal.api.ts | 48 +++++++-------- .../src/browser/terminal.client.ts | 2 +- .../src/browser/terminal.controller.ts | 23 ++++---- .../src/browser/terminal.internal.service.ts | 5 +- packages/terminal-next/src/common/client.ts | 3 +- .../terminal-next/src/common/extension.ts | 3 + packages/terminal-next/src/common/pty.ts | 3 +- 9 files changed, 94 insertions(+), 72 deletions(-) diff --git a/packages/extension/src/browser/vscode/api/main.thread.terminal.ts b/packages/extension/src/browser/vscode/api/main.thread.terminal.ts index a9c3d48f01..a33231923f 100644 --- a/packages/extension/src/browser/vscode/api/main.thread.terminal.ts +++ b/packages/extension/src/browser/vscode/api/main.thread.terminal.ts @@ -31,6 +31,9 @@ import { IActivationEventService } from '../../types'; @Injectable({ multiple: true }) export class MainThreadTerminal implements IMainThreadTerminal { private readonly proxy: IExtHostTerminal; + + shortId2LongIdMap: Map = new Map(); + private readonly _terminalProcessProxies = new Map(); private readonly _profileProviders = new Map(); @@ -142,33 +145,43 @@ export class MainThreadTerminal implements IMainThreadTerminal { this._updateDefaultProfile(); } + transform(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 { 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 { @@ -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 { diff --git a/packages/extension/src/hosted/api/vscode/ext.host.terminal.ts b/packages/extension/src/hosted/api/vscode/ext.host.terminal.ts index b5da1cc4ea..6d6d2b4c21 100644 --- a/packages/extension/src/hosted/api/vscode/ext.host.terminal.ts +++ b/packages/extension/src/hosted/api/vscode/ext.host.terminal.ts @@ -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; @@ -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 { - await this.proxy.$createTerminal(options, id); - this.created(id); + async create(options: vscode.TerminalOptions, shortId: string): Promise { + 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(); } diff --git a/packages/terminal-next/src/browser/terminal.api.ts b/packages/terminal-next/src/browser/terminal.api.ts index 4f71bc0376..2dc726c213 100644 --- a/packages/terminal-next/src/browser/terminal.api.ts +++ b/packages/terminal-next/src/browser/terminal.api.ts @@ -40,8 +40,6 @@ export class TerminalApiService implements ITerminalApiService { @Autowired(ITerminalNetwork) protected readonly network: ITerminalNetwork; - protected _entries = new Map(); - constructor() { this.controller.onDidOpenTerminal((info) => { this._onDidOpenTerminal.fire(info); @@ -98,12 +96,10 @@ 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(); @@ -111,42 +107,46 @@ export class TerminalApiService implements ITerminalApiService { 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) { diff --git a/packages/terminal-next/src/browser/terminal.client.ts b/packages/terminal-next/src/browser/terminal.client.ts index 2fd3636a9c..2d0702602c 100644 --- a/packages/terminal-next/src/browser/terminal.client.ts +++ b/packages/terminal-next/src/browser/terminal.client.ts @@ -286,7 +286,6 @@ export class TerminalClient extends Disposable implements ITerminalClient { config: this.convertTerminalOptionsToLaunchConfig(), }); } - convertProfileToLaunchConfig( shellLaunchConfigOrProfile: IShellLaunchConfig | ITerminalProfile | undefined, cwd?: Uri | string, @@ -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, }; } diff --git a/packages/terminal-next/src/browser/terminal.controller.ts b/packages/terminal-next/src/browser/terminal.controller.ts index 356d399186..24bffc04a2 100644 --- a/packages/terminal-next/src/browser/terminal.controller.ts +++ b/packages/terminal-next/src/browser/terminal.controller.ts @@ -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); @@ -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, @@ -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; diff --git a/packages/terminal-next/src/browser/terminal.internal.service.ts b/packages/terminal-next/src/browser/terminal.internal.service.ts index 341da1b929..371dacbe71 100644 --- a/packages/terminal-next/src/browser/terminal.internal.service.ts +++ b/packages/terminal-next/src/browser/terminal.internal.service.ts @@ -12,6 +12,7 @@ import { IShellLaunchConfig, ITerminalConnection, IPtyProcessChangeEvent, + TERMINAL_ID_SEPARATOR, } from '../common'; import { IXTerm } from '../common/xterm'; @@ -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) { diff --git a/packages/terminal-next/src/common/client.ts b/packages/terminal-next/src/common/client.ts index 6cea0c2ba8..34da98d349 100644 --- a/packages/terminal-next/src/common/client.ts +++ b/packages/terminal-next/src/common/client.ts @@ -22,7 +22,8 @@ export interface ITerminalTitleChangeEvent { export interface ITerminalClient extends Disposable { /** - * 标识终端客户端的唯一 id + * 标识终端客户端的唯一 id。 + * 长 id,由 clientId + "|" + shortId 组成 */ id: string; diff --git a/packages/terminal-next/src/common/extension.ts b/packages/terminal-next/src/common/extension.ts index 544045dea6..2ecbc9cec0 100644 --- a/packages/terminal-next/src/common/extension.ts +++ b/packages/terminal-next/src/common/extension.ts @@ -139,6 +139,9 @@ export interface ITerminalChildProcess { } export interface ITerminalProcessExtHostProxy extends IDisposable { + /** + * 这个 id 是由 clientId 和 shortId 拼接而成的 + */ readonly terminalId: string; emitData(data: string): void; diff --git a/packages/terminal-next/src/common/pty.ts b/packages/terminal-next/src/common/pty.ts index 8dcbae9f96..9ba62a3ad9 100644 --- a/packages/terminal-next/src/common/pty.ts +++ b/packages/terminal-next/src/common/pty.ts @@ -544,7 +544,8 @@ export interface IShellLaunchConfig { export interface ICreateTerminalOptions { /** - * unique id + * unique long id + * longId = clientId + '|' + shortId(generate by uuid()) */ id?: string; /**