in patched-vscode/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts [808:1057]
private async _executeInTerminal(task: CustomTask | ContributedTask, trigger: string, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder | undefined): Promise<ITaskSummary> {
let terminal: ITerminalInstance | undefined = undefined;
let error: TaskError | undefined = undefined;
let promise: Promise<ITaskSummary> | undefined = undefined;
if (task.configurationProperties.isBackground) {
const problemMatchers = await this._resolveMatchers(resolver, task.configurationProperties.problemMatchers);
const watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this._markerService, this._modelService, this._fileService);
if ((problemMatchers.length > 0) && !watchingProblemMatcher.isWatching()) {
this._appendOutput(nls.localize('TerminalTaskSystem.nonWatchingMatcher', 'Task {0} is a background task but uses a problem matcher without a background pattern', task._label));
this._showOutput();
}
const toDispose = new DisposableStore();
let eventCounter: number = 0;
const mapKey = task.getMapKey();
toDispose.add(watchingProblemMatcher.onDidStateChange((event) => {
if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) {
eventCounter++;
this._busyTasks[mapKey] = task;
this._fireTaskEvent(TaskEvent.general(TaskEventKind.Active, task, terminal?.instanceId));
} else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) {
eventCounter--;
if (this._busyTasks[mapKey]) {
delete this._busyTasks[mapKey];
}
this._fireTaskEvent(TaskEvent.general(TaskEventKind.Inactive, task, terminal?.instanceId));
if (eventCounter === 0) {
if ((watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity &&
(watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) {
const reveal = task.command.presentation!.reveal;
const revealProblems = task.command.presentation!.revealProblems;
if (revealProblems === RevealProblemKind.OnProblem) {
this._viewsService.openView(Markers.MARKERS_VIEW_ID, true);
} else if (reveal === RevealKind.Silent) {
this._terminalService.setActiveInstance(terminal!);
this._terminalGroupService.showPanel(false);
}
}
}
}
}));
watchingProblemMatcher.aboutToStart();
let delayer: Async.Delayer<any> | undefined = undefined;
[terminal, error] = await this._createTerminal(task, resolver, workspaceFolder);
if (error) {
return Promise.reject(new Error((<TaskError>error).message));
}
if (!terminal) {
return Promise.reject(new Error(`Failed to create terminal for task ${task._label}`));
}
this._terminalStatusManager.addTerminal(task, terminal, watchingProblemMatcher);
let processStartedSignaled = false;
terminal.processReady.then(() => {
if (!processStartedSignaled) {
this._fireTaskEvent(TaskEvent.processStarted(task, terminal!.instanceId, terminal!.processId!));
processStartedSignaled = true;
}
}, (_error) => {
this._logService.error('Task terminal process never got ready');
});
this._fireTaskEvent(TaskEvent.start(task, terminal.instanceId, resolver.values));
let onData: IDisposable | undefined;
if (problemMatchers.length) {
// prevent https://github.com/microsoft/vscode/issues/174511 from happening
onData = terminal.onLineData((line) => {
watchingProblemMatcher.processLine(line);
if (!delayer) {
delayer = new Async.Delayer(3000);
}
delayer.trigger(() => {
watchingProblemMatcher.forceDelivery();
delayer = undefined;
});
});
}
promise = new Promise<ITaskSummary>((resolve, reject) => {
const onExit = terminal!.onExit((terminalLaunchResult) => {
const exitCode = typeof terminalLaunchResult === 'number' ? terminalLaunchResult : terminalLaunchResult?.code;
onData?.dispose();
onExit.dispose();
const key = task.getMapKey();
if (this._busyTasks[mapKey]) {
delete this._busyTasks[mapKey];
}
this._removeFromActiveTasks(task);
this._fireTaskEvent(TaskEvent.changed());
if (terminalLaunchResult !== undefined) {
// Only keep a reference to the terminal if it is not being disposed.
switch (task.command.presentation!.panel) {
case PanelKind.Dedicated:
this._sameTaskTerminals[key] = terminal!.instanceId.toString();
break;
case PanelKind.Shared:
this._idleTaskTerminals.set(key, terminal!.instanceId.toString(), Touch.AsOld);
break;
}
}
const reveal = task.command.presentation!.reveal;
if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity &&
(watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) {
try {
this._terminalService.setActiveInstance(terminal!);
this._terminalGroupService.showPanel(false);
} catch (e) {
// If the terminal has already been disposed, then setting the active instance will fail. #99828
// There is nothing else to do here.
}
}
watchingProblemMatcher.done();
watchingProblemMatcher.dispose();
if (!processStartedSignaled) {
this._fireTaskEvent(TaskEvent.processStarted(task, terminal!.instanceId, terminal!.processId!));
processStartedSignaled = true;
}
this._fireTaskEvent(TaskEvent.processEnded(task, terminal!.instanceId, exitCode));
for (let i = 0; i < eventCounter; i++) {
this._fireTaskEvent(TaskEvent.general(TaskEventKind.Inactive, task, terminal!.instanceId));
}
eventCounter = 0;
this._fireTaskEvent(TaskEvent.general(TaskEventKind.End, task));
toDispose.dispose();
resolve({ exitCode: exitCode ?? undefined });
});
});
if (trigger === Triggers.reconnect && !!terminal.xterm) {
const bufferLines = [];
const bufferReverseIterator = terminal.xterm.getBufferReverseIterator();
const startRegex = new RegExp(watchingProblemMatcher.beginPatterns.map(pattern => pattern.source).join('|'));
for (const nextLine of bufferReverseIterator) {
bufferLines.push(nextLine);
if (startRegex.test(nextLine)) {
break;
}
}
let delayer: Async.Delayer<any> | undefined = undefined;
for (let i = bufferLines.length - 1; i >= 0; i--) {
watchingProblemMatcher.processLine(bufferLines[i]);
if (!delayer) {
delayer = new Async.Delayer(3000);
}
delayer.trigger(() => {
watchingProblemMatcher.forceDelivery();
delayer = undefined;
});
}
}
} else {
[terminal, error] = await this._createTerminal(task, resolver, workspaceFolder);
if (error) {
return Promise.reject(new Error((<TaskError>error).message));
}
if (!terminal) {
return Promise.reject(new Error(`Failed to create terminal for task ${task._label}`));
}
this._fireTaskEvent(TaskEvent.start(task, terminal.instanceId, resolver.values));
const mapKey = task.getMapKey();
this._busyTasks[mapKey] = task;
this._fireTaskEvent(TaskEvent.general(TaskEventKind.Active, task, terminal.instanceId));
const problemMatchers = await this._resolveMatchers(resolver, task.configurationProperties.problemMatchers);
const startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this._markerService, this._modelService, ProblemHandlingStrategy.Clean, this._fileService);
this._terminalStatusManager.addTerminal(task, terminal, startStopProblemMatcher);
let processStartedSignaled = false;
terminal.processReady.then(() => {
if (!processStartedSignaled) {
this._fireTaskEvent(TaskEvent.processStarted(task, terminal!.instanceId, terminal!.processId!));
processStartedSignaled = true;
}
}, (_error) => {
// The process never got ready. Need to think how to handle this.
});
const onData = terminal.onLineData((line) => {
startStopProblemMatcher.processLine(line);
});
promise = new Promise<ITaskSummary>((resolve, reject) => {
const onExit = terminal!.onExit((terminalLaunchResult) => {
const exitCode = typeof terminalLaunchResult === 'number' ? terminalLaunchResult : terminalLaunchResult?.code;
onExit.dispose();
const key = task.getMapKey();
this._removeFromActiveTasks(task);
this._fireTaskEvent(TaskEvent.changed());
if (terminalLaunchResult !== undefined) {
// Only keep a reference to the terminal if it is not being disposed.
switch (task.command.presentation!.panel) {
case PanelKind.Dedicated:
this._sameTaskTerminals[key] = terminal!.instanceId.toString();
break;
case PanelKind.Shared:
this._idleTaskTerminals.set(key, terminal!.instanceId.toString(), Touch.AsOld);
break;
}
}
const reveal = task.command.presentation!.reveal;
const revealProblems = task.command.presentation!.revealProblems;
const revealProblemPanel = terminal && (revealProblems === RevealProblemKind.OnProblem) && (startStopProblemMatcher.numberOfMatches > 0);
if (revealProblemPanel) {
this._viewsService.openView(Markers.MARKERS_VIEW_ID);
} else if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity &&
(startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) {
try {
this._terminalService.setActiveInstance(terminal);
this._terminalGroupService.showPanel(false);
} catch (e) {
// If the terminal has already been disposed, then setting the active instance will fail. #99828
// There is nothing else to do here.
}
}
// Hack to work around #92868 until terminal is fixed.
setTimeout(() => {
onData.dispose();
startStopProblemMatcher.done();
startStopProblemMatcher.dispose();
}, 100);
if (!processStartedSignaled && terminal) {
this._fireTaskEvent(TaskEvent.processStarted(task, terminal.instanceId, terminal.processId!));
processStartedSignaled = true;
}
this._fireTaskEvent(TaskEvent.processEnded(task, terminal?.instanceId, exitCode ?? undefined));
if (this._busyTasks[mapKey]) {
delete this._busyTasks[mapKey];
}
this._fireTaskEvent(TaskEvent.general(TaskEventKind.Inactive, task, terminal?.instanceId));
this._fireTaskEvent(TaskEvent.general(TaskEventKind.End, task, terminal?.instanceId));
resolve({ exitCode: exitCode ?? undefined });
});
});
}
const showProblemPanel = task.command.presentation && (task.command.presentation.revealProblems === RevealProblemKind.Always);
if (showProblemPanel) {
this._viewsService.openView(Markers.MARKERS_VIEW_ID);
} else if (task.command.presentation && (task.command.presentation.focus || task.command.presentation.reveal === RevealKind.Always)) {
this._terminalService.setActiveInstance(terminal);
await this._terminalService.revealActiveTerminal();
if (task.command.presentation.focus) {
this._terminalService.focusActiveInstance();
}
}
this._activeTasks[task.getMapKey()].terminal = terminal;
this._fireTaskEvent(TaskEvent.changed());
return promise;
}