From dc4744f69af8c6ecafbbb909d4a2f1d9ba09b3b8 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 21 Jul 2021 11:33:07 +0200 Subject: [PATCH] Add line data debouncing for background tasks Part of #129080 --- .../tasks/browser/terminalTaskSystem.ts | 78 +++++++++++++++---- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index e7546271f39..21fb6f55f4b 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -14,7 +14,7 @@ import { IStringDictionary, values } from 'vs/base/common/collections'; import { LinkedMap, Touch } from 'vs/base/common/map'; import Severity from 'vs/base/common/severity'; import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { isUNC } from 'vs/base/common/extpath'; import { IFileService } from 'vs/platform/files/common/files'; @@ -765,6 +765,65 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } + private setupBackgroundTaskOnLineData(terminal: ITerminalInstance, watchingProblemMatcher: WatchingProblemCollector, task: CustomTask | ContributedTask): IDisposable { + let delayer: Async.Delayer | undefined = undefined; + let skipLine: boolean = (!!task.command.presentation && task.command.presentation.echo); + let lastEventTime = new Date().getTime(); + let lineQueue: string[] = []; + let doneDebouncing = false; + // If "enough" time (3 seconds) has passed without more line data, start processing and stop debouncing. + let startProcessingTimeout: NodeJS.Timeout | undefined; + return terminal.onLineData((line) => { + if (skipLine) { + skipLine = false; + return; + } + const currentTime = new Date().getTime(); + if (!doneDebouncing && ((currentTime - lastEventTime) < 1500)) { + if (startProcessingTimeout) { + clearTimeout(startProcessingTimeout); + } + if (lineQueue.length > 300) { + lineQueue = lineQueue.slice(100, lineQueue.length); + } + lineQueue.push(line); + startProcessingTimeout = setTimeout(() => { + if (doneDebouncing) { + return; + } + doneDebouncing = true; + for (const queuedLine of lineQueue) { + watchingProblemMatcher.processLine(queuedLine); + } + lineQueue = []; + if (startProcessingTimeout) { + clearTimeout(startProcessingTimeout); + } + }, 3000); + lastEventTime = new Date().getTime(); + return; + } else if (!doneDebouncing) { + doneDebouncing = true; + if (startProcessingTimeout) { + clearTimeout(startProcessingTimeout); + } + for (const queuedLine of lineQueue) { + watchingProblemMatcher.processLine(queuedLine); + } + lineQueue = []; + } + + watchingProblemMatcher.processLine(line); + if (!delayer) { + delayer = new Async.Delayer(3000); + } + delayer.trigger(() => { + watchingProblemMatcher.forceDelivery(); + delayer = undefined; + }); + }); + } + private async executeInTerminal(task: CustomTask | ContributedTask, trigger: string, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder | undefined): Promise { let terminal: ITerminalInstance | undefined = undefined; let executedCommand: string | undefined = undefined; @@ -807,7 +866,6 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } })); watchingProblemMatcher.aboutToStart(); - let delayer: Async.Delayer | undefined = undefined; [terminal, executedCommand, error] = await this.createTerminal(task, resolver, workspaceFolder); if (error) { @@ -828,21 +886,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { this.logService.error('Task terminal process never got ready'); }); this.fireTaskEvent(TaskEvent.create(TaskEventKind.Start, task, terminal.instanceId)); - let skipLine: boolean = (!!task.command.presentation && task.command.presentation.echo); - const onData = terminal.onLineData((line) => { - if (skipLine) { - skipLine = false; - return; - } - watchingProblemMatcher.processLine(line); - if (!delayer) { - delayer = new Async.Delayer(3000); - } - delayer.trigger(() => { - watchingProblemMatcher.forceDelivery(); - delayer = undefined; - }); - }); + const onData = this.setupBackgroundTaskOnLineData(terminal, watchingProblemMatcher, task); promise = new Promise((resolve, reject) => { const onExit = terminal!.onExit((exitCode) => { onData.dispose();