Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix terminals in editor area not reloading #151852

Merged
merged 11 commits into from
Jul 6, 2022
3 changes: 2 additions & 1 deletion src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ export interface IPtyService extends IPtyHostController {
getProfiles?(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]>;
getEnvironment(): Promise<IProcessEnvironment>;
getWslPath(original: string): Promise<string>;
getRevivedPtyNewId(id: number): Promise<number | undefined>;
setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void>;
getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise<ITerminalsLayoutInfo | undefined>;
reduceConnectionGraceTime(): Promise<void>;
Expand Down Expand Up @@ -459,7 +460,7 @@ export interface IShellLaunchConfig {
/**
* This is a terminal that attaches to an already running terminal.
*/
attachPersistentProcess?: { id: number; pid: number; title: string; titleSource: TitleEventSource; cwd: string; icon?: TerminalIcon; color?: string; hasChildProcesses?: boolean; fixedDimensions?: IFixedTerminalDimensions; environmentVariableCollections?: ISerializableEnvironmentVariableCollections };
attachPersistentProcess?: { id: number; findRevivedId?: boolean; pid: number; title: string; titleSource: TitleEventSource; cwd: string; icon?: TerminalIcon; color?: string; hasChildProcesses?: boolean; fixedDimensions?: IFixedTerminalDimensions; environmentVariableCollections?: ISerializableEnvironmentVariableCollections };

/**
* Whether the terminal process environment should be exactly as provided in
Expand Down
4 changes: 4 additions & 0 deletions src/vs/platform/terminal/node/ptyHostService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@ export class PtyHostService extends Disposable implements IPtyService {
return this._proxy.getWslPath(original);
}

getRevivedPtyNewId(id: number): Promise<number | undefined> {
return this._proxy.getRevivedPtyNewId(id);
}

setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void> {
return this._proxy.setTerminalLayoutInfo(args);
}
Expand Down
9 changes: 9 additions & 0 deletions src/vs/platform/terminal/node/ptyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,15 @@ export class PtyService extends Disposable implements IPtyService {
});
}

async getRevivedPtyNewId(id: number): Promise<number | undefined> {
try {
return this._revivedPtyIdMap.get(id)?.newId;
} catch (e) {
this._logService.trace(`Couldn't find terminal ID ${id}`, e.message);
}
return undefined;
}

async setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void> {
this._workspaceLayoutInfos.set(args.workspaceId, args);
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/server/node/remoteTerminalChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
case '$setTerminalLayoutInfo': return this._ptyService.setTerminalLayoutInfo(<ISetTerminalLayoutInfoArgs>args);
case '$serializeTerminalState': return this._ptyService.serializeTerminalState.apply(this._ptyService, args);
case '$reviveTerminalProcesses': return this._ptyService.reviveTerminalProcesses.apply(this._ptyService, args);
case '$getRevivedPtyNewId': return this._ptyService.getRevivedPtyNewId.apply(this._ptyService, args);
case '$setUnicodeVersion': return this._ptyService.setUnicodeVersion.apply(this._ptyService, args);
case '$reduceConnectionGraceTime': return this._reduceConnectionGraceTime();
case '$updateIcon': return this._ptyService.updateIcon.apply(this._ptyService, args);
Expand Down
14 changes: 14 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,20 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack
return undefined;
}

async attachToRevivedProcess(id: number): Promise<ITerminalChildProcess | undefined> {
if (!this._remoteTerminalChannel) {
throw new Error(`Cannot create remote terminal when there is no remote!`);
}

try {
const newId = await this._remoteTerminalChannel.getRevivedPtyNewId(id) ?? id;
return await this.attachToProcess(newId);
} catch (e) {
this._logService.trace(`Couldn't attach to process ${e.message}`);
}
return undefined;
}

async listProcesses(): Promise<IProcessDetails[]> {
const terms = this._remoteTerminalChannel ? await this._remoteTerminalChannel.listProcesses() : [];
return terms.map(termDto => {
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,11 @@ export interface ITerminalInstance {
*/
readonly persistentProcessId: number | undefined;

/**
* The id of a persistent process during the shutdown process
*/
shutdownPersistentProcessId: number | undefined;

/**
* Whether the process should be persisted across reloads.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor
const inputKey = resource.path;

if ('pid' in deserializedInput) {
const instance = this._terminalInstanceService.createInstance({ attachPersistentProcess: deserializedInput }, TerminalLocation.Editor);
const newDeserializedInput = { ...deserializedInput, findRevivedId: true };
const instance = this._terminalInstanceService.createInstance({ attachPersistentProcess: newDeserializedInput }, TerminalLocation.Editor);
instance.target = TerminalLocation.Editor;
const input = this._instantiationService.createInstance(TerminalEditorInput, resource, instance);
this._registerInstance(inputKey, input, instance);
Expand Down
8 changes: 6 additions & 2 deletions src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {

private readonly _processManager: ITerminalProcessManager;
private readonly _resource: URI;
private _shutdownPersistentProcessId: number | undefined;

// Enables disposal of the xterm onKey
// event when the CwdDetection capability
Expand Down Expand Up @@ -664,8 +665,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
return TerminalInstance._lastKnownCanvasDimensions;
}

get persistentProcessId(): number | undefined { return this._processManager.persistentProcessId; }
get shouldPersist(): boolean { return this._processManager.shouldPersist && !this.shellLaunchConfig.isTransient; }
set shutdownPersistentProcessId(shutdownPersistentProcessId: number | undefined) {
this._shutdownPersistentProcessId = shutdownPersistentProcessId;
}
get persistentProcessId(): number | undefined { return this._processManager.persistentProcessId ?? this._shutdownPersistentProcessId; }
get shouldPersist(): boolean { return (this._processManager.shouldPersist || this._shutdownPersistentProcessId !== undefined) && !this.shellLaunchConfig.isTransient; }

/**
* Create xterm.js instance and attach data listeners.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}
} else {
if (shellLaunchConfig.attachPersistentProcess) {
const result = await backend.attachToProcess(shellLaunchConfig.attachPersistentProcess.id);
const result = shellLaunchConfig.attachPersistentProcess.findRevivedId ? await backend.attachToRevivedProcess(shellLaunchConfig.attachPersistentProcess.id) : await backend.attachToProcess(shellLaunchConfig.attachPersistentProcess.id);
if (result) {
newProcess = result;
} else {
Expand Down
7 changes: 6 additions & 1 deletion src/vs/workbench/contrib/terminal/browser/terminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICreateContributedTerminalProfileOptions, IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalLocation, TerminalLocationString, TitleEventSource } from 'vs/platform/terminal/common/terminal';
import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings';
import { iconForeground } from 'vs/platform/theme/common/colorRegistry';
import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry';
import { ColorScheme } from 'vs/platform/theme/common/theme';
Expand All @@ -38,7 +39,6 @@ import { getInstanceFromResource, getTerminalUri, parseTerminalUri } from 'vs/wo
import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView';
import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalBackend, ITerminalConfigHelper, ITerminalProcessExtHostProxy, ITerminalProfileService, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey';
import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
Expand Down Expand Up @@ -620,8 +620,13 @@ export class TerminalService implements ITerminalService {
return;
}


// Force dispose of all terminal instances
const shouldPersistTerminalsForEvent = this._shouldReviveProcesses(e.reason);
for (const instance of this.instances) {
if (shouldPersistTerminalsForEvent) {
instance.shutdownPersistentProcessId = instance.persistentProcessId;
}
instance.dispose();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ export class RemoteTerminalChannelClient implements IPtyHostController {
return this._channel.call('$reviveTerminalProcesses', [state, dateTimeFormatLocate]);
}

getRevivedPtyNewId(id: number): Promise<number | undefined> {
return this._channel.call('$getRevivedPtyNewId', [id]);
}

serializeTerminalState(ids: number[]): Promise<string> {
return this._channel.call('$serializeTerminalState', [ids]);
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/contrib/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export interface ITerminalBackend {
onDidRequestDetach: Event<{ requestId: number; workspaceId: string; instanceId: number }>;

attachToProcess(id: number): Promise<ITerminalChildProcess | undefined>;
attachToRevivedProcess(id: number): Promise<ITerminalChildProcess | undefined>;
listProcesses(): Promise<IProcessDetails[]>;
getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string>;
getProfiles(profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke
return undefined;
}

async attachToRevivedProcess(id: number): Promise<ITerminalChildProcess | undefined> {
try {
const newId = await this._localPtyService.getRevivedPtyNewId(id) ?? id;
return await this.attachToProcess(newId);
} catch (e) {
this._logService.trace(`Couldn't attach to process ${e.message}`);
}
return undefined;
}

async listProcesses(): Promise<IProcessDetails[]> {
return this._localPtyService.listProcesses();
}
Expand Down