Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions src/console/multiBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default class MultiBar {

private readonly singleBars: SingleBar[] = [];
private renderTimer?: Timer;
private lastRawOutput = '';
private lastOutput = '';
private stopped = false;
private readonly sigwinchHandler?: () => void;
Expand All @@ -55,6 +56,8 @@ export default class MultiBar {
}
this.terminalColumns = this.terminal.columns;
this.terminalRows = this.terminal.rows;
// The terminal size affects line truncation, so force a full re-render
this.lastRawOutput = '';
this.clearAndRender();
};
process.on('SIGWINCH', this.sigwinchHandler);
Expand Down Expand Up @@ -188,7 +191,7 @@ export default class MultiBar {
Math.max(1000 / MultiBar.RENDER_MIN_FPS, 1),
);

const outputLines = this.singleBars
const rawLines = this.singleBars
.flatMap((singleBar) => {
const lines = singleBar
.format()
Expand All @@ -199,14 +202,29 @@ export default class MultiBar {
}
return lines;
})
.slice(0, this.terminalRows - 1)
.map((line) => {
const stripChars = stripAnsi(line).length - this.terminalColumns + 10;
if (stripChars <= 0) {
return `${MultiBar.OUTPUT_PADDING}${line}`;
}
return `${MultiBar.OUTPUT_PADDING}${line.slice(0, line.length - stripChars)}…`;
});
.slice(0, this.terminalRows - 1);

// `format()` must run every render (above) to keep each bar's last output and display state
// fresh, but ANSI-stripping and truncating every line is expensive. Skip that pipeline when
// the bars haven't visibly changed since the last render.
const rawOutput = rawLines.join('\n');
if (rawOutput === this.lastRawOutput && MultiBar.logQueue.length === 0) {
return;
}
this.lastRawOutput = rawOutput;

const outputLines = rawLines.map((line) => {
// The visible (ANSI-stripped) length can only be shorter than the raw length, so a line that
// already fits within the terminal needs no stripping or truncation.
if (line.length <= this.terminalColumns - 10) {
return `${MultiBar.OUTPUT_PADDING}${line}`;
}
const stripChars = stripAnsi(line).length - this.terminalColumns + 10;
if (stripChars <= 0) {
return `${MultiBar.OUTPUT_PADDING}${line}`;
}
return `${MultiBar.OUTPUT_PADDING}${line.slice(0, line.length - stripChars)}…`;
});
const output = outputLines.length > 0 ? `${outputLines.join('\n')}\n` : '';

if (output === this.lastOutput && MultiBar.logQueue.length === 0) {
Expand Down
24 changes: 21 additions & 3 deletions src/console/singleBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ export default class SingleBar extends ProgressBar {
private finishedMessage?: string;

private lastOutput?: string;

// Memoize the formatted `completed`/`total` values. `total` is constant for the life of the bar,
// and `completed` repeats across the renders where it hasn't advanced yet, so a single-slot cache
// per value avoids re-running the (potentially expensive) progress formatter every render.
private lastFormattedCompletedValue?: number;
private lastFormattedCompleted = '';
private lastFormattedTotalValue?: number;
private lastFormattedTotal = '';

private valueTimeBuffer: number[][] = [];
private valueTimeBufferIndex = 0;
private valueTimeBufferSize = 0;
Expand Down Expand Up @@ -97,7 +106,6 @@ export default class SingleBar extends ProgressBar {
addChildBar(options?: SingleBarOptions): ProgressBar {
return this.multiBar.addSingleBar(
{
displayDelay: 2000,
indentSize: this.indentSize + (this.symbol?.symbol ? 2 : 0),
progressBarSizeMultiplier: this.progressBarSizeMultiplier / 2,
showProgressNewline: false,
Expand Down Expand Up @@ -291,8 +299,18 @@ export default class SingleBar extends ProgressBar {
bar += CHALK_PROGRESS_INCOMPLETE(BAR_INCOMPLETE_CHAR.repeat(Math.max(incompleteSize, 0)));
bar += ' ';

const formattedCompleted = this.progressFormatter(this.completed);
const formattedTotal = this.progressFormatter(this.total);
if (this.completed !== this.lastFormattedCompletedValue) {
this.lastFormattedCompletedValue = this.completed;
this.lastFormattedCompleted = this.progressFormatter(this.completed);
}
const formattedCompleted = this.lastFormattedCompleted;

if (this.total !== this.lastFormattedTotalValue) {
this.lastFormattedTotalValue = this.total;
this.lastFormattedTotal = this.progressFormatter(this.total);
}
const formattedTotal = this.lastFormattedTotal;

const paddedCompleted = formattedCompleted.padStart(
Math.max(formattedTotal.length, this.indentSize > 0 ? 7 : 0),
' ',
Expand Down
Loading