clear terminal layout info on window close (#117496)

* fix #117372

* fix #117583

* restore any valid terminals
This commit is contained in:
Megan Rogge 2021-02-24 16:02:50 -08:00 committed by GitHub
parent 8de95c02db
commit d83895acf7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 50 additions and 28 deletions

View file

@ -95,8 +95,12 @@ export class PtyService extends Disposable implements IPtyService {
}
async attachToProcess(id: number): Promise<void> {
this._throwIfNoPty(id).attach();
this._logService.trace(`Persistent terminal "${id}": Attach`);
try {
this._throwIfNoPty(id);
this._logService.trace(`Persistent terminal reconnection "${id}"`);
} catch (e) {
this._logService.trace(`Persistent terminal reconnection "${id}" failed`, e.message);
}
}
async detachFromProcess(id: number): Promise<void> {
@ -136,16 +140,13 @@ export class PtyService extends Disposable implements IPtyService {
}
async getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise<ITerminalsLayoutInfo | undefined> {
if (args) {
const layout = this._workspaceLayoutInfos.get(args.workspaceId);
if (layout) {
const expandedTabs = await Promise.all(layout.tabs.map(async tab => this._expandTerminalTab(tab)));
const filtered = expandedTabs.filter(t => t.terminals.length > 0);
return {
tabs: filtered
};
}
const layout = this._workspaceLayoutInfos.get(args.workspaceId);
if (layout) {
const expandedTabs = await Promise.all(layout.tabs.map(async tab => this._expandTerminalTab(tab)));
const filtered = expandedTabs.filter(t => t.terminals.length > 0);
return {
tabs: filtered
};
}
return undefined;
}
@ -161,12 +162,21 @@ export class PtyService extends Disposable implements IPtyService {
}
private async _expandTerminalInstance(t: ITerminalInstanceLayoutInfoById): Promise<IRawTerminalInstanceLayoutInfo<IPtyHostDescriptionDto | null>> {
const persistentTerminalProcess = this._throwIfNoPty(t.terminal);
const termDto = persistentTerminalProcess && await this._terminalToDto(t.terminal, persistentTerminalProcess);
return {
terminal: termDto ?? null,
relativeSize: t.relativeSize
};
try {
const persistentTerminalProcess = this._throwIfNoPty(t.terminal);
const termDto = persistentTerminalProcess && await this._terminalToDto(t.terminal, persistentTerminalProcess);
return {
terminal: termDto ?? null,
relativeSize: t.relativeSize
};
} catch (e) {
this._logService.trace(`Couldn't get layout info, a terminal was probably disconnected`, e.message);
// this will be filtered out and not reconnected
return {
terminal: null,
relativeSize: t.relativeSize
};
}
}
private async _terminalToDto(id: number, persistentTerminalProcess: PersistentTerminalProcess): Promise<IPtyHostDescriptionDto> {

View file

@ -46,10 +46,10 @@ export interface ITerminalInstanceService {
getXtermWebglConstructor(): Promise<typeof XTermWebglAddon>;
createWindowsShellHelper(shellProcessId: number, xterm: XTermTerminal): IWindowsShellHelper;
createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean, shouldPersist: boolean): Promise<ITerminalChildProcess>;
attachToProcess(id: number): Promise<ITerminalChildProcess>;
attachToProcess(id: number): Promise<ITerminalChildProcess | undefined>;
getDefaultShellAndArgs(useAutomationShell: boolean, platformOverride?: Platform): Promise<{ shell: string, args: string[] | string | undefined }>;
getMainProcessParentEnv(): Promise<IProcessEnvironment>;
setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): void;
setTerminalLayoutInfo(args?: ISetTerminalLayoutInfoArgs): void;
setTerminalLayoutInfo(layout: ITerminalsLayoutInfoById): void;
getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined>;
}

View file

@ -81,7 +81,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
getWorkspaceId(): string {
return '';
}
setTerminalLayoutInfo(layout: ITerminalsLayoutInfoById, id?: string): Promise<void> {
setTerminalLayoutInfo(layout?: ITerminalsLayoutInfoById, id?: string): Promise<void> {
throw new Error('Method not implemented.');
}
getTerminalLayoutInfo(args?: IGetTerminalLayoutInfoArgs): Promise<ITerminalsLayoutInfo | undefined> {

View file

@ -195,7 +195,13 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
// Flow control is not needed for ptys hosted in the same process (ie. the electron
// renderer).
if (shellLaunchConfig.attachPersistentTerminal) {
this._process = await this._terminalInstanceService.attachToProcess(shellLaunchConfig.attachPersistentTerminal.id);
const result = await this._terminalInstanceService.attachToProcess(shellLaunchConfig.attachPersistentTerminal.id);
if (result) {
this._process = result;
} else {
this._logService.trace(`Attach to process failed for terminal ${shellLaunchConfig.attachPersistentTerminal}`);
return undefined;
}
} else {
this._process = await this._launchLocalProcess(shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled);
}

View file

@ -182,7 +182,7 @@ export class TerminalService implements ITerminalService {
private async _reconnectToLocalTerminals(): Promise<void> {
// Reattach to all local terminals
const layoutInfo = await this._terminalInstanceService.getTerminalLayoutInfo();
if (layoutInfo) {
if (layoutInfo && layoutInfo.tabs.length > 0) {
this._recreateTerminalTabs(layoutInfo);
// now that terminals have been restored,
// attach listeners to update local state when terminals are changed
@ -335,6 +335,7 @@ export class TerminalService implements ITerminalService {
// Force dispose of all terminal instances
this.terminalInstances.forEach(instance => instance.dispose(true));
this._terminalInstanceService.setTerminalLayoutInfo(undefined);
}
public getTabLabels(): string[] {

View file

@ -117,9 +117,14 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
return this._instantiationService.createInstance(LocalPty, id, shouldPersist);
}
public async attachToProcess(id: number): Promise<ITerminalChildProcess> {
await this._localPtyService.attachToProcess(id);
return this._instantiationService.createInstance(LocalPty, id, true);
public async attachToProcess(id: number): Promise<ITerminalChildProcess | undefined> {
try {
await this._localPtyService.attachToProcess(id);
return this._instantiationService.createInstance(LocalPty, id, true);
} catch (e) {
this._logService.trace(`Couldn't attach to process ${e.message}`);
}
return undefined;
}
private _isWorkspaceShellAllowed(): boolean {
@ -157,10 +162,10 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
return getMainProcessParentEnv();
}
public setTerminalLayoutInfo(layoutInfo: ITerminalsLayoutInfoById): void {
public setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): void {
const args: ISetTerminalLayoutInfoArgs = {
workspaceId: this._getWorkspaceId(),
tabs: layoutInfo.tabs
tabs: layoutInfo ? layoutInfo.tabs : []
};
this._localPtyService.setTerminalLayoutInfo(args);
}