@@ -27,54 +27,74 @@ function getIcon(task: Task) {
2727 }
2828}
2929
30- function render ( tasks : Task [ ] ) : string {
30+ function render ( tasks : Task [ ] , width : number ) : string {
3131 const all = getTests ( tasks )
32- const output : string [ ] = [ ]
32+ let currentIcon = pending
33+ let currentTasks = 0
34+ let previousLineWidth = 0
35+ let output = ''
3336
3437 // The log-update uses various ANSI helper utilities, e.g. ansi-warp, ansi-slice,
3538 // when printing. Passing it hundreds of single characters containing ANSI codes reduces
3639 // performances. We can optimize it by reducing amount of ANSI codes, e.g. by coloring
3740 // multiple tasks at once instead of each task separately.
38- let currentIcon = pending
39- let currentTasks = 0
40-
41- const addOutput = ( ) => output . push ( currentIcon . color ( currentIcon . char . repeat ( currentTasks ) ) )
41+ const addOutput = ( ) => {
42+ const { char, color } = currentIcon
43+ const availableWidth = width - previousLineWidth
44+ if ( availableWidth > currentTasks ) {
45+ output += color ( char . repeat ( currentTasks ) )
46+ previousLineWidth += currentTasks
47+ }
48+ else {
49+ // We need to split the line otherwise it will mess up log-update's height calculation
50+ // and spam the scrollback buffer with dots.
51+
52+ // Fill the current line first
53+ let buf = `${ char . repeat ( availableWidth ) } \n`
54+ const remaining = currentTasks - availableWidth
55+
56+ // Then fill as many full rows as possible
57+ const fullRows = Math . floor ( remaining / width )
58+ buf += `${ char . repeat ( width ) } \n` . repeat ( fullRows )
59+
60+ // Add remaining dots which don't make a full row
61+ const partialRow = remaining % width
62+ if ( partialRow > 0 ) {
63+ buf += char . repeat ( partialRow )
64+ previousLineWidth = partialRow
65+ }
66+ else {
67+ previousLineWidth = 0
68+ }
4269
70+ output += color ( buf )
71+ }
72+ }
4373 for ( const task of all ) {
4474 const icon = getIcon ( task )
45- const isLast = all . indexOf ( task ) === all . length - 1
46-
4775 if ( icon === currentIcon ) {
4876 currentTasks ++
49-
50- if ( isLast )
51- addOutput ( )
52-
5377 continue
5478 }
55-
5679 // Task mode/state has changed, add previous group to output
5780 addOutput ( )
5881
5982 // Start tracking new group
6083 currentTasks = 1
6184 currentIcon = icon
62-
63- if ( isLast )
64- addOutput ( )
6585 }
66-
67- return output . join ( '' )
86+ addOutput ( )
87+ return output
6888}
6989
7090export function createDotRenderer ( _tasks : Task [ ] , options : DotRendererOptions ) {
7191 let tasks = _tasks
7292 let timer : any
7393
74- const log = options . logger . logUpdate
94+ const { logUpdate : log , outputStream } = options . logger
7595
7696 function update ( ) {
77- log ( render ( tasks ) )
97+ log ( render ( tasks , outputStream . columns ) )
7898 }
7999
80100 return {
@@ -94,7 +114,7 @@ export function createDotRenderer(_tasks: Task[], options: DotRendererOptions) {
94114 timer = undefined
95115 }
96116 log . clear ( )
97- options . logger . log ( render ( tasks ) )
117+ options . logger . log ( render ( tasks , outputStream . columns ) )
98118 return this
99119 } ,
100120 clear ( ) {
0 commit comments