Commit Diff


commit - e3f361530d5b3c16f397a90c6f03e76715955985
commit + 6ae7a56e0f08ffd6e04a5312789e91ddb3059fc9
blob - 13f6ba8319a779dea33ffc6865f0366a04382848
blob + a1f4b1c0cbcb37cca869106d9198736e47d3467c
--- exercise
+++ exercise
@@ -110,11 +110,11 @@ proc execute_test {countervar path} {
 
     # Report test failures and errors to our stderr.
     if {$what ne "ok"} {
-        set foot "$what $path"
+        set foot [list "$what $path"]
         if {[isatty stderr]} {
-            set foot [color {33} $foot]  ;# dark yellow
+            set foot [color 3 $foot]    ;# dark yellow
         }
-        puts stderr $foot
+        puts stderr [join $foot ""]
         puts stderr ""
     }
 
@@ -177,10 +177,58 @@ proc isatty {chan} {
     dict exists [fconfigure $chan] -mode
 }
 
+# ANSI (ECMA-48) escape sequences
+set esc(bu)     [list "\033\[4m"]           ;# begin underline
+set esc(eu)     [list "\033\[24m"]          ;# end underline
+set esc(ce)     [list "\033\[0K"]           ;# clear to end
+set esc(endc)   [list "\033\[39m"]          ;# end color
+set esc(endcb)  [list "\033\[39;22m"]       ;# end color and bold
+
 proc color {c s} {
-    return "\033\[${c}m$s\033\[0m"
+    global esc
+    concat [list "\033\[3${c}m"] $s $esc(endc)
 }
 
+proc boldc {c s} {
+    global esc
+    concat [list "\033\[1;3${c}m"] $s $esc(endcb)
+}
+
+proc cinsert {line pos args} {
+    set cur 0
+    set idx 0
+
+    foreach elem $line {
+        if {$cur == $pos} {
+            return [linsert $line $idx {*}$args]
+        }
+        if {[string index $elem 0] ne "\033"} {
+            set len [string length $elem]
+
+            if {$pos < $cur + $len} {
+                set split [expr {$pos - $cur}]
+                set left [string range $elem 0 [expr {$split - 1}]]
+                set right [string range $elem $split end]
+                return [lreplace $line $idx $idx $left {*}$args $right]
+            }
+
+            incr cur $len
+        }
+        incr idx
+    }
+
+    # if we reach here, pos is beyond the end of line
+    lappend line [string repeat " " [expr {$pos - $cur}]] {*}$args
+}
+
+proc clength {line} {
+    foreach elem $line {
+        if {[string index $elem 0] eq "\033"} continue
+        incr len [string length $elem]
+    }
+    return $len
+}
+
 proc inconsistent {counters} {
     dict with counters {
         expr {$run < $ok + $fail + $error}
@@ -194,23 +242,36 @@ proc notok {counters} {
 }
 
 proc summary {counters} {
+    global esc
+
     dict with counters {
-        set r "run $run"
-        set o "ok $ok"
-        set f "fail $fail"
-        set e "error $error"
+        set r [list "run $run"]
+        set o [list "ok $ok"]
+        set f [list "fail $fail"]
+        set e [list "error $error"]
 
         # color
         if {[isatty stdout]} {
             set good [expr {$ok>0 && ![notok $counters]}]
 
-            if {$run == 0}      {set r [color {31} $r]}     ;# dark red
-            if {$good}          {set o [color {1;32} $o]}   ;# bright green
-            if {$fail > 0}      {set f [color {1;31} $f]}   ;# bright red
-            if {$error > 0}     {set e [color {31} $e]}     ;# dark red
+            if {$run == 0}      {set r [color 1 $r]}    ;# dark red
+            if {$good}          {set o [boldc 2 $o]}    ;# bright green
+            if {$fail > 0}      {set f [boldc 1 $f]}    ;# bright red
+            if {$error > 0}     {set e [color 1 $e]}    ;# dark red
         }
 
-        return "$r  $o  $f  $e"
+        set line [concat $r "{  }" $o "{  }" $f "{  }" $e]
+
+        # progress bar, using underlining
+        if {[isatty stdout]} {
+            set ran [expr {$ok + $fail + $error}]
+            if {$run > 0 && $ran < $run} {
+                set pos [expr {[clength $line] * $ran / $run}]
+                set line [concat $esc(bu) [cinsert $line $pos {*}$esc(eu)]]
+            }
+        }
+
+        return [join $line ""]
     }
 }