@@ -23,17 +23,13 @@ redraw_event: std.Thread.ResetEvent,
23
23
/// Indicates a request to shut down and reset global state.
24
24
/// Accessed atomically.
25
25
done : bool ,
26
+ need_clear : bool ,
26
27
27
28
refresh_rate_ns : u64 ,
28
29
initial_delay_ns : u64 ,
29
30
30
31
rows : u16 ,
31
32
cols : u16 ,
32
- /// Tracks the number of newlines that have been actually written to the terminal.
33
- written_newline_count : u16 ,
34
- /// Tracks the number of newlines that will be written to the terminal if the
35
- /// draw buffer is sent.
36
- accumulated_newline_count : u16 ,
37
33
38
34
/// Accessed only by the update thread.
39
35
draw_buffer : []u8 ,
@@ -312,10 +308,9 @@ var global_progress: Progress = .{
312
308
.initial_delay_ns = undefined ,
313
309
.rows = 0 ,
314
310
.cols = 0 ,
315
- .written_newline_count = 0 ,
316
- .accumulated_newline_count = 0 ,
317
311
.draw_buffer = undefined ,
318
312
.done = false ,
313
+ .need_clear = false ,
319
314
320
315
.node_parents = & node_parents_buffer ,
321
316
.node_storage = & node_storage_buffer ,
@@ -446,10 +441,11 @@ fn updateThreadRun() void {
446
441
if (@atomicLoad (bool , & global_progress .done , .seq_cst )) return ;
447
442
maybeUpdateSize (resize_flag );
448
443
449
- const buffer = computeRedraw (& serialized_buffer );
444
+ const buffer , _ = computeRedraw (& serialized_buffer );
450
445
if (stderr_mutex .tryLock ()) {
451
446
defer stderr_mutex .unlock ();
452
447
write (buffer ) catch return ;
448
+ global_progress .need_clear = true ;
453
449
}
454
450
}
455
451
@@ -464,10 +460,11 @@ fn updateThreadRun() void {
464
460
465
461
maybeUpdateSize (resize_flag );
466
462
467
- const buffer = computeRedraw (& serialized_buffer );
463
+ const buffer , _ = computeRedraw (& serialized_buffer );
468
464
if (stderr_mutex .tryLock ()) {
469
465
defer stderr_mutex .unlock ();
470
466
write (buffer ) catch return ;
467
+ global_progress .need_clear = true ;
471
468
}
472
469
}
473
470
}
@@ -488,11 +485,13 @@ fn windowsApiUpdateThreadRun() void {
488
485
if (@atomicLoad (bool , & global_progress .done , .seq_cst )) return ;
489
486
maybeUpdateSize (resize_flag );
490
487
491
- const buffer = computeRedraw (& serialized_buffer );
488
+ const buffer , const nl_n = computeRedraw (& serialized_buffer );
492
489
if (stderr_mutex .tryLock ()) {
493
490
defer stderr_mutex .unlock ();
494
491
windowsApiWriteMarker ();
495
492
write (buffer ) catch return ;
493
+ global_progress .need_clear = true ;
494
+ windowsApiMoveToMarker (nl_n ) catch return ;
496
495
}
497
496
}
498
497
@@ -507,12 +506,14 @@ fn windowsApiUpdateThreadRun() void {
507
506
508
507
maybeUpdateSize (resize_flag );
509
508
510
- const buffer = computeRedraw (& serialized_buffer );
509
+ const buffer , const nl_n = computeRedraw (& serialized_buffer );
511
510
if (stderr_mutex .tryLock ()) {
512
511
defer stderr_mutex .unlock ();
513
512
clearWrittenWindowsApi () catch return ;
514
513
windowsApiWriteMarker ();
515
514
write (buffer ) catch return ;
515
+ global_progress .need_clear = true ;
516
+ windowsApiMoveToMarker (nl_n ) catch return ;
516
517
}
517
518
}
518
519
}
@@ -645,40 +646,16 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize {
645
646
}
646
647
647
648
fn clearWrittenWithEscapeCodes () anyerror ! void {
648
- if (global_progress .written_newline_count == 0 ) return ;
649
+ if (! global_progress .need_clear ) return ;
649
650
650
651
var i : usize = 0 ;
651
652
const buf = global_progress .draw_buffer ;
652
653
653
- buf [i .. ][0.. start_sync .len ].* = start_sync .* ;
654
- i += start_sync .len ;
655
-
656
- i = computeClear (buf , i );
657
-
658
- buf [i .. ][0.. finish_sync .len ].* = finish_sync .* ;
659
- i += finish_sync .len ;
660
-
661
- global_progress .accumulated_newline_count = 0 ;
662
- try write (buf [0.. i ]);
663
- }
664
-
665
- fn computeClear (buf : []u8 , start_i : usize ) usize {
666
- var i = start_i ;
667
-
668
- const prev_nl_n = global_progress .written_newline_count ;
669
- if (prev_nl_n > 0 ) {
670
- buf [i ] = '\r ' ;
671
- i += 1 ;
672
- for (0.. prev_nl_n ) | _ | {
673
- buf [i .. ][0.. up_one_line .len ].* = up_one_line .* ;
674
- i += up_one_line .len ;
675
- }
676
- }
677
-
678
654
buf [i .. ][0.. clear .len ].* = clear .* ;
679
655
i += clear .len ;
680
656
681
- return i ;
657
+ global_progress .need_clear = false ;
658
+ try write (buf [0.. i ]);
682
659
}
683
660
684
661
/// U+25BA or ►
@@ -704,38 +681,44 @@ fn clearWrittenWindowsApi() error{Unexpected}!void {
704
681
// but it must be a valid attribute and it actually needs to apply to the first
705
682
// character in order to be readable via ReadConsoleOutputAttribute. It doesn't seem
706
683
// like any of the available attributes are invisible/benign.
707
- const prev_nl_n = global_progress .written_newline_count ;
708
- if (prev_nl_n > 0 ) {
709
- const handle = global_progress .terminal .handle ;
710
- const screen_area = @as (windows .DWORD , global_progress .cols ) * global_progress .rows ;
684
+ if (! global_progress .need_clear ) return ;
685
+ const handle = global_progress .terminal .handle ;
686
+ const screen_area = @as (windows .DWORD , global_progress .cols ) * global_progress .rows ;
711
687
712
- var console_info : windows.CONSOLE_SCREEN_BUFFER_INFO = undefined ;
713
- if (windows .kernel32 .GetConsoleScreenBufferInfo (handle , & console_info ) == 0 ) {
714
- return error .Unexpected ;
715
- }
716
- const cursor_pos = console_info .dwCursorPosition ;
717
- const expected_y = cursor_pos .Y - @as (i16 , @intCast (prev_nl_n ));
718
- var start_pos = windows.COORD { .X = 0 , .Y = expected_y };
719
- while (start_pos .Y >= 0 ) {
720
- var wchar : [1 ]u16 = undefined ;
721
- var num_console_chars_read : windows.DWORD = undefined ;
722
- if (windows .kernel32 .ReadConsoleOutputCharacterW (handle , & wchar , wchar .len , start_pos , & num_console_chars_read ) == 0 ) {
723
- return error .Unexpected ;
724
- }
688
+ var console_info : windows.CONSOLE_SCREEN_BUFFER_INFO = undefined ;
689
+ if (windows .kernel32 .GetConsoleScreenBufferInfo (handle , & console_info ) == 0 ) {
690
+ return error .Unexpected ;
691
+ }
692
+ var num_chars_written : windows.DWORD = undefined ;
693
+ if (windows .kernel32 .FillConsoleOutputCharacterW (handle , ' ' , screen_area , console_info .dwCursorPosition , & num_chars_written ) == 0 ) {
694
+ return error .Unexpected ;
695
+ }
696
+ }
725
697
726
- if (wchar [0 ] == windows_api_start_marker ) break ;
727
- start_pos .Y -= 1 ;
728
- } else {
729
- // If we couldn't find the marker, then just assume that no lines wrapped
730
- start_pos = .{ .X = 0 , .Y = expected_y };
731
- }
732
- var num_chars_written : windows.DWORD = undefined ;
733
- if (windows .kernel32 .FillConsoleOutputCharacterW (handle , ' ' , screen_area , start_pos , & num_chars_written ) == 0 ) {
734
- return error .Unexpected ;
735
- }
736
- if (windows .kernel32 .SetConsoleCursorPosition (handle , start_pos ) == 0 ) {
698
+ fn windowsApiMoveToMarker (nl_n : usize ) error {Unexpected }! void {
699
+ const handle = global_progress .terminal .handle ;
700
+ var console_info : windows.CONSOLE_SCREEN_BUFFER_INFO = undefined ;
701
+ if (windows .kernel32 .GetConsoleScreenBufferInfo (handle , & console_info ) == 0 ) {
702
+ return error .Unexpected ;
703
+ }
704
+ const cursor_pos = console_info .dwCursorPosition ;
705
+ const expected_y = cursor_pos .Y - @as (i16 , @intCast (nl_n ));
706
+ var start_pos : windows.COORD = .{ .X = 0 , .Y = expected_y };
707
+ while (start_pos .Y >= 0 ) {
708
+ var wchar : [1 ]u16 = undefined ;
709
+ var num_console_chars_read : windows.DWORD = undefined ;
710
+ if (windows .kernel32 .ReadConsoleOutputCharacterW (handle , & wchar , wchar .len , start_pos , & num_console_chars_read ) == 0 ) {
737
711
return error .Unexpected ;
738
712
}
713
+
714
+ if (wchar [0 ] == windows_api_start_marker ) break ;
715
+ start_pos .Y -= 1 ;
716
+ } else {
717
+ // If we couldn't find the marker, then just assume that no lines wrapped
718
+ start_pos = .{ .X = 0 , .Y = expected_y };
719
+ }
720
+ if (windows .kernel32 .SetConsoleCursorPosition (handle , start_pos ) == 0 ) {
721
+ return error .Unexpected ;
739
722
}
740
723
}
741
724
@@ -1052,7 +1035,7 @@ fn useSavedIpcData(
1052
1035
return start_serialized_len + storage .len ;
1053
1036
}
1054
1037
1055
- fn computeRedraw (serialized_buffer : * Serialized.Buffer ) []u8 {
1038
+ fn computeRedraw (serialized_buffer : * Serialized.Buffer ) struct { []u8 , usize } {
1056
1039
const serialized = serialize (serialized_buffer );
1057
1040
1058
1041
// Now we can analyze our copy of the graph without atomics, reconstructing
@@ -1078,8 +1061,10 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 {
1078
1061
}
1079
1062
}
1080
1063
1081
- // The strategy is: keep the cursor at the end, and then with every redraw:
1082
- // move cursor to beginning of line, move cursor up N lines, erase to end of screen, write
1064
+ // The strategy is, with every redraw:
1065
+ // erase to end of screen, write, move cursor to beginning of line, move cursor up N lines
1066
+ // This keeps the cursor at the beginning so that unlocked stderr writes
1067
+ // don't get eaten by the clear.
1083
1068
1084
1069
var i : usize = 0 ;
1085
1070
const buf = global_progress .draw_buffer ;
@@ -1091,20 +1076,31 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 {
1091
1076
1092
1077
switch (global_progress .terminal_mode ) {
1093
1078
.off = > unreachable ,
1094
- .ansi_escape_codes = > i = computeClear (buf , i ),
1079
+ .ansi_escape_codes = > {
1080
+ buf [i .. ][0.. clear .len ].* = clear .* ;
1081
+ i += clear .len ;
1082
+ },
1095
1083
.windows_api = > if (! is_windows ) unreachable ,
1096
1084
}
1097
1085
1098
- global_progress .accumulated_newline_count = 0 ;
1099
1086
const root_node_index : Node.Index = @enumFromInt (0 );
1100
- i = computeNode (buf , i , serialized , children , root_node_index );
1087
+ i , const nl_n = computeNode (buf , i , 0 , serialized , children , root_node_index );
1101
1088
1102
1089
if (global_progress .terminal_mode == .ansi_escape_codes ) {
1090
+ if (nl_n > 0 ) {
1091
+ buf [i ] = '\r ' ;
1092
+ i += 1 ;
1093
+ for (0.. nl_n ) | _ | {
1094
+ buf [i .. ][0.. up_one_line .len ].* = up_one_line .* ;
1095
+ i += up_one_line .len ;
1096
+ }
1097
+ }
1098
+
1103
1099
buf [i .. ][0.. finish_sync .len ].* = finish_sync .* ;
1104
1100
i += finish_sync .len ;
1105
1101
}
1106
1102
1107
- return buf [0.. i ];
1103
+ return .{ buf [0.. i ], nl_n } ;
1108
1104
}
1109
1105
1110
1106
fn computePrefix (
@@ -1138,20 +1134,23 @@ fn computePrefix(
1138
1134
}
1139
1135
1140
1136
const line_upper_bound_len = @max (TreeSymbol .tee .maxByteLen (), TreeSymbol .langle .maxByteLen ()) +
1141
- "[4294967296/4294967296] " .len + Node .max_name_len + finish_sync .len ;
1137
+ "[4294967296/4294967296] " .len + Node .max_name_len + ( 1 + up_one_line . len ) + finish_sync .len ;
1142
1138
1143
1139
fn computeNode (
1144
1140
buf : []u8 ,
1145
1141
start_i : usize ,
1142
+ start_nl_n : usize ,
1146
1143
serialized : Serialized ,
1147
1144
children : []const Children ,
1148
1145
node_index : Node.Index ,
1149
- ) usize {
1146
+ ) struct { usize , usize } {
1150
1147
var i = start_i ;
1148
+ var nl_n = start_nl_n ;
1149
+
1151
1150
i = computePrefix (buf , i , serialized , children , node_index );
1152
1151
1153
1152
if (i + line_upper_bound_len > buf .len )
1154
- return start_i ;
1153
+ return .{ start_i , start_nl_n } ;
1155
1154
1156
1155
const storage = & serialized .storage [@intFromEnum (node_index )];
1157
1156
const estimated_total = storage .estimated_total_count ;
@@ -1186,34 +1185,33 @@ fn computeNode(
1186
1185
i = @min (global_progress .cols + start_i , i );
1187
1186
buf [i ] = '\n ' ;
1188
1187
i += 1 ;
1189
- global_progress . accumulated_newline_count += 1 ;
1188
+ nl_n += 1 ;
1190
1189
}
1191
1190
1192
- if (global_progress .withinRowLimit ()) {
1191
+ if (global_progress .withinRowLimit (nl_n )) {
1193
1192
if (children [@intFromEnum (node_index )].child .unwrap ()) | child | {
1194
- i = computeNode (buf , i , serialized , children , child );
1193
+ i , nl_n = computeNode (buf , i , nl_n , serialized , children , child );
1195
1194
}
1196
1195
}
1197
1196
1198
- if (global_progress .withinRowLimit ()) {
1197
+ if (global_progress .withinRowLimit (nl_n )) {
1199
1198
if (children [@intFromEnum (node_index )].sibling .unwrap ()) | sibling | {
1200
- i = computeNode (buf , i , serialized , children , sibling );
1199
+ i , nl_n = computeNode (buf , i , nl_n , serialized , children , sibling );
1201
1200
}
1202
1201
}
1203
1202
1204
- return i ;
1203
+ return .{ i , nl_n } ;
1205
1204
}
1206
1205
1207
- fn withinRowLimit (p : * Progress ) bool {
1206
+ fn withinRowLimit (p : * Progress , nl_n : usize ) bool {
1208
1207
// The +2 here is so that the PS1 is not scrolled off the top of the terminal.
1209
1208
// one because we keep the cursor on the next line
1210
1209
// one more to account for the PS1
1211
- return p . accumulated_newline_count + 2 < p .rows ;
1210
+ return nl_n + 2 < p .rows ;
1212
1211
}
1213
1212
1214
1213
fn write (buf : []const u8 ) anyerror ! void {
1215
1214
try global_progress .terminal .writeAll (buf );
1216
- global_progress .written_newline_count = global_progress .accumulated_newline_count ;
1217
1215
}
1218
1216
1219
1217
var remaining_write_trash_bytes : usize = 0 ;
0 commit comments