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

Ensure layout info is fetched from the pty host on reconnects #187285

Merged
merged 1 commit into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/vs/platform/terminal/node/ptyHostService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,10 @@ export class PtyHostService extends Disposable implements IPtyHostService {
return this._proxy.setTerminalLayoutInfo(args);
}
async getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise<ITerminalsLayoutInfo | undefined> {
return await this._proxy.getTerminalLayoutInfo(args);
// This is optional as we want reconnect requests to go through only if the pty host exists.
// Revive is handled specially as reviveTerminalProcesses is guaranteed to be called before
// the request for layout info.
return this._optionalProxy?.getTerminalLayoutInfo(args);
}

async requestDetachInstance(workspaceId: string, instanceId: number): Promise<IProcessDetails | undefined> {
Expand Down
5 changes: 3 additions & 2 deletions src/vs/platform/terminal/node/ptyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,9 @@ export class PtyService extends Disposable implements IPtyService {
};
} catch (e) {
this._logService.warn(`Couldn't get layout info, a terminal was probably disconnected`, e.message);
this._logService.info('Reattach to wrong terminal debug info - layout info by id', t);
this._logService.info('Reattach to wrong terminal debug info - _revivePtyIdMap', Array.from(this._revivedPtyIdMap.values()));
this._logService.debug('Reattach to wrong terminal debug info - layout info by id', t);
this._logService.debug('Reattach to wrong terminal debug info - _revivePtyIdMap', Array.from(this._revivedPtyIdMap.values()));
this._logService.debug('Reattach to wrong terminal debug info - _ptys ids', Array.from(this._ptys.keys()));
// this will be filtered out and not reconnected
return {
terminal: null,
Expand Down
42 changes: 20 additions & 22 deletions src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,29 +337,27 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack

// Revive processes if needed
const serializedState = this._storageService.get(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE);
const parsed = this._deserializeTerminalState(serializedState);
if (!parsed) {
return undefined;
}

try {
// Note that remote terminals do not get their environment re-resolved unlike in local terminals

mark('code/terminal/willReviveTerminalProcessesRemote');
await this._remoteTerminalChannel.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale);
mark('code/terminal/didReviveTerminalProcessesRemote');
this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE);
// If reviving processes, send the terminal layout info back to the pty host as it
// will not have been persisted on application exit
const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
if (layoutInfo) {
mark('code/terminal/willSetTerminalLayoutInfoRemote');
await this._remoteTerminalChannel.setTerminalLayoutInfo(JSON.parse(layoutInfo));
mark('code/terminal/didSetTerminalLayoutInfoRemote');
this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
const reviveBufferState = this._deserializeTerminalState(serializedState);
if (reviveBufferState && reviveBufferState.length > 0) {
try {
// Note that remote terminals do not get their environment re-resolved unlike in local terminals

mark('code/terminal/willReviveTerminalProcessesRemote');
await this._remoteTerminalChannel.reviveTerminalProcesses(reviveBufferState, Intl.DateTimeFormat().resolvedOptions().locale);
mark('code/terminal/didReviveTerminalProcessesRemote');
this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE);
// If reviving processes, send the terminal layout info back to the pty host as it
// will not have been persisted on application exit
const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
if (layoutInfo) {
mark('code/terminal/willSetTerminalLayoutInfoRemote');
await this._remoteTerminalChannel.setTerminalLayoutInfo(JSON.parse(layoutInfo));
mark('code/terminal/didSetTerminalLayoutInfoRemote');
this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
}
} catch (e: unknown) {
this._logService.warn('RemoteTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e);
}
} catch (e: unknown) {
this._logService.warn('RemoteTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e);
}

return this._remoteTerminalChannel.getTerminalLayoutInfo();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,43 +317,41 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke

// Revive processes if needed
const serializedState = this._storageService.get(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE);
const parsed = this._deserializeTerminalState(serializedState);
if (!parsed) {
return undefined;
}

try {
// Create variable resolver
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot();
const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority), this._configurationResolverService);

// Re-resolve the environments and replace it on the state so local terminals use a fresh
// environment
mark('code/terminal/willGetReviveEnvironments');
await Promise.all(parsed.map(state => new Promise<void>(r => {
this._resolveEnvironmentForRevive(variableResolver, state.shellLaunchConfig).then(freshEnv => {
state.processLaunchConfig.env = freshEnv;
r();
});
})));
mark('code/terminal/didGetReviveEnvironments');

mark('code/terminal/willReviveTerminalProcesses');
await this._proxy.reviveTerminalProcesses(workspaceId, parsed, Intl.DateTimeFormat().resolvedOptions().locale);
mark('code/terminal/didReviveTerminalProcesses');
this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE);
// If reviving processes, send the terminal layout info back to the pty host as it
// will not have been persisted on application exit
const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
if (layoutInfo) {
mark('code/terminal/willSetTerminalLayoutInfo');
await this._proxy.setTerminalLayoutInfo(JSON.parse(layoutInfo));
mark('code/terminal/didSetTerminalLayoutInfo');
this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
const reviveBufferState = this._deserializeTerminalState(serializedState);
if (reviveBufferState && reviveBufferState.length > 0) {
try {
// Create variable resolver
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot();
const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority), this._configurationResolverService);

// Re-resolve the environments and replace it on the state so local terminals use a fresh
// environment
mark('code/terminal/willGetReviveEnvironments');
await Promise.all(reviveBufferState.map(state => new Promise<void>(r => {
this._resolveEnvironmentForRevive(variableResolver, state.shellLaunchConfig).then(freshEnv => {
state.processLaunchConfig.env = freshEnv;
r();
});
})));
mark('code/terminal/didGetReviveEnvironments');

mark('code/terminal/willReviveTerminalProcesses');
await this._proxy.reviveTerminalProcesses(workspaceId, reviveBufferState, Intl.DateTimeFormat().resolvedOptions().locale);
mark('code/terminal/didReviveTerminalProcesses');
this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE);
// If reviving processes, send the terminal layout info back to the pty host as it
// will not have been persisted on application exit
const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
if (layoutInfo) {
mark('code/terminal/willSetTerminalLayoutInfo');
await this._proxy.setTerminalLayoutInfo(JSON.parse(layoutInfo));
mark('code/terminal/didSetTerminalLayoutInfo');
this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
}
} catch (e: unknown) {
this._logService.warn('LocalTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e);
}
} catch (e: unknown) {
this._logService.warn('LocalTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e);
}

return this._proxy.getTerminalLayoutInfo(layoutArgs);
Expand Down