colors: Introduce a disjoint type and pre-compute ANSI escapes.

* guix/colors.scm (color-table, color): Remove.
(<color>): New record type.
(print-color): New procedure.
(define-color-table, color): New macros.
(color-codes->ansi): New procedure.
(%reset): New variable.
(colorize-string): Rewrite accordingly.
(color-rules): Adjust accordingly.
* guix/status.scm (print-build-event): Adjust to new 'colorize-string'
interface.
* guix/ui.scm (%highlight-argument): Likewise.
(%warning-colors, %info-colors, %error-colors, %hint-colors)
(%highlight-colors): Remove.
(%warning-color, %info-color, %error-color, %hint-color)
(%highlight-color): New variables.
This commit is contained in:
Ludovic Courtès 2019-04-11 16:57:38 +02:00
parent c1df77e215
commit 2569ef9dab
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
3 changed files with 101 additions and 65 deletions

View File

@ -22,9 +22,14 @@
(define-module (guix colors) (define-module (guix colors)
#:use-module (guix memoization) #:use-module (guix memoization)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-9)
#:use-module (srfi srfi-9 gnu)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:use-module (ice-9 regex) #:use-module (ice-9 regex)
#:export (colorize-string #:export (color
color?
colorize-string
color-rules color-rules
color-output? color-output?
isatty?*)) isatty?*))
@ -35,55 +40,86 @@
;;; ;;;
;;; Code: ;;; Code:
(define color-table ;; Record type for "colors", which are actually lists of color attributes.
`((CLEAR . "0") (define-record-type <color>
(RESET . "0") (make-color symbols ansi)
(BOLD . "1") color?
(DARK . "2") (symbols color-symbols)
(UNDERLINE . "4") (ansi color-ansi))
(UNDERSCORE . "4")
(BLINK . "5")
(REVERSE . "6")
(CONCEALED . "8")
(BLACK . "30")
(RED . "31")
(GREEN . "32")
(YELLOW . "33")
(BLUE . "34")
(MAGENTA . "35")
(CYAN . "36")
(WHITE . "37")
(ON-BLACK . "40")
(ON-RED . "41")
(ON-GREEN . "42")
(ON-YELLOW . "43")
(ON-BLUE . "44")
(ON-MAGENTA . "45")
(ON-CYAN . "46")
(ON-WHITE . "47")))
(define (color . lst) (define (print-color color port)
"Return a string containing the ANSI escape sequence for producing the (format port "#<color ~a>"
requested set of attributes in LST. Unknown attributes are ignored." (string-join (map symbol->string
(let ((color-list (color-symbols color)))))
(remove not
(map (lambda (color) (assq-ref color-table color))
lst))))
(if (null? color-list)
""
(string-append
(string #\esc #\[)
(string-join color-list ";" 'infix)
"m"))))
(define (colorize-string str . color-list) (set-record-type-printer! <color> print-color)
"Return a copy of STR colorized using ANSI escape sequences according to the
attributes STR. At the end of the returned string, the color attributes will (define-syntax define-color-table
be reset such that subsequent output will not have any colors in effect." (syntax-rules ()
(string-append "Define NAME as a macro that builds a list of color attributes."
(apply color color-list) ((_ name (color escape) ...)
str (begin
(color 'RESET))) (define-syntax color-codes
(syntax-rules (color ...)
((_)
'())
((_ color rest (... ...))
`(escape ,@(color-codes rest (... ...))))
...))
(define-syntax-rule (name colors (... ...))
"Return a list of color attributes that can be passed to
'colorize-string'."
(make-color '(colors (... ...))
(color-codes->ansi (color-codes colors (... ...)))))))))
(define-color-table color
(CLEAR "0")
(RESET "0")
(BOLD "1")
(DARK "2")
(UNDERLINE "4")
(UNDERSCORE "4")
(BLINK "5")
(REVERSE "6")
(CONCEALED "8")
(BLACK "30")
(RED "31")
(GREEN "32")
(YELLOW "33")
(BLUE "34")
(MAGENTA "35")
(CYAN "36")
(WHITE "37")
(ON-BLACK "40")
(ON-RED "41")
(ON-GREEN "42")
(ON-YELLOW "43")
(ON-BLUE "44")
(ON-MAGENTA "45")
(ON-CYAN "46")
(ON-WHITE "47"))
(define (color-codes->ansi codes)
"Convert CODES, a list of color attribute codes, to a ANSI escape string."
(match codes
(()
"")
(_
(string-append (string #\esc #\[)
(string-join codes ";" 'infix)
"m"))))
(define %reset
(color RESET))
(define (colorize-string str color)
"Return a copy of STR colorized using ANSI escape sequences according to
COLOR. At the end of the returned string, the color attributes are reset such
that subsequent output will not have any colors in effect."
(string-append (color-ansi color)
str
(color-ansi %reset)))
(define isatty?* (define isatty?*
(mlambdaq (port) (mlambdaq (port)
@ -114,7 +150,7 @@ on."
(match (regexp-exec rx str) (match (regexp-exec rx str)
(#f (next str)) (#f (next str))
(m (let loop ((n 1) (m (let loop ((n 1)
(c '(colors ...)) (c (list (color colors) ...))
(result '())) (result '()))
(match c (match c
(() (()

View File

@ -410,17 +410,17 @@ produce colorful output. When PRINT-LOG? is true, display the build log in
addition to build events." addition to build events."
(define info (define info
(if colorize? (if colorize?
(cut colorize-string <> 'BOLD) (cute colorize-string <> (color BOLD))
identity)) identity))
(define success (define success
(if colorize? (if colorize?
(cut colorize-string <> 'GREEN 'BOLD) (cute colorize-string <> (color GREEN BOLD))
identity)) identity))
(define failure (define failure
(if colorize? (if colorize?
(cut colorize-string <> 'RED 'BOLD) (cute colorize-string <> (color RED BOLD))
identity)) identity))
(define (report-build-progress phase %) (define (report-build-progress phase %)

View File

@ -158,7 +158,7 @@ is a trivial format string."
(define highlight (define highlight
(if (color-output? port) (if (color-output? port)
(lambda (str) (lambda (str)
(apply colorize-string str %highlight-colors)) (colorize-string str %highlight-color))
identity)) identity))
(cond ((string? arg) (cond ((string? arg)
@ -206,9 +206,9 @@ messages."
;; XXX: This doesn't work well for right-to-left languages. ;; XXX: This doesn't work well for right-to-left languages.
;; TRANSLATORS: The goal is to emit "warning:" followed by a short phrase; ;; TRANSLATORS: The goal is to emit "warning:" followed by a short phrase;
;; "~a" is a placeholder for that phrase. ;; "~a" is a placeholder for that phrase.
(define-diagnostic warning (G_ "warning: ") %warning-colors) ;emit a warning (define-diagnostic warning (G_ "warning: ") %warning-color) ;emit a warning
(define-diagnostic info (G_ "") %info-colors) (define-diagnostic info (G_ "") %info-color)
(define-diagnostic report-error (G_ "error: ") %error-colors) (define-diagnostic report-error (G_ "error: ") %error-color)
(define-syntax-rule (leave args ...) (define-syntax-rule (leave args ...)
"Emit an error message and exit." "Emit an error message and exit."
@ -216,27 +216,27 @@ messages."
(report-error args ...) (report-error args ...)
(exit 1))) (exit 1)))
(define %warning-colors '(BOLD MAGENTA)) (define %warning-color (color BOLD MAGENTA))
(define %info-colors '(BOLD)) (define %info-color (color BOLD))
(define %error-colors '(BOLD RED)) (define %error-color (color BOLD RED))
(define %hint-colors '(BOLD CYAN)) (define %hint-color (color BOLD CYAN))
(define %highlight-colors '(BOLD)) (define %highlight-color (color BOLD))
(define* (print-diagnostic-prefix prefix #:optional location (define* (print-diagnostic-prefix prefix #:optional location
#:key (colors '())) #:key (colors (color)))
"Print PREFIX as a diagnostic line prefix." "Print PREFIX as a diagnostic line prefix."
(define color? (define color?
(color-output? (guix-warning-port))) (color-output? (guix-warning-port)))
(define location-color (define location-color
(if color? (if color?
(cut colorize-string <> 'BOLD) (cut colorize-string <> (color BOLD))
identity)) identity))
(define prefix-color (define prefix-color
(if color? (if color?
(lambda (prefix) (lambda (prefix)
(apply colorize-string prefix colors)) (colorize-string prefix colors))
identity)) identity))
(let ((prefix (if (string-null? prefix) (let ((prefix (if (string-null? prefix)
@ -404,7 +404,7 @@ PORT."
(define colorize (define colorize
(if (color-output? port) (if (color-output? port)
(lambda (str) (lambda (str)
(apply colorize-string str %hint-colors)) (colorize-string str %hint-color))
identity)) identity))
(display (colorize (G_ "hint: ")) port) (display (colorize (G_ "hint: ")) port)