| ... | @@ -8,19 +8,13 @@ const assert = std.debug.assert; | ... | @@ -8,19 +8,13 @@ const assert = std.debug.assert; |
| 8 | const Progress = @This(); | 8 | const Progress = @This(); |
| 9 | const posix = std.posix; | 9 | const posix = std.posix; |
| 10 | const is_big_endian = builtin.cpu.arch.endian() == .big; | 10 | const is_big_endian = builtin.cpu.arch.endian() == .big; |
| | 11 | const is_windows = builtin.os.tag == .windows; |
| 11 | | 12 | |
| 12 | /// `null` if the current node (and its children) should | 13 | /// `null` if the current node (and its children) should |
| 13 | /// not print on update() | 14 | /// not print on update() |
| 14 | terminal: ?std.fs.File, | 15 | terminal: std.fs.File, |
| 15 | | 16 | |
| 16 | /// Is this a windows API terminal (note: this is not the same as being run on windows | 17 | terminal_mode: TerminalMode, |
| 17 | /// because other terminals exist like MSYS/git-bash) | | |
| 18 | is_windows_terminal: bool, | | |
| 19 | /// The output code page of the console (only set if the console is a Windows API terminal) | | |
| 20 | console_code_page: if (builtin.os.tag == .windows) windows.UINT else void, | | |
| 21 | | | |
| 22 | /// Whether the terminal supports ANSI escape codes. | | |
| 23 | supports_ansi_escape_codes: bool, | | |
| 24 | | 18 | |
| 25 | update_thread: ?std.Thread, | 19 | update_thread: ?std.Thread, |
| 26 | | 20 | |
| ... | @@ -53,6 +47,19 @@ node_freelist: []Node.OptionalIndex, | ... | @@ -53,6 +47,19 @@ node_freelist: []Node.OptionalIndex, |
| 53 | node_freelist_first: Node.OptionalIndex, | 47 | node_freelist_first: Node.OptionalIndex, |
| 54 | node_end_index: u32, | 48 | node_end_index: u32, |
| 55 | | 49 | |
| | 50 | pub const TerminalMode = union(enum) { |
| | 51 | off, |
| | 52 | ansi_escape_codes, |
| | 53 | /// This is not the same as being run on windows because other terminals |
| | 54 | /// exist like MSYS/git-bash. |
| | 55 | windows_api: if (is_windows) WindowsApi else void, |
| | 56 | |
| | 57 | pub const WindowsApi = struct { |
| | 58 | /// The output code page of the console. |
| | 59 | code_page: windows.UINT, |
| | 60 | }; |
| | 61 | }; |
| | 62 | |
| 56 | pub const Options = struct { | 63 | pub const Options = struct { |
| 57 | /// User-provided buffer with static lifetime. | 64 | /// User-provided buffer with static lifetime. |
| 58 | /// | 65 | /// |
| ... | @@ -297,10 +304,8 @@ pub const Node = struct { | ... | @@ -297,10 +304,8 @@ pub const Node = struct { |
| 297 | }; | 304 | }; |
| 298 | | 305 | |
| 299 | var global_progress: Progress = .{ | 306 | var global_progress: Progress = .{ |
| 300 | .terminal = null, | 307 | .terminal = undefined, |
| 301 | .is_windows_terminal = false, | 308 | .terminal_mode = .off, |
| 302 | .console_code_page = if (builtin.os.tag == .windows) undefined else {}, | | |
| 303 | .supports_ansi_escape_codes = false, | | |
| 304 | .update_thread = null, | 309 | .update_thread = null, |
| 305 | .redraw_event = .{}, | 310 | .redraw_event = .{}, |
| 306 | .refresh_rate_ns = undefined, | 311 | .refresh_rate_ns = undefined, |
| ... | @@ -376,20 +381,16 @@ pub fn start(options: Options) Node { | ... | @@ -376,20 +381,16 @@ pub fn start(options: Options) Node { |
| 376 | return .{ .index = .none }; | 381 | return .{ .index = .none }; |
| 377 | } | 382 | } |
| 378 | const stderr = std.io.getStdErr(); | 383 | const stderr = std.io.getStdErr(); |
| | 384 | global_progress.terminal = stderr; |
| 379 | if (stderr.supportsAnsiEscapeCodes()) { | 385 | if (stderr.supportsAnsiEscapeCodes()) { |
| 380 | global_progress.terminal = stderr; | 386 | global_progress.terminal_mode = .ansi_escape_codes; |
| 381 | global_progress.supports_ansi_escape_codes = true; | 387 | } else if (is_windows and stderr.isTty()) { |
| 382 | } else if (builtin.os.tag == .windows and stderr.isTty()) { | 388 | global_progress.terminal_mode = TerminalMode{ .windows_api = .{ |
| 383 | global_progress.is_windows_terminal = true; | 389 | .code_page = windows.kernel32.GetConsoleOutputCP(), |
| 384 | global_progress.console_code_page = windows.kernel32.GetConsoleOutputCP(); | 390 | } }; |
| 385 | global_progress.terminal = stderr; | | |
| 386 | } else if (builtin.os.tag != .windows) { | | |
| 387 | // we are in a "dumb" terminal like in acme or writing to a file | | |
| 388 | global_progress.terminal = stderr; | | |
| 389 | } | 391 | } |
| 390 | | 392 | |
| 391 | const can_clear_terminal = global_progress.supports_ansi_escape_codes or global_progress.is_windows_terminal; | 393 | if (global_progress.terminal_mode == .off) { |
| 392 | if (global_progress.terminal == null or !can_clear_terminal) { | | |
| 393 | return .{ .index = .none }; | 394 | return .{ .index = .none }; |
| 394 | } | 395 | } |
| 395 | | 396 | |
| ... | @@ -404,7 +405,11 @@ pub fn start(options: Options) Node { | ... | @@ -404,7 +405,11 @@ pub fn start(options: Options) Node { |
| 404 | }; | 405 | }; |
| 405 | } | 406 | } |
| 406 | | 407 | |
| 407 | if (std.Thread.spawn(.{}, updateThreadRun, .{})) |thread| { | 408 | if (switch (global_progress.terminal_mode) { |
| | 409 | .off => unreachable, // handled a few lines above |
| | 410 | .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{}), |
| | 411 | .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{}) else unreachable, |
| | 412 | }) |thread| { |
| 408 | global_progress.update_thread = thread; | 413 | global_progress.update_thread = thread; |
| 409 | } else |err| { | 414 | } else |err| { |
| 410 | std.log.warn("unable to spawn thread for printing progress to terminal: {s}", .{@errorName(err)}); | 415 | std.log.warn("unable to spawn thread for printing progress to terminal: {s}", .{@errorName(err)}); |
| ... | @@ -438,13 +443,42 @@ fn updateThreadRun() void { | ... | @@ -438,13 +443,42 @@ fn updateThreadRun() void { |
| 438 | | 443 | |
| 439 | { | 444 | { |
| 440 | const resize_flag = wait(global_progress.initial_delay_ns); | 445 | const resize_flag = wait(global_progress.initial_delay_ns); |
| | 446 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) return; |
| 441 | maybeUpdateSize(resize_flag); | 447 | maybeUpdateSize(resize_flag); |
| 442 | | 448 | |
| | 449 | const buffer = computeRedraw(&serialized_buffer); |
| | 450 | if (stderr_mutex.tryLock()) { |
| | 451 | defer stderr_mutex.unlock(); |
| | 452 | write(buffer) catch return; |
| | 453 | } |
| | 454 | } |
| | 455 | |
| | 456 | while (true) { |
| | 457 | const resize_flag = wait(global_progress.refresh_rate_ns); |
| | 458 | |
| 443 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) { | 459 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) { |
| 444 | stderr_mutex.lock(); | 460 | stderr_mutex.lock(); |
| 445 | defer stderr_mutex.unlock(); | 461 | defer stderr_mutex.unlock(); |
| 446 | return clearTerminal(); | 462 | return clearWrittenWithEscapeCodes() catch {}; |
| | 463 | } |
| | 464 | |
| | 465 | maybeUpdateSize(resize_flag); |
| | 466 | |
| | 467 | const buffer = computeRedraw(&serialized_buffer); |
| | 468 | if (stderr_mutex.tryLock()) { |
| | 469 | defer stderr_mutex.unlock(); |
| | 470 | write(buffer) catch return; |
| 447 | } | 471 | } |
| | 472 | } |
| | 473 | } |
| | 474 | |
| | 475 | fn windowsApiUpdateThreadRun() void { |
| | 476 | var serialized_buffer: Serialized.Buffer = undefined; |
| | 477 | |
| | 478 | { |
| | 479 | const resize_flag = wait(global_progress.initial_delay_ns); |
| | 480 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) return; |
| | 481 | maybeUpdateSize(resize_flag); |
| 448 | | 482 | |
| 449 | const buffer = computeRedraw(&serialized_buffer); | 483 | const buffer = computeRedraw(&serialized_buffer); |
| 450 | if (stderr_mutex.tryLock()) { | 484 | if (stderr_mutex.tryLock()) { |
| ... | @@ -455,17 +489,19 @@ fn updateThreadRun() void { | ... | @@ -455,17 +489,19 @@ fn updateThreadRun() void { |
| 455 | | 489 | |
| 456 | while (true) { | 490 | while (true) { |
| 457 | const resize_flag = wait(global_progress.refresh_rate_ns); | 491 | const resize_flag = wait(global_progress.refresh_rate_ns); |
| 458 | maybeUpdateSize(resize_flag); | | |
| 459 | | 492 | |
| 460 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) { | 493 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) { |
| 461 | stderr_mutex.lock(); | 494 | stderr_mutex.lock(); |
| 462 | defer stderr_mutex.unlock(); | 495 | defer stderr_mutex.unlock(); |
| 463 | return clearTerminal(); | 496 | return clearWrittenWindowsApi() catch {}; |
| 464 | } | 497 | } |
| 465 | | 498 | |
| | 499 | maybeUpdateSize(resize_flag); |
| | 500 | |
| 466 | const buffer = computeRedraw(&serialized_buffer); | 501 | const buffer = computeRedraw(&serialized_buffer); |
| 467 | if (stderr_mutex.tryLock()) { | 502 | if (stderr_mutex.tryLock()) { |
| 468 | defer stderr_mutex.unlock(); | 503 | defer stderr_mutex.unlock(); |
| | 504 | clearWrittenWindowsApi() catch return; |
| 469 | write(buffer) catch return; | 505 | write(buffer) catch return; |
| 470 | } | 506 | } |
| 471 | } | 507 | } |
| ... | @@ -476,7 +512,7 @@ fn updateThreadRun() void { | ... | @@ -476,7 +512,7 @@ fn updateThreadRun() void { |
| 476 | /// During the lock, any `std.Progress` information is cleared from the terminal. | 512 | /// During the lock, any `std.Progress` information is cleared from the terminal. |
| 477 | pub fn lockStdErr() void { | 513 | pub fn lockStdErr() void { |
| 478 | stderr_mutex.lock(); | 514 | stderr_mutex.lock(); |
| 479 | clearTerminal(); | 515 | clearWrittenWithEscapeCodes() catch {}; |
| 480 | } | 516 | } |
| 481 | | 517 | |
| 482 | pub fn unlockStdErr() void { | 518 | pub fn unlockStdErr() void { |
| ... | @@ -504,7 +540,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { | ... | @@ -504,7 +540,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { |
| 504 | _ = wait(global_progress.refresh_rate_ns); | 540 | _ = wait(global_progress.refresh_rate_ns); |
| 505 | | 541 | |
| 506 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) | 542 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) |
| 507 | return clearTerminal(); | 543 | return; |
| 508 | | 544 | |
| 509 | const serialized = serialize(&serialized_buffer); | 545 | const serialized = serialize(&serialized_buffer); |
| 510 | writeIpc(fd, serialized) catch |err| switch (err) { | 546 | writeIpc(fd, serialized) catch |err| switch (err) { |
| ... | @@ -569,41 +605,36 @@ const TreeSymbol = enum { | ... | @@ -569,41 +605,36 @@ const TreeSymbol = enum { |
| 569 | var max: usize = 0; | 605 | var max: usize = 0; |
| 570 | inline for (@typeInfo(Encoding).Enum.fields) |field| { | 606 | inline for (@typeInfo(Encoding).Enum.fields) |field| { |
| 571 | const len = symbol.bytes(@field(Encoding, field.name)).len; | 607 | const len = symbol.bytes(@field(Encoding, field.name)).len; |
| 572 | if (len > max) max = len; | 608 | max = @max(max, len); |
| 573 | } | 609 | } |
| 574 | return max; | 610 | return max; |
| 575 | } | 611 | } |
| 576 | }; | 612 | }; |
| 577 | | 613 | |
| 578 | fn appendTreeSymbol(comptime symbol: TreeSymbol, buf: []u8, start_i: usize) usize { | 614 | fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { |
| 579 | if (builtin.os.tag == .windows and global_progress.is_windows_terminal) { | 615 | switch (global_progress.terminal_mode) { |
| 580 | const bytes = switch (global_progress.console_code_page) { | 616 | .off => unreachable, |
| 581 | // Code page 437 is the default code page and contains the box drawing symbols | 617 | .ansi_escape_codes => { |
| 582 | 437 => symbol.bytes(.code_page_437), | 618 | const bytes = symbol.escapeSeq(); |
| 583 | // UTF-8 | 619 | buf[start_i..][0..bytes.len].* = bytes.*; |
| 584 | 65001 => symbol.bytes(.utf8), | 620 | return start_i + bytes.len; |
| 585 | // Fall back to ASCII approximation | 621 | }, |
| 586 | else => symbol.bytes(.ascii), | 622 | .windows_api => |windows_api| { |
| 587 | }; | 623 | const bytes = if (!is_windows) unreachable else switch (windows_api.code_page) { |
| 588 | @memcpy(buf[start_i..][0..bytes.len], bytes); | 624 | // Code page 437 is the default code page and contains the box drawing symbols |
| 589 | return start_i + bytes.len; | 625 | 437 => symbol.bytes(.code_page_437), |
| | 626 | // UTF-8 |
| | 627 | 65001 => symbol.bytes(.utf8), |
| | 628 | // Fall back to ASCII approximation |
| | 629 | else => symbol.bytes(.ascii), |
| | 630 | }; |
| | 631 | @memcpy(buf[start_i..][0..bytes.len], bytes); |
| | 632 | return start_i + bytes.len; |
| | 633 | }, |
| 590 | } | 634 | } |
| 591 | | | |
| 592 | // Drawing the tree is disabled when ansi escape codes are not supported | | |
| 593 | assert(global_progress.supports_ansi_escape_codes); | | |
| 594 | | | |
| 595 | const bytes = symbol.escapeSeq(); | | |
| 596 | buf[start_i..][0..bytes.len].* = bytes.*; | | |
| 597 | return start_i + bytes.len; | | |
| 598 | } | 635 | } |
| 599 | | 636 | |
| 600 | fn clearTerminal() void { | 637 | fn clearWrittenWithEscapeCodes() anyerror!void { |
| 601 | if (builtin.os.tag == .windows and global_progress.is_windows_terminal) { | | |
| 602 | return clearTerminalWindowsApi() catch { | | |
| 603 | global_progress.terminal = null; | | |
| 604 | }; | | |
| 605 | } | | |
| 606 | | | |
| 607 | if (global_progress.written_newline_count == 0) return; | 638 | if (global_progress.written_newline_count == 0) return; |
| 608 | | 639 | |
| 609 | var i: usize = 0; | 640 | var i: usize = 0; |
| ... | @@ -618,9 +649,7 @@ fn clearTerminal() void { | ... | @@ -618,9 +649,7 @@ fn clearTerminal() void { |
| 618 | i += finish_sync.len; | 649 | i += finish_sync.len; |
| 619 | | 650 | |
| 620 | global_progress.accumulated_newline_count = 0; | 651 | global_progress.accumulated_newline_count = 0; |
| 621 | write(buf[0..i]) catch { | 652 | try write(buf[0..i]); |
| 622 | global_progress.terminal = null; | | |
| 623 | }; | | |
| 624 | } | 653 | } |
| 625 | | 654 | |
| 626 | fn computeClear(buf: []u8, start_i: usize) usize { | 655 | fn computeClear(buf: []u8, start_i: usize) usize { |
| ... | @@ -645,7 +674,7 @@ fn computeClear(buf: []u8, start_i: usize) usize { | ... | @@ -645,7 +674,7 @@ fn computeClear(buf: []u8, start_i: usize) usize { |
| 645 | /// U+25BA or ► | 674 | /// U+25BA or ► |
| 646 | const windows_api_start_marker = 0x25BA; | 675 | const windows_api_start_marker = 0x25BA; |
| 647 | | 676 | |
| 648 | fn clearTerminalWindowsApi() error{Unexpected}!void { | 677 | fn clearWrittenWindowsApi() error{Unexpected}!void { |
| 649 | // This uses a 'marker' strategy. The idea is: | 678 | // This uses a 'marker' strategy. The idea is: |
| 650 | // - Always write a marker (in this case U+25BA or ►) at the beginning of the progress | 679 | // - Always write a marker (in this case U+25BA or ►) at the beginning of the progress |
| 651 | // - Get the current cursor position (at the end of the progress) | 680 | // - Get the current cursor position (at the end of the progress) |
| ... | @@ -667,7 +696,7 @@ fn clearTerminalWindowsApi() error{Unexpected}!void { | ... | @@ -667,7 +696,7 @@ fn clearTerminalWindowsApi() error{Unexpected}!void { |
| 667 | // like any of the available attributes are invisible/benign. | 696 | // like any of the available attributes are invisible/benign. |
| 668 | const prev_nl_n = global_progress.written_newline_count; | 697 | const prev_nl_n = global_progress.written_newline_count; |
| 669 | if (prev_nl_n > 0) { | 698 | if (prev_nl_n > 0) { |
| 670 | const handle = (global_progress.terminal orelse return).handle; | 699 | const handle = global_progress.terminal.handle; |
| 671 | const screen_area = @as(windows.DWORD, global_progress.cols) * global_progress.rows; | 700 | const screen_area = @as(windows.DWORD, global_progress.cols) * global_progress.rows; |
| 672 | | 701 | |
| 673 | var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | 702 | var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; |
| ... | @@ -777,14 +806,14 @@ const SavedMetadata = struct { | ... | @@ -777,14 +806,14 @@ const SavedMetadata = struct { |
| 777 | nodes_len: u8, | 806 | nodes_len: u8, |
| 778 | | 807 | |
| 779 | fn getIpcFd(metadata: SavedMetadata) posix.fd_t { | 808 | fn getIpcFd(metadata: SavedMetadata) posix.fd_t { |
| 780 | return if (builtin.os.tag == .windows) | 809 | return if (is_windows) |
| 781 | @ptrFromInt(@as(usize, metadata.ipc_fd) << 2) | 810 | @ptrFromInt(@as(usize, metadata.ipc_fd) << 2) |
| 782 | else | 811 | else |
| 783 | metadata.ipc_fd; | 812 | metadata.ipc_fd; |
| 784 | } | 813 | } |
| 785 | | 814 | |
| 786 | fn setIpcFd(fd: posix.fd_t) u16 { | 815 | fn setIpcFd(fd: posix.fd_t) u16 { |
| 787 | return @intCast(if (builtin.os.tag == .windows) | 816 | return @intCast(if (is_windows) |
| 788 | @shrExact(@intFromPtr(fd), 2) | 817 | @shrExact(@intFromPtr(fd), 2) |
| 789 | else | 818 | else |
| 790 | fd); | 819 | fd); |
| ... | @@ -1019,35 +1048,21 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 { | ... | @@ -1019,35 +1048,21 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 { |
| 1019 | var i: usize = 0; | 1048 | var i: usize = 0; |
| 1020 | const buf = global_progress.draw_buffer; | 1049 | const buf = global_progress.draw_buffer; |
| 1021 | | 1050 | |
| 1022 | if (global_progress.supports_ansi_escape_codes) { | 1051 | buf[i..][0..start_sync.len].* = start_sync.*; |
| 1023 | buf[i..][0..start_sync.len].* = start_sync.*; | 1052 | i += start_sync.len; |
| 1024 | i += start_sync.len; | | |
| 1025 | | | |
| 1026 | i = computeClear(buf, i); | | |
| 1027 | } else if (builtin.os.tag == .windows and global_progress.is_windows_terminal) { | | |
| 1028 | clearTerminalWindowsApi() catch { | | |
| 1029 | global_progress.terminal = null; | | |
| 1030 | return buf[0..0]; | | |
| 1031 | }; | | |
| 1032 | | 1053 | |
| 1033 | // Write the marker that we will use to find the beginning of the progress when clearing. | 1054 | switch (global_progress.terminal_mode) { |
| 1034 | // Note: This doesn't have to use WriteConsoleW, but doing so avoids dealing with the code page. | 1055 | .off => unreachable, |
| 1035 | var num_chars_written: windows.DWORD = undefined; | 1056 | .ansi_escape_codes => i = computeClear(buf, i), |
| 1036 | const handle = (global_progress.terminal orelse return buf[0..0]).handle; | 1057 | .windows_api => if (!is_windows) unreachable, |
| 1037 | if (windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null) == 0) { | | |
| 1038 | global_progress.terminal = null; | | |
| 1039 | return buf[0..0]; | | |
| 1040 | } | | |
| 1041 | } | 1058 | } |
| 1042 | | 1059 | |
| 1043 | global_progress.accumulated_newline_count = 0; | 1060 | global_progress.accumulated_newline_count = 0; |
| 1044 | const root_node_index: Node.Index = @enumFromInt(0); | 1061 | const root_node_index: Node.Index = @enumFromInt(0); |
| 1045 | i = computeNode(buf, i, serialized, children, root_node_index); | 1062 | i = computeNode(buf, i, serialized, children, root_node_index); |
| 1046 | | 1063 | |
| 1047 | if (global_progress.supports_ansi_escape_codes) { | 1064 | buf[i..][0..finish_sync.len].* = finish_sync.*; |
| 1048 | buf[i..][0..finish_sync.len].* = finish_sync.*; | 1065 | i += finish_sync.len; |
| 1049 | i += finish_sync.len; | | |
| 1050 | } | | |
| 1051 | | 1066 | |
| 1052 | return buf[0..i]; | 1067 | return buf[0..i]; |
| 1053 | } | 1068 | } |
| ... | @@ -1075,15 +1090,15 @@ fn computePrefix( | ... | @@ -1075,15 +1090,15 @@ fn computePrefix( |
| 1075 | buf[i..][0..prefix.len].* = prefix.*; | 1090 | buf[i..][0..prefix.len].* = prefix.*; |
| 1076 | i += prefix.len; | 1091 | i += prefix.len; |
| 1077 | } else { | 1092 | } else { |
| 1078 | const upper_bound_len = TreeSymbol.line.maxByteLen() + line_upper_bound_len; | 1093 | const upper_bound_len = comptime (TreeSymbol.line.maxByteLen() + line_upper_bound_len); |
| 1079 | if (i + upper_bound_len > buf.len) return buf.len; | 1094 | if (i + upper_bound_len > buf.len) return buf.len; |
| 1080 | i = appendTreeSymbol(.line, buf, i); | 1095 | i = appendTreeSymbol(.line, buf, i); |
| 1081 | } | 1096 | } |
| 1082 | return i; | 1097 | return i; |
| 1083 | } | 1098 | } |
| 1084 | | 1099 | |
| 1085 | const line_upper_bound_len = @max(TreeSymbol.tee.maxByteLen(), TreeSymbol.langle.maxByteLen()) + "[4294967296/4294967296] ".len + | 1100 | const line_upper_bound_len = @max(TreeSymbol.tee.maxByteLen(), TreeSymbol.langle.maxByteLen()) + |
| 1086 | Node.max_name_len + finish_sync.len; | 1101 | "[4294967296/4294967296] ".len + Node.max_name_len + finish_sync.len; |
| 1087 | | 1102 | |
| 1088 | fn computeNode( | 1103 | fn computeNode( |
| 1089 | buf: []u8, | 1104 | buf: []u8, |
| ... | @@ -1157,8 +1172,7 @@ fn withinRowLimit(p: *Progress) bool { | ... | @@ -1157,8 +1172,7 @@ fn withinRowLimit(p: *Progress) bool { |
| 1157 | } | 1172 | } |
| 1158 | | 1173 | |
| 1159 | fn write(buf: []const u8) anyerror!void { | 1174 | fn write(buf: []const u8) anyerror!void { |
| 1160 | const tty = global_progress.terminal orelse return; | 1175 | try global_progress.terminal.writeAll(buf); |
| 1161 | try tty.writeAll(buf); | | |
| 1162 | global_progress.written_newline_count = global_progress.accumulated_newline_count; | 1176 | global_progress.written_newline_count = global_progress.accumulated_newline_count; |
| 1163 | } | 1177 | } |
| 1164 | | 1178 | |
| ... | @@ -1218,23 +1232,23 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { | ... | @@ -1218,23 +1232,23 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { |
| 1218 | fn maybeUpdateSize(resize_flag: bool) void { | 1232 | fn maybeUpdateSize(resize_flag: bool) void { |
| 1219 | if (!resize_flag) return; | 1233 | if (!resize_flag) return; |
| 1220 | | 1234 | |
| 1221 | const fd = (global_progress.terminal orelse return).handle; | 1235 | const fd = global_progress.terminal.handle; |
| 1222 | | 1236 | |
| 1223 | if (builtin.os.tag == .windows) { | 1237 | if (is_windows) { |
| 1224 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | 1238 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; |
| 1225 | | 1239 | |
| 1226 | if (windows.kernel32.GetConsoleScreenBufferInfo(fd, &info) == windows.FALSE) { | 1240 | if (windows.kernel32.GetConsoleScreenBufferInfo(fd, &info) != windows.FALSE) { |
| | 1241 | // In the old Windows console, dwSize.Y is the line count of the |
| | 1242 | // entire scrollback buffer, so we use this instead so that we |
| | 1243 | // always get the size of the screen. |
| | 1244 | const screen_height = info.srWindow.Bottom - info.srWindow.Top; |
| | 1245 | global_progress.rows = @intCast(screen_height); |
| | 1246 | global_progress.cols = @intCast(info.dwSize.X); |
| | 1247 | } else { |
| 1227 | std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{}); | 1248 | std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{}); |
| 1228 | global_progress.rows = 25; | 1249 | global_progress.rows = 25; |
| 1229 | global_progress.cols = 80; | 1250 | global_progress.cols = 80; |
| 1230 | } | 1251 | } |
| 1231 | | | |
| 1232 | // In the old Windows console, dwSize.Y is the line count of the entire | | |
| 1233 | // scrollback buffer, so we use this instead so that we always get the | | |
| 1234 | // size of the screen. | | |
| 1235 | const screen_height = info.srWindow.Bottom - info.srWindow.Top; | | |
| 1236 | global_progress.rows = @intCast(screen_height); | | |
| 1237 | global_progress.cols = @intCast(info.dwSize.X); | | |
| 1238 | } else { | 1252 | } else { |
| 1239 | var winsize: posix.winsize = .{ | 1253 | var winsize: posix.winsize = .{ |
| 1240 | .ws_row = 0, | 1254 | .ws_row = 0, |