Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts')
-rw-r--r--src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts190
1 files changed, 120 insertions, 70 deletions
diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts
index 7cfab363b6d..22c53792466 100644
--- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts
+++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts
@@ -3,58 +3,52 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import * as path from 'vs/base/common/path';
-import * as nls from 'vs/nls';
-import * as Objects from 'vs/base/common/objects';
-import * as Types from 'vs/base/common/types';
-import * as Platform from 'vs/base/common/platform';
import * as Async from 'vs/base/common/async';
-import * as resources from 'vs/base/common/resources';
import { IStringDictionary } from 'vs/base/common/collections';
+import { Emitter, Event } from 'vs/base/common/event';
+import { isUNC } from 'vs/base/common/extpath';
+import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { LinkedMap, Touch } from 'vs/base/common/map';
+import * as Objects from 'vs/base/common/objects';
+import * as path from 'vs/base/common/path';
+import * as Platform from 'vs/base/common/platform';
+import * as resources from 'vs/base/common/resources';
import Severity from 'vs/base/common/severity';
-import { Event, Emitter } from 'vs/base/common/event';
-import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
-import { isUNC } from 'vs/base/common/extpath';
+import * as Types from 'vs/base/common/types';
+import * as nls from 'vs/nls';
+import { IModelService } from 'vs/editor/common/services/model';
import { IFileService } from 'vs/platform/files/common/files';
import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
-import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
-import { IModelService } from 'vs/editor/common/services/model';
-import { ProblemMatcher, ProblemMatcherRegistry /*, ProblemPattern, getResource */ } from 'vs/workbench/contrib/tasks/common/problemMatcher';
+import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { Markers } from 'vs/workbench/contrib/markers/common/markers';
+import { ProblemMatcher, ProblemMatcherRegistry /*, ProblemPattern, getResource */ } from 'vs/workbench/contrib/tasks/common/problemMatcher';
-import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
-import { ITerminalProfileResolverService, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
-import { ITerminalService, ITerminalInstance, ITerminalGroupService } from 'vs/workbench/contrib/terminal/browser/terminal';
-import { IOutputService } from 'vs/workbench/services/output/common/output';
-import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind, ProblemHandlingStrategy } from 'vs/workbench/contrib/tasks/common/problemCollectors';
-import {
- Task, CustomTask, ContributedTask, RevealKind, CommandOptions, IShellConfiguration, RuntimeType, PanelKind,
- TaskEvent, TaskEventKind, IShellQuotingOptions, ShellQuoting, CommandString, ICommandConfiguration, IExtensionTaskSource, TaskScope, RevealProblemKind, DependsOrder, TaskSourceKind, InMemoryTask, ITaskEvent, TaskSettingId
-} from 'vs/workbench/contrib/tasks/common/tasks';
-import {
- ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver,
- Triggers, ITaskTerminateResponse, ITaskSystemInfoResolver, ITaskSystemInfo, IResolveSet, IResolvedVariables
-} from 'vs/workbench/contrib/tasks/common/taskSystem';
-import { URI } from 'vs/base/common/uri';
-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+import { Codicon } from 'vs/base/common/codicons';
import { Schemas } from 'vs/base/common/network';
-import { IPathService } from 'vs/workbench/services/path/common/pathService';
-import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views';
-import { ILogService } from 'vs/platform/log/common/log';
+import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
-import { IShellLaunchConfig, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
-import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy';
-import { TaskTerminalStatus } from 'vs/workbench/contrib/tasks/browser/taskTerminalStatus';
-import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService';
-import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
+import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
-import { ThemeIcon } from 'vs/platform/theme/common/themeService';
+import { IShellLaunchConfig, TerminalLocation, TerminalSettingId } 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 { Codicon } from 'vs/base/common/codicons';
+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 { 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';
import { VSCodeOscProperty, VSCodeOscPt, VSCodeSequence } from 'vs/workbench/contrib/terminal/browser/terminalEscapeSequences';
+import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy';
+import { ITerminalProfileResolverService, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
+import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
+import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+import { IOutputService } from 'vs/workbench/services/output/common/output';
+import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
+import { IPathService } from 'vs/workbench/services/path/common/pathService';
interface ITerminalData {
terminal: ITerminalInstance;
@@ -69,6 +63,8 @@ interface IActiveTerminalData {
state?: TaskEventKind;
}
+const ReconnectionType = 'Task';
+
class InstanceManager {
private _currentInstances: number = 0;
private _counter: number = 0;
@@ -196,6 +192,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
private _terminals: IStringDictionary<ITerminalData>;
private _idleTaskTerminals: LinkedMap<string, string>;
private _sameTaskTerminals: IStringDictionary<string>;
+ private _terminalForTask: ITerminalInstance | undefined;
private _taskSystemInfoResolver: ITaskSystemInfoResolver;
private _lastTask: VerifiedTask | undefined;
// Should always be set in run
@@ -205,7 +202,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
private _previousTerminalInstance: ITerminalInstance | undefined;
private _terminalStatusManager: TaskTerminalStatus;
private _terminalCreationQueue: Promise<ITerminalInstance | void> = Promise.resolve();
-
+ private _hasReconnected: boolean = false;
+ private _tasksToReconnect: string[] = [];
private readonly _onDidStateChange: Emitter<ITaskEvent>;
get taskShellIntegrationStartSequence(): string {
@@ -245,12 +243,24 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
this._terminals = Object.create(null);
this._idleTaskTerminals = new LinkedMap<string, string>();
this._sameTaskTerminals = Object.create(null);
-
this._onDidStateChange = new Emitter();
this._taskSystemInfoResolver = taskSystemInfoResolver;
this._register(this._terminalStatusManager = new TaskTerminalStatus(taskService));
}
+ private _reconnectToTerminals(terminals: ITerminalInstance[]): void {
+ for (const terminal of terminals) {
+ const taskForTerminal = terminal.shellLaunchConfig.attachPersistentProcess?.task;
+ if (taskForTerminal?.id && taskForTerminal?.lastTask) {
+ this._tasksToReconnect.push(taskForTerminal.id);
+ this._terminals[terminal.instanceId] = { terminal, lastTask: taskForTerminal.lastTask, group: taskForTerminal.group };
+ } else {
+ this._logService.trace(`Could not reconnect to terminal ${terminal.instanceId} with process details ${terminal.shellLaunchConfig.attachPersistentProcess}`);
+ }
+ }
+ this._hasReconnected = true;
+ }
+
public get onDidStateChange(): Event<ITaskEvent> {
return this._onDidStateChange.event;
}
@@ -263,6 +273,22 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
this._outputService.showChannel(this._outputChannelId, true);
}
+ public reconnect(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult | undefined {
+ const terminals = this._terminalService.getReconnectedTerminals(ReconnectionType);
+ if (!terminals || terminals?.length === 0) {
+ return;
+ }
+ if (!this._hasReconnected && terminals && terminals.length > 0) {
+ this._reviveTerminals();
+ this._reconnectToTerminals(terminals);
+ }
+ if (this._tasksToReconnect.includes(task._id)) {
+ this._terminalForTask = terminals.find(t => t.shellLaunchConfig.attachPersistentProcess?.task?.id === task._id);
+ this.run(task, resolver, trigger);
+ }
+ return undefined;
+ }
+
public run(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult {
task = task.clone(); // A small amount of task state is stored in the task (instance) and tasks passed in to run may have that set already.
const recentTaskKey = task.getRecentlyUsedKey() ?? '';
@@ -280,7 +306,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
try {
- const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this._executeTask(task, resolver, trigger, new Set()) };
+ const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this._executeTask(task, resolver, trigger, new Set(), undefined) };
executeResult.promise.then(summary => {
this._lastTask = this._currentTask;
});
@@ -430,12 +456,14 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
}
- private _removeFromActiveTasks(task: Task): void {
- if (!this._activeTasks[task.getMapKey()]) {
+ private _removeFromActiveTasks(task: Task | string): void {
+ const key = typeof task === 'string' ? task : task.getMapKey();
+ if (!this._activeTasks[key]) {
return;
}
- delete this._activeTasks[task.getMapKey()];
- this._removeInstances(task);
+ const taskToRemove = this._activeTasks[key];
+ delete this._activeTasks[key];
+ this._removeInstances(taskToRemove.task);
}
private _fireTaskEvent(event: ITaskEvent) {
@@ -853,7 +881,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}));
watchingProblemMatcher.aboutToStart();
let delayer: Async.Delayer<any> | undefined = undefined;
- [terminal, error] = await this._createTerminal(task, resolver, workspaceFolder);
+ [terminal, error] = this._terminalForTask ? [this._terminalForTask, undefined] : await this._createTerminal(task, resolver, workspaceFolder);
+ this._terminalForTask = undefined;
if (error) {
return Promise.reject(new Error((<TaskError>error).message));
@@ -935,7 +964,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
});
});
} else {
- [terminal, error] = await this._createTerminal(task, resolver, workspaceFolder);
+ [terminal, error] = this._terminalForTask ? [this._terminalForTask, undefined] : await this._createTerminal(task, resolver, workspaceFolder);
+ this._terminalForTask = undefined;
if (error) {
return Promise.reject(new Error((<TaskError>error).message));
@@ -1040,7 +1070,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
const isShellCommand = task.command.runtime === RuntimeType.Shell;
const needsFolderQualification = this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
const terminalName = this._createTerminalName(task);
- const type = 'Task';
+ const type = ReconnectionType;
const originalCommand = task.command.name;
if (isShellCommand) {
let os: Platform.OperatingSystem;
@@ -1269,6 +1299,40 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
return createdTerminal;
}
+ private _reviveTerminals(): void {
+ if (Object.entries(this._terminals).length > 0) {
+ return;
+ }
+ const terminals = this._terminalService.getReconnectedTerminals(ReconnectionType)?.filter(t => !t.isDisposed);
+ if (!terminals?.length) {
+ return;
+ }
+ for (const terminal of terminals) {
+ const task = terminal.shellLaunchConfig.attachPersistentProcess?.task;
+ if (!task) {
+ continue;
+ }
+ const terminalData = { lastTask: task.lastTask, group: task.group, terminal };
+ this._terminals[terminal.instanceId] = terminalData;
+ terminal.onDisposed(() => this._deleteTaskAndTerminal(terminal, terminalData));
+ }
+ }
+
+ private _deleteTaskAndTerminal(terminal: ITerminalInstance, terminalData: ITerminalData): void {
+ delete this._terminals[terminal.instanceId];
+ delete this._sameTaskTerminals[terminalData.lastTask];
+ this._idleTaskTerminals.delete(terminalData.lastTask);
+ // Delete the task now as a work around for cases when the onExit isn't fired.
+ // This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected.
+ // For correct terminal re-use, the task needs to be deleted immediately.
+ // Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate.
+ const mapKey = terminalData.lastTask;
+ this._removeFromActiveTasks(mapKey);
+ if (this._busyTasks[mapKey]) {
+ delete this._busyTasks[mapKey];
+ }
+ }
+
private async _createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder | undefined): Promise<[ITerminalInstance | undefined, TaskError | undefined]> {
const platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform;
const options = await this._resolveOptions(resolver, task.command.options);
@@ -1308,7 +1372,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}, 'Executing task: {0}', task._label), { excludeLeadingNewLine: true }) : undefined,
isFeatureTerminal: true,
icon: task.configurationProperties.icon?.id ? ThemeIcon.fromId(task.configurationProperties.icon.id) : undefined,
- color: task.configurationProperties.icon?.color || undefined,
+ color: task.configurationProperties.icon?.color || undefined
};
} else {
const resolvedResult: { command: CommandString; args: CommandString[] } = await this._resolveCommandAndArgs(resolver, task.command);
@@ -1368,28 +1432,14 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
this._terminalCreationQueue = this._terminalCreationQueue.then(() => this._doCreateTerminal(group, launchConfigs!));
- const result: ITerminalInstance = (await this._terminalCreationQueue)!;
-
- const terminalKey = result.instanceId.toString();
- result.onDisposed((terminal) => {
- const terminalData = this._terminals[terminalKey];
- if (terminalData) {
- delete this._terminals[terminalKey];
- delete this._sameTaskTerminals[terminalData.lastTask];
- this._idleTaskTerminals.delete(terminalData.lastTask);
- // Delete the task now as a work around for cases when the onExit isn't fired.
- // This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected.
- // For correct terminal re-use, the task needs to be deleted immediately.
- // Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate.
- const mapKey = task.getMapKey();
- this._removeFromActiveTasks(task);
- if (this._busyTasks[mapKey]) {
- delete this._busyTasks[mapKey];
- }
- }
- });
- this._terminals[terminalKey] = { terminal: result, lastTask: taskKey, group };
- return [result, undefined];
+ const terminal: ITerminalInstance = (await this._terminalCreationQueue)!;
+ terminal.shellLaunchConfig.task = { lastTask: taskKey, group, label: task._label, id: task._id };
+ terminal.shellLaunchConfig.reconnectionOwner = ReconnectionType;
+ const terminalKey = terminal.instanceId.toString();
+ const terminalData = { terminal: terminal, lastTask: taskKey, group };
+ terminal.onDisposed(() => this._deleteTaskAndTerminal(terminal, terminalData));
+ this._terminals[terminalKey] = terminalData;
+ return [terminal, undefined];
}
private _buildShellCommandLine(platform: Platform.Platform, shellExecutable: string, shellOptions: IShellConfiguration | undefined, command: CommandString, originalCommand: CommandString | undefined, args: CommandString[]): string {