diff --git a/cli/unstable_progress_bar.ts b/cli/unstable_progress_bar.ts index 4f13230a5570..13ad4de776ff 100644 --- a/cli/unstable_progress_bar.ts +++ b/cli/unstable_progress_bar.ts @@ -110,6 +110,7 @@ export interface ProgressBarOptions { * const writer = (await Deno.create("./_tmp/output.txt")).writable.getWriter(); * * const bar = new ProgressBar(Deno.stdout.writable, { max: 100_000 }); + * bar.start(); * * for await (const buffer of gen) { * bar.add(buffer.length); @@ -131,6 +132,7 @@ export interface ProgressBarOptions { * return `${x.styledTime()}${x.progressBar}[${x.value}/${x.max} files]`; * }, * }); + * bar.start(); * * for (const x of Array(100)) { * bar.add(1); @@ -143,10 +145,10 @@ export class ProgressBar { #unit: string; #rate: number; #writer: WritableStreamDefaultWriter; - #id: number; - #startTime: number; - #lastTime: number; - #lastValue: number; + #intervalId?: number; + #startTime: number = 0; + #lastTime: number = 0; + #lastValue: number = 0; #value: number; #max: number; @@ -204,9 +206,9 @@ export class ProgressBar { const stream = new TextEncoderStream(); stream.readable .pipeTo(writable, { preventClose: this.#keepOpen }) - .catch(() => clearInterval(this.#id)); + .catch(() => clearInterval(this.#intervalId)); this.#writer = stream.writable.getWriter(); - this.#id = setInterval(() => this.#print(), 1000); + this.#intervalId = setInterval(() => this.#print(), 1000); this.#startTime = performance.now(); this.#lastTime = this.#startTime; this.#lastValue = this.#value; @@ -259,6 +261,25 @@ export class ProgressBar { this.#value += x; } + /** + * Starts the progress bar. + * + * @example Usage + * ```ts ignore + * import { ProgressBar } from "@std/cli/unstable-progress-bar"; + * + * const bar = new ProgressBar(Deno.stdout.writable, { max: 1 }); + * bar.start(); + * ``` + */ + start(): void { + if (this.#intervalId) return; + this.#intervalId = setInterval(() => this.#print(), 200); + this.#startTime = performance.now(); + this.#lastTime = this.#startTime; + this.#lastValue = this.#value; + } + /** * Ends the progress bar and cleans up any lose ends. * @@ -271,7 +292,7 @@ export class ProgressBar { * ``` */ async end(): Promise { - clearInterval(this.#id); + clearInterval(this.#intervalId); await this.#print() .then(() => this.#writer.write(this.#clear ? "\r\u001b[K" : "\n")) .then(() => this.#writer.close()) diff --git a/cli/unstable_progress_bar_stream.ts b/cli/unstable_progress_bar_stream.ts index 2a46af40281e..294e9dc5ab85 100644 --- a/cli/unstable_progress_bar_stream.ts +++ b/cli/unstable_progress_bar_stream.ts @@ -55,6 +55,7 @@ export class ProgressBarStream extends TransformStream { super({ start(_controller) { bar = new ProgressBar(writable, options); + bar.start(); }, transform(chunk, controller) { bar?.add(chunk.length); diff --git a/cli/unstable_progress_bar_test.ts b/cli/unstable_progress_bar_test.ts index eac413eb82a7..36c13ac34867 100644 --- a/cli/unstable_progress_bar_test.ts +++ b/cli/unstable_progress_bar_test.ts @@ -16,6 +16,7 @@ async function* getData( Deno.test("ProgressBar() outputs default result", async () => { const { readable, writable } = new TransformStream(); const bar = new ProgressBar(writable, { max: 10 * 1000 }); + bar.start(); for await (const a of getData(10, 1000)) bar.add(a.length); bar.end().then(() => writable.close()); @@ -65,6 +66,7 @@ Deno.test("ProgressBar() outputs default result", async () => { Deno.test("ProgressBar() can handle a readable.cancel() correctly", async () => { const { readable, writable } = new TransformStream(); const bar = new ProgressBar(writable, { max: 10 * 1000 }); + bar.start(); for await (const a of getData(10, 1000)) bar.add(a.length); bar.end(); @@ -78,6 +80,7 @@ Deno.test("ProgressBar() can remove itself when finished", async () => { max: 10 * 1000, clear: true, }); + bar.start(); for await (const a of getData(10, 1000)) bar.add(a.length); bar.end() @@ -103,6 +106,7 @@ Deno.test("ProgressBar() passes correct values to formatter", async () => { return ""; }, }); + bar.start(); for await (const a of getData(10, 1000)) bar.add(a.length); bar.end(); @@ -119,7 +123,7 @@ Deno.test("ProgressBar() uses correct unit type", async () => { max: 2 ** (10 * ++i), keepOpen: false, }); - + bar.start(); const decoder = new TextDecoder(); for await (const buffer of readable) { assertEquals(decoder.decode(buffer.subarray(-5, -2)), unit); @@ -128,3 +132,48 @@ Deno.test("ProgressBar() uses correct unit type", async () => { bar.end(); } }); + +Deno.test("ProgressBar() handles successive start() calls", () => { + const { writable } = new TransformStream(); + const bar = new ProgressBar(writable, { max: 1 }); + bar.start(); + bar.start(); + bar.end(); +}); + +Deno.test("ProgressBar() handles successive end() calls", () => { + const { writable } = new TransformStream(); + const bar = new ProgressBar(writable, { max: 1 }); + bar.start(); + bar.end(); + bar.end(); +}); + +Deno.test( + "ProgressBar() handles multiple start() and stop() calls", + async () => { + const { readable, writable } = new TransformStream(); + const bar = new ProgressBar(writable, { max: 1 }); + bar.start(); + bar.add(0.5); + bar.end(); + const decoder = new TextDecoder(); + for await (const buffer of readable) { + assertEquals( + decoder.decode(buffer), + "\r\x1b[K[00:00] [#########################-------------------------] [0.00/0.00 KiB] ", + ); + break; + } + bar.start(); + bar.add(0.5); + bar.end(); + for await (const buffer of readable) { + assertEquals( + decoder.decode(buffer), + "\r\x1b[K[00:00] [#########################-------------------------] [0.00/0.00 KiB] ", + ); + break; + } + }, +);