From 4df0a67ff8e44af660d40b971bc29bc38cdf52ac Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 5 May 2021 09:59:04 -0700 Subject: [PATCH] fix #122441 --- src/vs/platform/terminal/common/terminal.ts | 5 ++- .../platform/terminal/node/ptyHostService.ts | 6 +++ src/vs/platform/terminal/node/ptyService.ts | 24 +++++++++++- .../terminal/browser/remoteTerminalService.ts | 8 ++++ .../contrib/terminal/browser/terminal.ts | 6 +++ .../terminal/browser/terminalInstance.ts | 4 +- .../terminal/browser/terminalService.ts | 37 +++++++++++++++++++ .../terminal/browser/terminalTabsList.ts | 1 + .../contrib/terminal/browser/terminalView.ts | 5 +++ .../terminal/common/remoteTerminalChannel.ts | 8 ++++ .../electron-sandbox/localTerminalService.ts | 7 ++++ .../test/browser/workbenchTestServices.ts | 2 + 12 files changed, 109 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 59d7391c9e0ba..b539eb20aebdc 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -99,6 +99,8 @@ export interface IOffProcessTerminalService { getWslPath(original: string): Promise; getEnvironment(): Promise; setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): Promise; + updateTitle(id: number, title: string): Promise; + updateIcon(id: number, icon: string): Promise; getTerminalLayoutInfo(): Promise; reduceConnectionGraceTime(): Promise; } @@ -160,7 +162,8 @@ export interface IPtyService { processBinary(id: number, data: string): Promise; /** Confirm the process is _not_ an orphan. */ orphanQuestionReply(id: number): Promise; - + updateTitle(id: number, title: string): Promise + updateIcon(id: number, icon: string): Promise getDefaultSystemShell(osOverride?: OperatingSystem): Promise; getEnvironment(): Promise; getWslPath(original: string): Promise; diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 12cd6b1fc693b..b9f450ad4b30c 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -154,6 +154,12 @@ export class PtyHostService extends Disposable implements IPtyService { lastPtyId = Math.max(lastPtyId, id); return id; } + updateTitle(id: number, title: string): Promise { + return this._proxy.updateTitle(id, title); + } + updateIcon(id: number, icon: string): Promise { + return this._proxy.updateIcon(id, icon); + } attachToProcess(id: number): Promise { return this._proxy.attachToProcess(id); } diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 77e21a771856a..20340c98fd9bb 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -114,6 +114,14 @@ export class PtyService extends Disposable implements IPtyService { } } + async updateTitle(id: number, title: string): Promise { + this._throwIfNoPty(id).setTitle(title); + } + + async updateIcon(id: number, icon: string): Promise { + this._throwIfNoPty(id).setIcon(icon); + } + async detachFromProcess(id: number): Promise { this._throwIfNoPty(id).detach(); } @@ -290,10 +298,22 @@ export class PersistentTerminalProcess extends Disposable { private _pid = -1; private _cwd = ''; + private _title: string = ''; + get pid(): number { return this._pid; } - get title(): string { return this._terminalProcess.currentTitle; } + get title(): string { + return this._title || this._terminalProcess.currentTitle; + } get icon(): string | undefined { return this._icon; } + setTitle(title: string): void { + this._title = title; + } + + setIcon(icon: string): void { + this._icon = icon; + } + constructor( private _persistentProcessId: number, private readonly _terminalProcess: TerminalProcess, @@ -302,7 +322,7 @@ export class PersistentTerminalProcess extends Disposable { readonly shouldPersistTerminal: boolean, cols: number, rows: number, private readonly _logService: ILogService, - private readonly _icon?: string + private _icon?: string ) { super(); this._recorder = new TerminalRecorder(cols, rows); diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts index 23cbb1c5d18c7..8add8f13019bf 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts @@ -189,6 +189,14 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal }); } + async updateTitle(id: number, title: string): Promise { + await this._remoteTerminalChannel?.updateTitle(id, title); + } + + async updateIcon(id: number, icon: string): Promise { + await this._remoteTerminalChannel?.updateIcon(id, icon); + } + async getDefaultSystemShell(osOverride?: OperatingSystem): Promise { return this._remoteTerminalChannel?.getDefaultSystemShell(osOverride) || ''; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 65640673ce65f..c222eaa6aee5e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -93,6 +93,7 @@ export interface ITerminalService { onInstanceRequestStartExtensionTerminal: Event; onInstancesChanged: Event; onInstanceTitleChanged: Event; + onInstanceIconChanged: Event; onInstancePrimaryStatusChanged: Event; onActiveInstanceChanged: Event; onRequestAvailableProfiles: Event; @@ -286,6 +287,11 @@ export interface ITerminalInstance { */ onTitleChanged: Event; + /** + * An event that fires when the terminal instance's icon changes. + */ + onIconChanged: Event; + /** * An event that fires when the terminal instance is disposed. */ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 046becf93ecec..1421769e763bb 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -198,6 +198,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { get onLinksReady(): Event { return this._onLinksReady.event; } private readonly _onTitleChanged = new Emitter(); get onTitleChanged(): Event { return this._onTitleChanged.event; } + private readonly _onIconChanged = new Emitter(); + get onIconChanged(): Event { return this._onIconChanged.event; } private readonly _onData = new Emitter(); get onData(): Event { return this._onData.event; } private readonly _onBinary = new Emitter(); @@ -1779,7 +1781,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); if (result) { this.shellLaunchConfig.icon = result.description; - this._onTitleChanged.fire(this); + this._onIconChanged.fire(this); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 412cc81f407bb..c50697aae9b2a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -110,6 +110,8 @@ export class TerminalService implements ITerminalService { get onInstancesChanged(): Event { return this._onInstancesChanged.event; } private readonly _onInstanceTitleChanged = new Emitter(); get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } + private readonly _onInstanceIconChanged = new Emitter(); + get onInstanceIconChanged(): Event { return this._onInstanceIconChanged.event; } private readonly _onActiveInstanceChanged = new Emitter(); get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } private readonly _onInstancePrimaryStatusChanged = new Emitter(); @@ -300,6 +302,22 @@ export class TerminalService implements ITerminalService { // The state must be updated when the terminal is relaunched, otherwise the persistent // terminal ID will be stale and the process will be leaked. this.onInstanceProcessIdReady(() => isRemote ? this._updateRemoteState() : this._updateLocalState()); + this.onInstanceTitleChanged(instance => { + this._updateTitle(instance); + if (isRemote) { + this._updateRemoteState(); + } else { + this._updateLocalState(); + } + }); + this.onInstanceIconChanged(instance => { + this._updateIcon(instance); + if (isRemote) { + this._updateRemoteState(); + } else { + this._updateLocalState(); + } + }); } private _handleInstanceContextKeys(): void { @@ -420,6 +438,24 @@ export class TerminalService implements ITerminalService { } } + @debounce(500) + private _updateTitle(instance?: ITerminalInstance): void { + if (!instance?.persistentProcessId || !instance?.title) { + return; + } + this._localTerminalService!.updateTitle(instance.persistentProcessId, instance.title); + this._updateLocalState(); + } + + @debounce(500) + private _updateIcon(instance?: ITerminalInstance): void { + if (!instance?.persistentProcessId || !instance?.icon) { + return; + } + this._localTerminalService!.updateIcon(instance.persistentProcessId, instance.icon.id); + this._updateLocalState(); + } + @debounce(500) private _updateLocalState(): void { const state: ITerminalsLayoutInfoById = { @@ -630,6 +666,7 @@ export class TerminalService implements ITerminalService { protected _initInstanceListeners(instance: ITerminalInstance): void { instance.addDisposable(instance.onDisposed(this._onInstanceDisposed.fire, this._onInstanceDisposed)); instance.addDisposable(instance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged)); + instance.addDisposable(instance.onIconChanged(this._onInstanceIconChanged.fire, this._onInstanceIconChanged)); instance.addDisposable(instance.onProcessIdReady(this._onInstanceProcessIdReady.fire, this._onInstanceProcessIdReady)); instance.addDisposable(instance.statusList.onDidChangePrimaryStatus(() => this._onInstancePrimaryStatusChanged.fire(instance))); instance.addDisposable(instance.onLinksReady(this._onInstanceLinksReady.fire, this._onInstanceLinksReady)); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 586a375436ad1..cba17982e1f5c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -74,6 +74,7 @@ export class TerminalTabList extends WorkbenchList { ); this._terminalService.onInstancesChanged(() => this._render()); this._terminalService.onInstanceTitleChanged(() => this._render()); + this._terminalService.onInstanceIconChanged(() => this._render()); this._terminalService.onActiveInstanceChanged(e => { if (e) { const i = this._terminalService.terminalInstances.indexOf(e); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index df15b8c1cc995..c43f4530b24ab 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -346,6 +346,11 @@ class SingleTerminalTabActionViewItem extends ActionViewItem { this.updateLabel(); } })); + this._register(this._terminalService.onInstanceIconChanged(e => { + if (e === this._terminalService.getActiveInstance()) { + this.updateLabel(); + } + })); } override updateLabel(): void { diff --git a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts index bdba2cba32bb2..1844ff66f3545 100644 --- a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts @@ -253,6 +253,14 @@ export class RemoteTerminalChannelClient { return this._channel.call('$setTerminalLayoutInfo', args); } + updateTitle(id: number, title: string): Promise { + return this._channel.call('$updateTitle', { id, title }); + } + + updateIcon(id: number, icon: string): Promise { + return this._channel.call('$updateIcon', { id, icon }); + } + getTerminalLayoutInfo(): Promise { const workspace = this._workspaceContextService.getWorkspace(); const args: IGetTerminalLayoutInfoArgs = { diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts index f32180d18bd72..4d465ac7836eb 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts @@ -97,6 +97,13 @@ export class LocalTerminalService extends Disposable implements ILocalTerminalSe })); } } + async updateTitle(id: number, title: string): Promise { + await this._localPtyService.updateTitle(id, title); + } + + async updateIcon(id: number, icon: string): Promise { + await this._localPtyService.updateIcon(id, icon); + } async createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean, shouldPersist: boolean): Promise { const executableEnv = await this._shellEnvironmentService.getShellEnv(); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 61837014956e1..653dcbe4baff8 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1617,6 +1617,8 @@ export class TestLocalTerminalService implements ILocalTerminalService { async getTerminalLayoutInfo(): Promise { throw new Error('Method not implemented.'); } async reduceConnectionGraceTime(): Promise { throw new Error('Method not implemented.'); } processBinary(id: number, data: string): Promise { throw new Error('Method not implemented.'); } + updateTitle(id: number, title: string): Promise { throw new Error('Method not implemented.'); } + updateIcon(id: number, icon: string): Promise { throw new Error('Method not implemented.'); } } class TestTerminalChildProcess implements ITerminalChildProcess {