Subject: [bug#59975] [PATCH] guix: Show better progress bars.

From: Julien Lepiller <[email-masked]>
Date: Sun, 11 Dec 2022 19:00:59 +0100

Hi Guix!

The attached patch is a small improvement on our progress bars. Instead
of our cute ASCII art:

1.2MiB/s 00:04 [###               ]  18.5%

We get something a little more smooth:

1.1MiB/s 00:04 ▕███               ▏  17.1%
1.2MiB/s 00:05 ▕███▋              ▏  20.7%

Using unicode characters that can represent 1/8 of a character width.

I used port-encoding to detect when the output supports unicode, but
maybe there's something more dedicated to figuring that out? When the
port encoding is not UTF-8, we fall back to the ASCII version.

Thoughts?
>From c428c80fd628797ae80029a0a22678ef55c68d6c Mon Sep 17 00:00:00 2001
From: Julien Lepiller <[email-masked]>
Date: Sun, 11 Dec 2022 18:51:13 +0100
Subject: [PATCH] guix: Show better progress bars.

* guix/progress.scm (progress-bar): When supported, use unicode variant.
---
 guix/progress.scm | 43 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/guix/progress.scm b/guix/progress.scm
index 4f8e98edc0..527bf72839 100644
--- a/guix/progress.scm
+++ b/guix/progress.scm
@@ -166,16 +166,45 @@ (define current-terminal-columns
   ;; Number of columns of the terminal.
   (make-parameter 80))
 
+(define-record-type* <progress-bar-style>
+  progress-bar-style make-progress-bar-style progress-bar-style?
+  (start  progress-bar-style-start
+          (default #\x2595))
+  (stop   progress-bar-style-stop
+          (default #\x258f))
+  (filled progress-bar-style-filled
+          (default #\x2588))
+  (steps  progress-bar-style-steps
+          (default '(#\x258F #\x258E #\x258D #\x258C #\x258B #\x258A #\x2589))))
+
+(define unicode-bar-style (progress-bar-style))
+(define ascii-bar-style
+  (progress-bar-style
+    (start #\[)
+    (stop #\])
+    (filled #\#)
+    (steps '())))
+
 (define* (progress-bar % #:optional (bar-width 20))
   "Return % as a string representing an ASCII-art progress bar.  The total
 width of the bar is BAR-WIDTH."
-  (let* ((bar-width (max 3 (- bar-width 2)))
-         (fraction (/ % 100))
-         (filled   (inexact->exact (floor (* fraction bar-width))))
-         (empty    (- bar-width filled)))
-    (format #f "[~a~a]"
-            (make-string filled #\#)
-            (make-string empty #\space))))
+  (let* ((bar-style (if (equal? (port-encoding (current-output-port)) "UTF-8")
+                        unicode-bar-style
+                        ascii-bar-style))
+         (bar-width (max 3 (- bar-width 2)))
+         (intermediates (+ (length (progress-bar-style-steps bar-style)) 1))
+         (step     (inexact->exact (floor (/ (* % bar-width intermediates) 100))))
+         (filled   (quotient step intermediates))
+         (intermediate
+           (list-ref (cons #f (progress-bar-style-steps bar-style))
+                     (modulo step intermediates)))
+         (empty    (- bar-width filled (if intermediate 1 0))))
+    (format #f "~a~a~a~a~a"
+            (string (progress-bar-style-start bar-style))
+            (make-string filled (progress-bar-style-filled bar-style))
+            (if intermediate (string intermediate) "")
+            (make-string empty #\space)
+            (string (progress-bar-style-stop bar-style)))))
 
 (define (erase-current-line port)
   "Write an ANSI erase-current-line sequence to PORT to erase the whole line and
-- 
2.38.1