From 4c6243176ca5e66635fde4107e217f1b9666eb62 Mon Sep 17 00:00:00 2001 From: David Simpson <45690499+davegarthsimpson@users.noreply.github.com> Date: Tue, 14 Jun 2022 13:00:20 +0200 Subject: [PATCH] Output panel optimisation (#1058) * test interval for output panel * create buffer provider * output panel buffer corrections * output buffer cleanup * code cleanup --- .../src/node/core-service-impl.ts | 76 +++++++++++++------ .../src/node/utils/simple-buffer.ts | 31 ++++++++ 2 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 arduino-ide-extension/src/node/utils/simple-buffer.ts diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts index 00d6ca5e..412e6480 100644 --- a/arduino-ide-extension/src/node/core-service-impl.ts +++ b/arduino-ide-extension/src/node/core-service-impl.ts @@ -25,7 +25,9 @@ import { firstToUpperCase, firstToLowerCase } from '../common/utils'; import { Port } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb'; import { nls } from '@theia/core'; import { MonitorManager } from './monitor-manager'; +import { SimpleBuffer } from './utils/simple-buffer'; +const FLUSH_OUTPUT_MESSAGES_TIMEOUT_MS = 32; @injectable() export class CoreServiceImpl extends CoreClientAware implements CoreService { @inject(ResponseService) @@ -73,18 +75,25 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { this.mergeSourceOverrides(compileReq, options); const result = client.compile(compileReq); + + const compileBuffer = new SimpleBuffer( + this.flushOutputPanelMessages.bind(this), + FLUSH_OUTPUT_MESSAGES_TIMEOUT_MS + ); try { await new Promise((resolve, reject) => { result.on('data', (cr: CompileResponse) => { - this.responseService.appendToOutput({ - chunk: Buffer.from(cr.getOutStream_asU8()).toString(), - }); - this.responseService.appendToOutput({ - chunk: Buffer.from(cr.getErrStream_asU8()).toString(), - }); + compileBuffer.addChunk(cr.getOutStream_asU8()); + compileBuffer.addChunk(cr.getErrStream_asU8()); + }); + result.on('error', (error) => { + compileBuffer.clearFlushInterval(); + reject(error); + }); + result.on('end', () => { + compileBuffer.clearFlushInterval(); + resolve(); }); - result.on('error', (error) => reject(error)); - result.on('end', () => resolve()); }); this.responseService.appendToOutput({ chunk: '\n--------------------------\nCompilation complete.\n', @@ -174,18 +183,24 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { const result = responseHandler(client, req); + const uploadBuffer = new SimpleBuffer( + this.flushOutputPanelMessages.bind(this), + FLUSH_OUTPUT_MESSAGES_TIMEOUT_MS + ); try { await new Promise((resolve, reject) => { result.on('data', (resp: UploadResponse) => { - this.responseService.appendToOutput({ - chunk: Buffer.from(resp.getOutStream_asU8()).toString(), - }); - this.responseService.appendToOutput({ - chunk: Buffer.from(resp.getErrStream_asU8()).toString(), - }); + uploadBuffer.addChunk(resp.getOutStream_asU8()); + uploadBuffer.addChunk(resp.getErrStream_asU8()); + }); + result.on('error', (error) => { + uploadBuffer.clearFlushInterval(); + reject(error); + }); + result.on('end', () => { + uploadBuffer.clearFlushInterval(); + resolve(); }); - result.on('error', (error) => reject(error)); - result.on('end', () => resolve()); }); this.responseService.appendToOutput({ chunk: @@ -238,18 +253,25 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { burnReq.setVerify(options.verify); burnReq.setVerbose(options.verbose); const result = client.burnBootloader(burnReq); + + const bootloaderBuffer = new SimpleBuffer( + this.flushOutputPanelMessages.bind(this), + FLUSH_OUTPUT_MESSAGES_TIMEOUT_MS + ); try { await new Promise((resolve, reject) => { result.on('data', (resp: BurnBootloaderResponse) => { - this.responseService.appendToOutput({ - chunk: Buffer.from(resp.getOutStream_asU8()).toString(), - }); - this.responseService.appendToOutput({ - chunk: Buffer.from(resp.getErrStream_asU8()).toString(), - }); + bootloaderBuffer.addChunk(resp.getOutStream_asU8()); + bootloaderBuffer.addChunk(resp.getErrStream_asU8()); + }); + result.on('error', (error) => { + bootloaderBuffer.clearFlushInterval(); + reject(error); + }); + result.on('end', () => { + bootloaderBuffer.clearFlushInterval(); + resolve(); }); - result.on('error', (error) => reject(error)); - result.on('end', () => resolve()); }); } catch (e) { const errorMessage = nls.localize( @@ -281,4 +303,10 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { } } } + + private flushOutputPanelMessages(chunk: string): void { + this.responseService.appendToOutput({ + chunk, + }); + } } diff --git a/arduino-ide-extension/src/node/utils/simple-buffer.ts b/arduino-ide-extension/src/node/utils/simple-buffer.ts new file mode 100644 index 00000000..3f5acd50 --- /dev/null +++ b/arduino-ide-extension/src/node/utils/simple-buffer.ts @@ -0,0 +1,31 @@ +export class SimpleBuffer { + private chunks: Uint8Array[] = []; + + private flushInterval?: NodeJS.Timeout; + + constructor(onFlush: (chunk: string) => void, flushTimeout: number) { + this.flushInterval = setInterval(() => { + if (this.chunks.length > 0) { + const chunkString = Buffer.concat(this.chunks).toString(); + this.clearChunks(); + + onFlush(chunkString); + } + }, flushTimeout); + } + + public addChunk(chunk: Uint8Array): void { + this.chunks.push(chunk); + } + + private clearChunks(): void { + this.chunks = []; + } + + public clearFlushInterval(): void { + this.clearChunks(); + + clearInterval(this.flushInterval); + this.flushInterval = undefined; + } +}