diff --git a/homebrew/brew-upgrade-manager.sh b/homebrew/brew-upgrade-manager.sh index 2db211f..170119c 100644 --- a/homebrew/brew-upgrade-manager.sh +++ b/homebrew/brew-upgrade-manager.sh @@ -674,13 +674,36 @@ sub colorize_line { } } -my $buffer = ""; -while (sysread(STDIN, my $chunk, 8192)) { - $buffer .= $chunk; +my $sync_begin = "\e[?2026h"; +my $sync_end = "\e[?2026l"; +my $transport_buffer = ""; +my $normal_buffer = ""; +my $inside_sync_update = 0; - while (length $buffer) { - my $newline = index($buffer, "\n"); - my $carriage = index($buffer, "\r"); +sub suffix_prefix_length { + my ($text, $marker) = @_; + my $maximum = length($marker) - 1; + $maximum = length($text) if length($text) < $maximum; + + for (my $length = $maximum; $length > 0; $length--) { + return $length if substr($text, -$length) eq substr($marker, 0, $length); + } + return 0; +} + +sub emit_leading_control_sequences { + while ($normal_buffer =~ s/\A(\e\[[0-?]*[ -\/]*[@-~])//) { + print $1; + } +} + +sub drain_normal_output { + my ($final) = @_; + emit_leading_control_sequences(); + + while (length $normal_buffer) { + my $newline = index($normal_buffer, "\n"); + my $carriage = index($normal_buffer, "\r"); my $position; if ($newline < 0) { $position = $carriage; @@ -689,33 +712,79 @@ while (sysread(STDIN, my $chunk, 8192)) { } else { $position = $newline < $carriage ? $newline : $carriage; } - last if $position < 0; - my $separator = substr($buffer, $position, 1); + if ($position < 0) { + if ($final) { + colorize_line($normal_buffer); + $normal_buffer = ""; + } + return; + } + + my $separator = substr($normal_buffer, $position, 1); # 分块时 CR 可能正好在 CRLF 的边界,等待下一块再决定它是换行还是进度重绘。 - last if $separator eq "\r" && $position + 1 == length($buffer); + return if !$final && $separator eq "\r" && $position + 1 == length($normal_buffer); - my $record = substr($buffer, 0, $position); - if ($separator eq "\r" && substr($buffer, $position + 1, 1) eq "\n") { - substr($buffer, 0, $position + 2, ""); + my $record = substr($normal_buffer, 0, $position); + if ($separator eq "\r" && substr($normal_buffer, $position + 1, 1) eq "\n") { + substr($normal_buffer, 0, $position + 2, ""); colorize_line($record); } elsif ($separator eq "\n") { - substr($buffer, 0, $position + 1, ""); + substr($normal_buffer, 0, $position + 1, ""); colorize_line($record); } else { - substr($buffer, 0, $position + 1, ""); + substr($normal_buffer, 0, $position + 1, ""); print $record, "\r"; } + emit_leading_control_sequences(); } } -if (length $buffer) { - if ($buffer =~ s/\r\z//) { - print $buffer, "\r"; - } else { - colorize_line($buffer); +sub drain_transport_output { + my ($final) = @_; + + while (length $transport_buffer) { + if ($inside_sync_update) { + my $end_position = index($transport_buffer, $sync_end); + if ($end_position >= 0) { + my $length = $end_position + length($sync_end); + print substr($transport_buffer, 0, $length, ""); + $inside_sync_update = 0; + next; + } + + my $keep = $final ? 0 : suffix_prefix_length($transport_buffer, $sync_end); + my $available = length($transport_buffer) - $keep; + print substr($transport_buffer, 0, $available, "") if $available > 0; + return; + } + + my $begin_position = index($transport_buffer, $sync_begin); + if ($begin_position >= 0) { + $normal_buffer .= substr($transport_buffer, 0, $begin_position, ""); + drain_normal_output(0); + print substr($transport_buffer, 0, length($sync_begin), ""); + $inside_sync_update = 1; + next; + } + + my $keep = $final ? 0 : suffix_prefix_length($transport_buffer, $sync_begin); + my $available = length($transport_buffer) - $keep; + if ($available > 0) { + $normal_buffer .= substr($transport_buffer, 0, $available, ""); + drain_normal_output(0); + } + return; } } + +while (sysread(STDIN, my $chunk, 8192)) { + $transport_buffer .= $chunk; + drain_transport_output(0); +} + +drain_transport_output(1); +drain_normal_output(1); ' }