Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMegan Rogge <megan.rogge@microsoft.com>2021-05-05 23:03:54 +0300
committerGitHub <noreply@github.com>2021-05-05 23:03:54 +0300
commit9ff7c93a9b4419c1a4bab787c59f0093578bc4d7 (patch)
treee66cb909c2a12782496e6371c045e8ed294a4f7f
parent9864cf242b9256997244b3f95eb75da57297400d (diff)
Persist terminal tab icons and titles on window reload (#123010)
-rw-r--r--src/vs/platform/terminal/common/terminal.ts5
-rw-r--r--src/vs/platform/terminal/node/ptyHostService.ts6
-rw-r--r--src/vs/platform/terminal/node/ptyService.ts22
-rw-r--r--src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts8
-rw-r--r--src/vs/workbench/contrib/terminal/browser/terminal.ts6
-rw-r--r--src/vs/workbench/contrib/terminal/browser/terminalInstance.ts14
-rw-r--r--src/vs/workbench/contrib/terminal/browser/terminalService.ts50
-rw-r--r--src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts1
-rw-r--r--src/vs/workbench/contrib/terminal/browser/terminalView.ts5
-rw-r--r--src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts8
-rw-r--r--src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts7
-rw-r--r--src/vs/workbench/test/browser/workbenchTestServices.ts2
12 files changed, 113 insertions, 21 deletions
diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts
index 59d7391c9e0..b539eb20aeb 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<string>;
getEnvironment(): Promise<IProcessEnvironment>;
setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): Promise<void>;
+ updateTitle(id: number, title: string): Promise<void>;
+ updateIcon(id: number, icon: string): Promise<void>;
getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined>;
reduceConnectionGraceTime(): Promise<void>;
}
@@ -160,7 +162,8 @@ export interface IPtyService {
processBinary(id: number, data: string): Promise<void>;
/** Confirm the process is _not_ an orphan. */
orphanQuestionReply(id: number): Promise<void>;
-
+ updateTitle(id: number, title: string): Promise<void>
+ updateIcon(id: number, icon: string): Promise<void>
getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string>;
getEnvironment(): Promise<IProcessEnvironment>;
getWslPath(original: string): Promise<string>;
diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts
index 12cd6b1fc69..b9f450ad4b3 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<void> {
+ return this._proxy.updateTitle(id, title);
+ }
+ updateIcon(id: number, icon: string): Promise<void> {
+ return this._proxy.updateIcon(id, icon);
+ }
attachToProcess(id: number): Promise<void> {
return this._proxy.attachToProcess(id);
}
diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts
index 77e21a77185..8b3af0d3706 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<void> {
+ this._throwIfNoPty(id).setTitle(title);
+ }
+
+ async updateIcon(id: number, icon: string): Promise<void> {
+ this._throwIfNoPty(id).setIcon(icon);
+ }
+
async detachFromProcess(id: number): Promise<void> {
this._throwIfNoPty(id).detach();
}
@@ -289,11 +297,21 @@ export class PersistentTerminalProcess extends Disposable {
private _pid = -1;
private _cwd = '';
+ private _title: string | undefined;
+
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 +320,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 23cbb1c5d18..8add8f13019 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<void> {
+ await this._remoteTerminalChannel?.updateTitle(id, title);
+ }
+
+ async updateIcon(id: number, icon: string): Promise<void> {
+ await this._remoteTerminalChannel?.updateIcon(id, icon);
+ }
+
async getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string> {
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 65640673ce6..c222eaa6aee 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<IStartExtensionTerminalRequest>;
onInstancesChanged: Event<void>;
onInstanceTitleChanged: Event<ITerminalInstance | undefined>;
+ onInstanceIconChanged: Event<ITerminalInstance | undefined>;
onInstancePrimaryStatusChanged: Event<ITerminalInstance>;
onActiveInstanceChanged: Event<ITerminalInstance | undefined>;
onRequestAvailableProfiles: Event<IAvailableProfilesRequest>;
@@ -287,6 +288,11 @@ export interface ITerminalInstance {
onTitleChanged: Event<ITerminalInstance>;
/**
+ * An event that fires when the terminal instance's icon changes.
+ */
+ onIconChanged: Event<ITerminalInstance>;
+
+ /**
* An event that fires when the terminal instance is disposed.
*/
onDisposed: Event<ITerminalInstance>;
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
index 046becf93ec..7eb18af8748 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
@@ -173,7 +173,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
get areLinksReady(): boolean { return this._areLinksReady; }
get initialDataEvents(): string[] | undefined { return this._initialDataEvents; }
get exitCode(): number | undefined { return this._exitCode; }
- get title(): string { return this._title; }
+
get hadFocusOnExit(): boolean { return this._hadFocusOnExit; }
get isTitleSetByProcess(): boolean { return !!this._messageTitleDisposable; }
get shellLaunchConfig(): IShellLaunchConfig { return this._shellLaunchConfig; }
@@ -182,6 +182,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
get navigationMode(): INavigationMode | undefined { return this._navigationModeAddon; }
get isDisconnected(): boolean { return this._processManager.isDisconnected; }
get isRemote(): boolean { return this._processManager.remoteAuthority !== undefined; }
+ get title(): string { return this._getTitle(); }
get icon(): Codicon | undefined { return this._getIcon(); }
private readonly _onExit = new Emitter<number | undefined>();
@@ -198,6 +199,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
get onLinksReady(): Event<ITerminalInstance> { return this._onLinksReady.event; }
private readonly _onTitleChanged = new Emitter<ITerminalInstance>();
get onTitleChanged(): Event<ITerminalInstance> { return this._onTitleChanged.event; }
+ private readonly _onIconChanged = new Emitter<ITerminalInstance>();
+ get onIconChanged(): Event<ITerminalInstance> { return this._onIconChanged.event; }
private readonly _onData = new Emitter<string>();
get onData(): Event<string> { return this._onData.event; }
private readonly _onBinary = new Emitter<string>();
@@ -327,6 +330,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
return undefined;
}
+ private _getTitle(): string {
+ if (this.shellLaunchConfig.attachPersistentProcess?.title) {
+ return this.shellLaunchConfig.attachPersistentProcess.title;
+ }
+ return this._title;
+ }
+
addDisposable(disposable: IDisposable): void {
this._register(disposable);
}
@@ -1779,7 +1789,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 412cc81f407..b77aa604252 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<void> { return this._onInstancesChanged.event; }
private readonly _onInstanceTitleChanged = new Emitter<ITerminalInstance | undefined>();
get onInstanceTitleChanged(): Event<ITerminalInstance | undefined> { return this._onInstanceTitleChanged.event; }
+ private readonly _onInstanceIconChanged = new Emitter<ITerminalInstance | undefined>();
+ get onInstanceIconChanged(): Event<ITerminalInstance | undefined> { return this._onInstanceIconChanged.event; }
private readonly _onActiveInstanceChanged = new Emitter<ITerminalInstance | undefined>();
get onActiveInstanceChanged(): Event<ITerminalInstance | undefined> { return this._onActiveInstanceChanged.event; }
private readonly _onInstancePrimaryStatusChanged = new Emitter<ITerminalInstance>();
@@ -294,12 +296,18 @@ export class TerminalService implements ITerminalService {
}
private _attachProcessLayoutListeners(isRemote: boolean): void {
- this.onActiveTabChanged(() => isRemote ? this._updateRemoteState() : this._updateLocalState());
- this.onActiveInstanceChanged(() => isRemote ? this._updateRemoteState() : this._updateLocalState());
- this.onInstancesChanged(() => isRemote ? this._updateRemoteState() : this._updateLocalState());
+ this.onActiveTabChanged(() => this._saveState(isRemote));
+ this.onActiveInstanceChanged(() => this._saveState(isRemote));
+ this.onInstancesChanged(() => this._saveState(isRemote));
// 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.onInstanceProcessIdReady(() => this._saveState(isRemote));
+ this.onInstanceTitleChanged(instance => {
+ this._updateTitle(instance);
+ });
+ this.onInstanceIconChanged(instance => {
+ this._updateIcon(instance);
+ });
}
private _handleInstanceContextKeys(): void {
@@ -411,21 +419,30 @@ export class TerminalService implements ITerminalService {
}
@debounce(500)
- private _updateRemoteState(): void {
- if (!!this._environmentService.remoteAuthority) {
- const state: ITerminalsLayoutInfoById = {
- tabs: this.terminalGroups.map(t => t.getLayoutInfo(t === this.getActiveGroup()))
- };
- this._remoteTerminalService.setTerminalLayoutInfo(state);
+ private _saveState(isRemote?: boolean): void {
+ const offProcService = isRemote ? this._remoteTerminalService : this._localTerminalService;
+ const state: ITerminalsLayoutInfoById = {
+ tabs: this.terminalGroups.map(t => t.getLayoutInfo(t === this.getActiveGroup()))
+ };
+ offProcService!.setTerminalLayoutInfo(state);
+ }
+
+ @debounce(500)
+ private _updateTitle(instance?: ITerminalInstance): void {
+ if (!instance?.persistentProcessId || !instance?.title) {
+ return;
}
+ this._localTerminalService!.updateTitle(instance.persistentProcessId, instance.title);
+ this._saveState();
}
@debounce(500)
- private _updateLocalState(): void {
- const state: ITerminalsLayoutInfoById = {
- tabs: this.terminalGroups.map(t => t.getLayoutInfo(t === this.getActiveGroup()))
- };
- this._localTerminalService!.setTerminalLayoutInfo(state);
+ private _updateIcon(instance?: ITerminalInstance): void {
+ if (!instance?.persistentProcessId || !instance?.icon) {
+ return;
+ }
+ this._localTerminalService!.updateIcon(instance.persistentProcessId, instance.icon.id);
+ this._saveState();
}
private _removeTab(group: ITerminalGroup): void {
@@ -630,13 +647,14 @@ 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));
instance.addDisposable(instance.onDimensionsChanged(() => {
this._onInstanceDimensionsChanged.fire(instance);
if (this.configHelper.config.enablePersistentSessions && this.isProcessSupportRegistered) {
- !!this._environmentService.remoteAuthority ? this._updateRemoteState() : this._updateLocalState();
+ this._saveState(!!this._environmentService.remoteAuthority);
}
}));
instance.addDisposable(instance.onMaximumDimensionsChanged(() => this._onInstanceMaximumDimensionsChanged.fire(instance)));
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts
index 586a375436a..cba17982e1f 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<ITerminalInstance> {
);
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 df15b8c1cc9..c43f4530b24 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 bdba2cba32b..1844ff66f35 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<void>('$setTerminalLayoutInfo', args);
}
+ updateTitle(id: number, title: string): Promise<string> {
+ return this._channel.call('$updateTitle', { id, title });
+ }
+
+ updateIcon(id: number, icon: string): Promise<string> {
+ return this._channel.call('$updateIcon', { id, icon });
+ }
+
getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined> {
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 f32180d18bd..4d465ac7836 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<void> {
+ await this._localPtyService.updateTitle(id, title);
+ }
+
+ async updateIcon(id: number, icon: string): Promise<void> {
+ await this._localPtyService.updateIcon(id, icon);
+ }
async createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean, shouldPersist: boolean): Promise<ITerminalChildProcess> {
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 ad5699dc83d..1e2d16ecc29 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<ITerminalsLayoutInfo | undefined> { throw new Error('Method not implemented.'); }
async reduceConnectionGraceTime(): Promise<void> { throw new Error('Method not implemented.'); }
processBinary(id: number, data: string): Promise<void> { throw new Error('Method not implemented.'); }
+ updateTitle(id: number, title: string): Promise<void> { throw new Error('Method not implemented.'); }
+ updateIcon(id: number, icon: string): Promise<void> { throw new Error('Method not implemented.'); }
}
class TestTerminalChildProcess implements ITerminalChildProcess {