From ba4448e72dab602dfcab1fc718e181093dc7e3cb Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 2 Mar 2021 14:16:12 -0600 Subject: [PATCH] Implement terminal layouts It doesn't mean much until we persist terminals though, I think. --- lib/vscode/src/vs/server/node/channel.ts | 73 ++++++++++++++++++++---- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/lib/vscode/src/vs/server/node/channel.ts b/lib/vscode/src/vs/server/node/channel.ts index 33b7a48f..3105c809 100644 --- a/lib/vscode/src/vs/server/node/channel.ts +++ b/lib/vscode/src/vs/server/node/channel.ts @@ -10,6 +10,7 @@ import * as resources from 'vs/base/common/resources'; import { ReadableStreamEventPayload } from 'vs/base/common/stream'; import { URI, UriComponents } from 'vs/base/common/uri'; import { transformOutgoingURIs } from 'vs/base/common/uriIpc'; +import { getSystemShell } from 'vs/base/node/shell'; import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -27,10 +28,9 @@ import { IEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/co import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; import { deserializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; import * as terminal from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; -import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError, ITerminalsLayoutInfo } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -import { getSystemShell } from 'vs/base/node/shell'; import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; @@ -611,6 +611,22 @@ class Terminal { this.rows = rows; return this.process.resize(cols, rows); } + + /** + * Serializable terminal information that can be sent to the client. + */ + public async description(id: number): Promise { + const cwd = await this.getCwd(); + return { + id, + pid: this.pid, + title: this.title, + cwd, + workspaceId: this.workspaceId, + workspaceName: this.workspaceName, + isOrphan: this.isOrphan, + }; + } } // References: - ../../workbench/api/node/extHostTerminalService.ts @@ -619,6 +635,8 @@ export class TerminalProviderChannel implements IServerChannel(); private id = 0; + private readonly layouts = new Map(); + public constructor (private readonly logService: ILogService) { } @@ -647,6 +665,8 @@ export class TerminalProviderChannel implements IServerChannel { - const cwd = await terminal.getCwd(); - return { - id, - pid: terminal.pid, - title: terminal.title, - cwd, - workspaceId: terminal.workspaceId, - workspaceName: terminal.workspaceName, - isOrphan: terminal.isOrphan, - }; + return terminal.description(id); })); + // Only returned orphaned terminals so we don't end up attaching to // terminals already attached elsewhere. return terminals.filter((t) => t.isOrphan); } + + public async setTerminalLayoutInfo(args: terminal.ISetTerminalLayoutInfoArgs): Promise { + this.layouts.set(args.workspaceId, args); + } + + public async getTerminalLayoutInfo(args: terminal.IGetTerminalLayoutInfoArgs): Promise { + const layout = this.layouts.get(args.workspaceId); + if (!layout) { + return undefined; + } + + const tabs = await Promise.all(layout.tabs.map(async (tab) => { + // The terminals are stored by ID so look them up. + const terminals = await Promise.all(tab.terminals.map(async (t) => { + const terminal = this.terminals.get(t.terminal); + if (!terminal) { + return undefined; + } + return { + ...t, + terminal: await terminal.description(t.terminal), + }; + })); + + return { + ...tab, + // Filter out terminals that have been killed. + terminals: terminals.filter(isDefined), + }; + })); + + return { tabs }; + } } function transformIncoming(remoteAuthority: string, uri: UriComponents | undefined): URI | undefined { const transformer = getUriTransformer(remoteAuthority); return uri ? URI.revive(transformer.transformIncoming(uri)) : uri; } + +function isDefined(t: T | undefined): t is T { + return typeof t !== "undefined"; +}