Skip to content

Commit

Permalink
Restore waitOnExit, type, hideFromUser, reconnectionOwner terminal pr…
Browse files Browse the repository at this point in the history
…operties on reconnect (microsoft#155346)
  • Loading branch information
meganrogge authored Jul 25, 2022
1 parent c7a138d commit 1a8d4bf
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 64 deletions.
16 changes: 13 additions & 3 deletions src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,14 @@ export interface IPtyHostAttachTarget {
environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined;
reconnectionOwner?: string;
task?: { label: string; id: string; lastTask: string; group?: string };
waitOnExit?: WaitOnExitValue;
hideFromUser?: boolean;
isFeatureTerminal?: boolean;
type?: TerminalType;
}

export type TerminalType = 'Task' | 'Local' | undefined;

export enum TitleEventSource {
/** From the API or the rename command that overrides any other type */
Api,
Expand Down Expand Up @@ -446,7 +452,7 @@ export interface IShellLaunchConfig {
reconnectionOwner?: string;

/** Whether to wait for a key press before closing the terminal. */
waitOnExit?: boolean | string | ((exitCode: number) => string);
waitOnExit?: WaitOnExitValue;

/**
* A string including ANSI escape sequences that will be written to the terminal emulator
Expand All @@ -469,7 +475,9 @@ export interface IShellLaunchConfig {
/**
* This is a terminal that attaches to an already running terminal.
*/
attachPersistentProcess?: { id: number; findRevivedId?: boolean; pid: number; title: string; titleSource: TitleEventSource; cwd: string; icon?: TerminalIcon; color?: string; hasChildProcesses?: boolean; fixedDimensions?: IFixedTerminalDimensions; environmentVariableCollections?: ISerializableEnvironmentVariableCollections; reconnectionOwner?: string; task?: { label: string; id: string; lastTask: string; group?: string } };
attachPersistentProcess?: {
id: number; findRevivedId?: boolean; pid: number; title: string; titleSource: TitleEventSource; cwd: string; icon?: TerminalIcon; color?: string; hasChildProcesses?: boolean; fixedDimensions?: IFixedTerminalDimensions; environmentVariableCollections?: ISerializableEnvironmentVariableCollections; reconnectionOwner?: string; task?: { label: string; id: string; lastTask: string; group?: string }; type?: TerminalType; waitOnExit?: WaitOnExitValue; hideFromUser?: boolean; isFeatureTerminal?: boolean;
};

/**
* Whether the terminal process environment should be exactly as provided in
Expand Down Expand Up @@ -544,9 +552,11 @@ export interface IShellLaunchConfig {
/**
* The task associated with this terminal
*/
task?: { lastTask: string; group?: string; label: string; id: string };
task?: { label: string; id: string; lastTask: string; group?: string };
}

export type WaitOnExitValue = boolean | string | ((exitCode: number) => string);

export interface ICreateContributedTerminalProfileOptions {
icon?: URI | string | { light: URI; dark: URI };
color?: string;
Expand Down
6 changes: 5 additions & 1 deletion src/vs/platform/terminal/common/terminalProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { UriComponents } from 'vs/base/common/uri';
import { ISerializableEnvironmentVariableCollection, ISerializableEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariable';
import { IFixedTerminalDimensions, IRawTerminalTabLayoutInfo, ITerminalEnvironment, ITerminalTabLayoutInfoById, TerminalIcon, TitleEventSource } from 'vs/platform/terminal/common/terminal';
import { IFixedTerminalDimensions, IRawTerminalTabLayoutInfo, ITerminalEnvironment, ITerminalTabLayoutInfoById, TerminalIcon, TerminalType, TitleEventSource, WaitOnExitValue } from 'vs/platform/terminal/common/terminal';

export interface ISingleTerminalConfiguration<T> {
userValue: T | undefined;
Expand Down Expand Up @@ -62,6 +62,10 @@ export interface IProcessDetails {
environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined;
reconnectionOwner?: string;
task?: { label: string; id: string; lastTask: string; group?: string };
waitOnExit?: WaitOnExitValue;
hideFromUser?: boolean;
isFeatureTerminal?: boolean;
type?: TerminalType;
}

export type ITerminalTabLayoutInfoDto = IRawTerminalTabLayoutInfo<IProcessDetails>;
Expand Down
6 changes: 5 additions & 1 deletion src/vs/platform/terminal/node/ptyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,11 @@ export class PtyService extends Disposable implements IPtyService {
fixedDimensions: persistentProcess.fixedDimensions,
environmentVariableCollections: persistentProcess.processLaunchOptions.options.environmentVariableCollections,
reconnectionOwner: persistentProcess.shellLaunchConfig.reconnectionOwner,
task: persistentProcess.shellLaunchConfig.task
task: persistentProcess.shellLaunchConfig.task,
waitOnExit: persistentProcess.shellLaunchConfig.waitOnExit,
hideFromUser: persistentProcess.shellLaunchConfig.hideFromUser,
isFeatureTerminal: persistentProcess.shellLaunchConfig.isFeatureTerminal,
type: persistentProcess.shellLaunchConfig.type
};
}

Expand Down
100 changes: 54 additions & 46 deletions src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IShellLaunchConfig, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfig, TerminalLocation, TerminalSettingId, WaitOnExitValue } from 'vs/platform/terminal/common/terminal';
import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { TaskTerminalStatus } from 'vs/workbench/contrib/tasks/browser/taskTerminalStatus';
import { ProblemCollectorEventKind, ProblemHandlingStrategy, StartStopProblemCollector, WatchingProblemCollector } from 'vs/workbench/contrib/tasks/common/problemCollectors';
import { GroupKind } from 'vs/workbench/contrib/tasks/common/taskConfiguration';
import { CommandOptions, CommandString, ContributedTask, CustomTask, DependsOrder, ICommandConfiguration, IExtensionTaskSource, InMemoryTask, IShellConfiguration, IShellQuotingOptions, ITaskEvent, PanelKind, RevealKind, RevealProblemKind, RuntimeType, ShellQuoting, Task, TaskEvent, TaskEventKind, TaskScope, TaskSettingId, TaskSourceKind } from 'vs/workbench/contrib/tasks/common/tasks';
import { CommandOptions, CommandString, ContributedTask, CustomTask, DependsOrder, ICommandConfiguration, IConfigurationProperties, IExtensionTaskSource, InMemoryTask, IPresentationOptions, IShellConfiguration, IShellQuotingOptions, ITaskEvent, PanelKind, RevealKind, RevealProblemKind, RuntimeType, ShellQuoting, Task, TaskEvent, TaskEventKind, TaskScope, TaskSettingId, TaskSourceKind } from 'vs/workbench/contrib/tasks/common/tasks';
import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService';
import { IResolvedVariables, IResolveSet, ITaskExecuteResult, ITaskResolver, ITaskSummary, ITaskSystem, ITaskSystemInfo, ITaskSystemInfoResolver, ITaskTerminateResponse, TaskError, TaskErrors, TaskExecuteKind, Triggers } from 'vs/workbench/contrib/tasks/common/taskSystem';
import { ITerminalGroupService, ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
Expand Down Expand Up @@ -284,6 +284,11 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
if (this._tasksToReconnect.includes(task._id)) {
this._terminalForTask = terminals.find(t => t.shellLaunchConfig.attachPersistentProcess?.task?.id === task._id);
// Restore the waitOnExit value of the terminal because it may have been a function
// that cannot be persisted in the pty host
if ('command' in task && task.command.presentation && this._terminalForTask) {
this._terminalForTask.waitOnExit = getWaitOnExitValue(task.command.presentation, task.configurationProperties);
}
this.run(task, resolver, trigger);
}
return undefined;
Expand Down Expand Up @@ -541,35 +546,38 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
alreadyResolved = alreadyResolved ?? new Map<string, string>();
const promises: Promise<ITaskSummary>[] = [];
if (task.configurationProperties.dependsOn) {
for (const dependency of task.configurationProperties.dependsOn) {
const dependencyTask = await resolver.resolve(dependency.uri, dependency.task!);
if (dependencyTask) {
this._adoptConfigurationForDependencyTask(dependencyTask, task);
const key = dependencyTask.getMapKey();
let promise = this._activeTasks[key] ? this._getDependencyPromise(this._activeTasks[key]) : undefined;
if (!promise) {
this._fireTaskEvent(TaskEvent.create(TaskEventKind.DependsOnStarted, task));
encounteredDependencies.add(task.getCommonTaskId());
promise = this._executeDependencyTask(dependencyTask, resolver, trigger, encounteredDependencies, alreadyResolved);
}
promises.push(promise);
if (task.configurationProperties.dependsOrder === DependsOrder.sequence) {
const promiseResult = await promise;
if (promiseResult.exitCode === 0) {
promise = Promise.resolve(promiseResult);
} else {
promise = Promise.reject(promiseResult);
break;
if (!this._terminalForTask) {
// we already handle dependent tasks when reconnecting, don't create extras
for (const dependency of task.configurationProperties.dependsOn) {
const dependencyTask = await resolver.resolve(dependency.uri, dependency.task!);
if (dependencyTask) {
this._adoptConfigurationForDependencyTask(dependencyTask, task);
const key = dependencyTask.getMapKey();
let promise = this._activeTasks[key] ? this._getDependencyPromise(this._activeTasks[key]) : undefined;
if (!promise) {
this._fireTaskEvent(TaskEvent.create(TaskEventKind.DependsOnStarted, task));
encounteredDependencies.add(task.getCommonTaskId());
promise = this._executeDependencyTask(dependencyTask, resolver, trigger, encounteredDependencies, alreadyResolved);
}
promises.push(promise);
if (task.configurationProperties.dependsOrder === DependsOrder.sequence) {
const promiseResult = await promise;
if (promiseResult.exitCode === 0) {
promise = Promise.resolve(promiseResult);
} else {
promise = Promise.reject(promiseResult);
break;
}
}
promises.push(promise);
} else {
this._log(nls.localize('dependencyFailed',
'Couldn\'t resolve dependent task \'{0}\' in workspace folder \'{1}\'',
Types.isString(dependency.task) ? dependency.task : JSON.stringify(dependency.task, undefined, 0),
dependency.uri.toString()
));
this._showOutput();
}
promises.push(promise);
} else {
this._log(nls.localize('dependencyFailed',
'Couldn\'t resolve dependent task \'{0}\' in workspace folder \'{1}\'',
Types.isString(dependency.task) ? dependency.task : JSON.stringify(dependency.task, undefined, 0),
dependency.uri.toString()
));
this._showOutput();
}
}
}
Expand Down Expand Up @@ -1065,7 +1073,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
return needsFolderQualification ? task.getQualifiedLabel() : (task.configurationProperties.name || '');
}

private async _createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string | ((exitCode: number) => string)): Promise<IShellLaunchConfig | undefined> {
private async _createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: WaitOnExitValue): Promise<IShellLaunchConfig | undefined> {
let shellLaunchConfig: IShellLaunchConfig;
const isShellCommand = task.command.runtime === RuntimeType.Shell;
const needsFolderQualification = this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
Expand Down Expand Up @@ -1338,24 +1346,10 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
const options = await this._resolveOptions(resolver, task.command.options);
const presentationOptions = task.command.presentation;

let waitOnExit: boolean | string | ((exitCode: number) => string) = false;
if (!presentationOptions) {
throw new Error('Task presentation options should not be undefined here.');
}

if ((presentationOptions.close === undefined) || (presentationOptions.close === false)) {
if ((presentationOptions.reveal !== RevealKind.Never) || !task.configurationProperties.isBackground || (presentationOptions.close === false)) {
if (presentationOptions.panel === PanelKind.New) {
waitOnExit = taskShellIntegrationWaitOnExitSequence(nls.localize('closeTerminal', 'Press any key to close the terminal.'));
} else if (presentationOptions.showReuseMessage) {
waitOnExit = taskShellIntegrationWaitOnExitSequence(nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'));
} else {
waitOnExit = true;
}
}
} else {
waitOnExit = !presentationOptions.close;
}
const waitOnExit = getWaitOnExitValue(presentationOptions, task.configurationProperties);

let command: CommandString | undefined;
let args: CommandString[] | undefined;
Expand Down Expand Up @@ -1384,7 +1378,6 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
return [undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)];
}
}

const prefersSameTerminal = presentationOptions.panel === PanelKind.Dedicated;
const allowsSharedTerminal = presentationOptions.panel === PanelKind.Shared;
const group = presentationOptions.group;
Expand Down Expand Up @@ -1787,6 +1780,21 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
}

function getWaitOnExitValue(presentationOptions: IPresentationOptions, configurationProperties: IConfigurationProperties) {
if ((presentationOptions.close === undefined) || (presentationOptions.close === false)) {
if ((presentationOptions.reveal !== RevealKind.Never) || !configurationProperties.isBackground || (presentationOptions.close === false)) {
if (presentationOptions.panel === PanelKind.New) {
return taskShellIntegrationWaitOnExitSequence(nls.localize('closeTerminal', 'Press any key to close the terminal.'));
} else if (presentationOptions.showReuseMessage) {
return taskShellIntegrationWaitOnExitSequence(nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'));
} else {
return true;
}
}
}
return !presentationOptions.close;
}

function taskShellIntegrationWaitOnExitSequence(message: string): (exitCode: number) => string {
return (exitCode) => {
return `${VSCodeSequence(VSCodeOscPt.CommandFinished, exitCode.toString())}${message}`;
Expand Down
13 changes: 12 additions & 1 deletion src/vs/workbench/contrib/terminal/browser/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { FindReplaceState } from 'vs/editor/contrib/find/browser/findState';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IKeyMods } from 'vs/platform/quickinput/common/quickInput';
import { ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities';
import { IExtensionTerminalProfile, IProcessPropertyMap, IShellIntegration, IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, ProcessPropertyType, TerminalExitReason, TerminalIcon, TerminalLocation, TerminalShellType, TitleEventSource } from 'vs/platform/terminal/common/terminal';
import { IExtensionTerminalProfile, IProcessPropertyMap, IShellIntegration, IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, ProcessPropertyType, TerminalExitReason, TerminalIcon, TerminalLocation, TerminalShellType, TerminalType, TitleEventSource, WaitOnExitValue } from 'vs/platform/terminal/common/terminal';
import { IGenericMarkProperties } from 'vs/platform/terminal/common/terminalProcess';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
Expand Down Expand Up @@ -257,6 +257,11 @@ interface ITerminalEditorInputObject {
readonly icon: TerminalIcon | undefined;
readonly color: string | undefined;
readonly hasChildProcesses?: boolean;
readonly task?: { label: string; id: string; lastTask: string; group?: string; waitOnExit?: WaitOnExitValue };
readonly type?: TerminalType;
readonly isFeatureTerminal?: boolean;
readonly hideFromUser?: boolean;
readonly reconnectionOwner?: string;
}

export interface ISerializedTerminalEditorInput extends ITerminalEditorInputObject {
Expand Down Expand Up @@ -513,6 +518,12 @@ export interface ITerminalInstance {
*/
readonly hasFocus: boolean;

/**
* Get or set the behavior of the terminal when it closes. This was indented only to be called
* after reconnecting to a terminal.
*/
waitOnExit: WaitOnExitValue | undefined;

/**
* An event that fires when the terminal instance's title changes.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ export class TerminalInputSerializer implements IEditorSerializer {
icon: instance.icon,
color: instance.color,
resource: instance.resource.toString(),
hasChildProcesses: instance.hasChildProcesses
hasChildProcesses: instance.hasChildProcesses,
task: instance.shellLaunchConfig.task,
type: instance.shellLaunchConfig.type,
isFeatureTerminal: instance.shellLaunchConfig.isFeatureTerminal,
hideFromUser: instance.shellLaunchConfig.hideFromUser,
};
}
}
Loading

0 comments on commit 1a8d4bf

Please sign in to comment.