From: Miha Rihtaršič Date: Tue, 5 Oct 2021 06:53:36 +0000 (+0200) Subject: Add support for 256-color and 24bit ANSI colors in ansi-color X-Git-Tag: emacs-29.0.90~3671^2~658 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=0fa2279b90bf5a638d8377032b71135e1374e8fb;p=emacs.git Add support for 256-color and 24bit ANSI colors in ansi-color * lisp/ansi-color.el (ansi-color--code-as-hex): New function to convert from 256-color and 24-bit ANSI codes. (ansi-color--face-vec-face): Add support for ANSI color codes greater than 16 (ansi-color--update-face-vec): Add support for ANSI codes 38 and 48 which can specify 256-color and 24bit ANSI colors. * test/lisp/ansi-color-tests.el (ansi-color-tests--strings): Add tests for ANSI codes 38 and 34 --- diff --git a/etc/NEWS b/etc/NEWS index ef8d95af0ba..c2dde4ea331 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -82,6 +82,13 @@ mode (instead of at load time). +++ *** New macro 'with-memoization' provides a very primitive form of memoization +** ansi-color.el + +--- +*** Support for ANSI 256-color and 24-bit colors. +256-color and 24-bit color codes are now handled by ANSI color +filters and displayed with the specified color. + * New Modes and Packages in Emacs 29.1 diff --git a/lisp/ansi-color.el b/lisp/ansi-color.el index 3878c73ded2..aaaf60cd00d 100644 --- a/lisp/ansi-color.el +++ b/lisp/ansi-color.el @@ -594,22 +594,24 @@ code. It is usually stored as the car of the variable (when-let ((fg (car colors))) (push `(:foreground - ,(face-foreground - (aref (if (or bright (>= fg 8)) - ansi-color-bright-colors-vector - ansi-color-normal-colors-vector) - (mod fg 8)) - nil 'default)) + ,(or (ansi-color--code-as-hex fg) + (face-foreground + (aref (if (or bright (>= fg 8)) + ansi-color-bright-colors-vector + ansi-color-normal-colors-vector) + (mod fg 8)) + nil 'default))) faces)) (when-let ((bg (cadr colors))) (push `(:background - ,(face-background - (aref (if (or bright (>= bg 8)) - ansi-color-bright-colors-vector - ansi-color-normal-colors-vector) - (mod bg 8)) - nil 'default)) + ,(or (ansi-color--code-as-hex bg) + (face-background + (aref (if (or bright (>= bg 8)) + ansi-color-bright-colors-vector + ansi-color-normal-colors-vector) + (mod bg 8)) + nil 'default))) faces)) (let ((i 8)) @@ -622,6 +624,32 @@ code. It is usually stored as the car of the variable faces (car faces)))) +(defun ansi-color--code-as-hex (color) + "Convert COLOR to hexadecimal string representation. +COLOR is an ANSI color code. If it is between 16 and 255 +inclusive, it corresponds to a color from an 8-bit color cube. +If it is greater or equal than 256, it is subtracted by 256 to +directly specify a 24-bit color. + +Return a hexadecimal string, specifying the color, or nil, if +COLOR is less than 16." + (cond + ((< color 16) nil) + ((>= color 256) (format "#%06X" (- color 256))) + ((>= color 232) ;; Grayscale + (format "#%06X" (* #x010101 (+ 8 (* 10 (- color 232)))))) + (t ;; 6x6x6 color cube + (setq color (- color 16)) + (let ((res 0) + (frac (* 6 6))) + (while (<= 1 frac) ; Repeat 3 times + (setq res (* res #x000100)) + (let ((color-num (mod (/ color frac) 6))) + (unless (zerop color-num) + (setq res (+ res #x37 (* #x28 color-num))))) + (setq frac (/ frac 6))) + (format "#%06X" res))))) + ;; Working with regions (defvar-local ansi-color-context-region nil @@ -907,7 +935,23 @@ unset all properties and colors." (let ((r (mod new 10)) (cell (if (memq q '(3 9)) colors (cdr colors)))) (pcase r - (8 (setq do-clear t)) + (8 + (pcase (funcall iterator) + (5 (setq new (setcar cell (funcall iterator))) + (setq do-clear (or (null new) (>= new 256)))) + (2 + (let ((red (funcall iterator)) + (green (funcall iterator)) + (blue (funcall iterator))) + (if (and red green blue + (progn + (setq new (+ (* #x010000 red) + (* #x000100 green) + (* #x000001 blue))) + (<= new #xFFFFFF))) + (setcar cell (+ 256 new)) + (setq do-clear t)))) + (_ (setq do-clear t)))) (9 (setcar cell nil)) (_ (setcar cell (+ (if (memq q '(3 4)) 0 8) r)))))) (_ (setq do-clear t))) diff --git a/test/lisp/ansi-color-tests.el b/test/lisp/ansi-color-tests.el index 953fdff8933..16a1ba4a892 100644 --- a/test/lisp/ansi-color-tests.el +++ b/test/lisp/ansi-color-tests.el @@ -27,7 +27,8 @@ (defvar ansi-color-tests--strings (let ((bright-yellow (face-foreground 'ansi-color-bright-yellow nil 'default)) - (yellow (face-foreground 'ansi-color-yellow nil 'default))) + (yellow (face-foreground 'ansi-color-yellow nil 'default)) + (custom-color "#87FFFF")) `(("Hello World" "Hello World") ("\e[33mHello World\e[0m" "Hello World" (:foreground ,yellow)) @@ -51,7 +52,14 @@ (ansi-color-bold (:foreground ,bright-yellow))) ("\e[1m\e[3m\e[5mbold italics blink\e[0m" "bold italics blink" (ansi-color-bold ansi-color-italic ansi-color-slow-blink)) - ("\e[10munrecognized\e[0m" "unrecognized")))) + ("\e[10munrecognized\e[0m" "unrecognized") + ("\e[38;5;3;1mHello World\e[0m" "Hello World" + (ansi-color-bold (:foreground ,yellow)) + (ansi-color-bold (:foreground ,bright-yellow))) + ("\e[48;5;123;1mHello World\e[0m" "Hello World" + (ansi-color-bold (:background ,custom-color))) + ("\e[48;2;135;255;255;1mHello World\e[0m" "Hello World" + (ansi-color-bold (:background ,custom-color)))))) (ert-deftest ansi-color-apply-on-region-test () (pcase-dolist (`(,input ,text ,face) ansi-color-tests--strings)