[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 76895fc 3/4: Add support for 256-color and 24bit ANSI colors in t
From: |
Lars Ingebrigtsen |
Subject: |
master 76895fc 3/4: Add support for 256-color and 24bit ANSI colors in term-mode |
Date: |
Tue, 5 Oct 2021 02:55:42 -0400 (EDT) |
branch: master
commit 76895fcd0b667eadd78bfe6cf51619f8b00e157f
Author: Miha Rihtaršič <miha@kamnitnik.top>
Commit: Lars Ingebrigtsen <larsi@gnus.org>
Add support for 256-color and 24bit ANSI colors in term-mode
(term-ansi-face-already-done): Make obsolete
(term--maybe-brighten-color): Remove
(term--color-as-hex): New function
(term-handle-colors-array): Make obsolete in favour of the new
function 'term--handle-colors-list'.
(term--handle-colors-list): New function, that can also handle ANSI
codes 38 and 48.
(term-handle-ansi-escape): Use it
* test/lisp/term-tests.el (ansi-test-strings): Add tests for 256-color
and 24bit ANSI colors
---
etc/NEWS | 7 ++
etc/e/README | 18 ++--
etc/e/eterm-color | Bin 1179 -> 1275 bytes
etc/e/eterm-color.ti | 15 ++-
etc/e/eterm-direct | Bin 0 -> 1354 bytes
lisp/term.el | 249 +++++++++++++++++++++++-------------------------
test/lisp/term-tests.el | 16 +++-
7 files changed, 163 insertions(+), 142 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS
index c2dde4e..3c16e2f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -89,6 +89,13 @@ mode (instead of at load time).
256-color and 24-bit color codes are now handled by ANSI color
filters and displayed with the specified color.
+** term-mode
+
+---
+*** Support for ANSI 256-color and 24-bit colors.
+256-color and 24-bit color codes are now displayed with the specified
+color.
+
* New Modes and Packages in Emacs 29.1
diff --git a/etc/e/README b/etc/e/README
index dd2c8d6..1293292 100644
--- a/etc/e/README
+++ b/etc/e/README
@@ -1,12 +1,12 @@
-eterm-color.ti is a terminfo source file. eterm-color is a compiled
-version produced by the terminfo compiler (tic). The compiled files
-are binary, and depend on the version of tic, but they seem to be
-system-independent and backwardly compatible. So there should be no
-need to recompile the distributed binary version. If it is
-necessary, use:
+eterm-color.ti is a terminfo source file. eterm-color and
+eterm-direct are compiled versions produced by the terminfo compiler
+(tic). The compiled files are binary, and depend on the version of
+tic, but they seem to be system-independent and backwardly compatible.
+So there should be no need to recompile the distributed binary
+version. If it is necessary, use:
tic -o ../ ./eterm-color.ti
-The compiled file is used by lisp/term.el, so if it is moved term.el
-needs to be changed. terminfo requires it to be stored in an 'e'
-subdirectory (the first character of the file name).
+The compiled files are used by lisp/term.el, so if they are moved,
+term.el needs to be changed. terminfo requires them to be stored in
+an 'e' subdirectory (the first character of the file name).
diff --git a/etc/e/eterm-color b/etc/e/eterm-color
index bd3f500..99603ba 100644
Binary files a/etc/e/eterm-color and b/etc/e/eterm-color differ
diff --git a/etc/e/eterm-color.ti b/etc/e/eterm-color.ti
index a6ef814..61c29e6 100644
--- a/etc/e/eterm-color.ti
+++ b/etc/e/eterm-color.ti
@@ -9,10 +9,10 @@ eterm-color|Emacs term.el terminal emulator
term-protocol-version 0.96,
# Any change to this file should be done at the same time with a
# corresponding change to the TERMCAP environment variable in term.el.
# Comments in term.el specify where each of these capabilities is implemented.
- colors#8,
+ colors#256,
cols#80,
lines#24,
- pairs#64,
+ pairs#32767,
am,
mir,
msgr,
@@ -65,8 +65,8 @@ eterm-color|Emacs term.el terminal emulator
term-protocol-version 0.96,
rmul=\E[24m,
rs1=\Ec,
sc=\E7,
- setab=\E[%p1%{40}%+%dm,
- setaf=\E[%p1%{30}%+%dm,
+ setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
+ setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
sgr0=\E[m,
smir=\E[4h,
smul=\E[4m,
@@ -76,3 +76,10 @@ eterm-color|Emacs term.el terminal emulator
term-protocol-version 0.96,
# smcup=\E[?47h,
# rmcup=\E[?47l,
# rs2 may need to be added
+
+eterm-direct|Emacs term.el with direct-color indexing term-protocol-version
0.96,
+ use=eterm-color,
+ colors#0x1000000,
+ pairs#0x10000,
+
setab=\E[%?%p1%{8}%<%t4%p1%d%e48;2;%p1%{65536}%/%d;%p1%{256}%/%{255}%&%d;%p1%{255}%&%d%;m,
+
setaf=\E[%?%p1%{8}%<%t3%p1%d%e38;2;%p1%{65536}%/%d;%p1%{256}%/%{255}%&%d;%p1%{255}%&%d%;m,
diff --git a/etc/e/eterm-direct b/etc/e/eterm-direct
new file mode 100644
index 0000000..35983ec
Binary files /dev/null and b/etc/e/eterm-direct differ
diff --git a/lisp/term.el b/lisp/term.el
index e76eb77..771b732 100644
--- a/lisp/term.el
+++ b/lisp/term.el
@@ -303,6 +303,7 @@
(require 'ange-ftp)
(require 'cl-lib))
(require 'comint) ; Password regexp.
+(require 'ansi-color)
(require 'ehelp)
(require 'ring)
(require 'shell)
@@ -717,6 +718,9 @@ Buffer local variable.")
(defvar term-ansi-current-reverse nil)
(defvar term-ansi-current-invisible nil)
+(make-obsolete-variable 'term-ansi-face-already-done
+ "it doesn't have any effect." "28.1")
+
;;; Faces
(defvar ansi-term-color-vector
[term
@@ -1039,10 +1043,6 @@ is buffer-local."
(setq term-ansi-current-reverse nil)
(setq term-ansi-current-color 0)
(setq term-ansi-current-invisible nil)
- ;; Stefan thought this should be t, but could not remember why.
- ;; Setting it to t seems to cause bug#11785. Setting it to nil
- ;; again to see if there are other consequences...
- (setq term-ansi-face-already-done nil)
(setq term-ansi-current-bg-color 0))
(define-derived-mode term-mode fundamental-mode "Term"
@@ -1584,7 +1584,8 @@ Using \"emacs\" loses, because bash disables editing if
$TERM == emacs.")
:so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:md=\\E[1m:mr=\\E[7m:me=\\E[m\
:UP=\\E[%%dA:DO=\\E[%%dB:LE=\\E[%%dD:RI=\\E[%%dC\
:kl=\\EOD:kd=\\EOB:kr=\\EOC:ku=\\EOA:kN=\\E[6~:kP=\\E[5~:@7=\\E[4~:kh=\\E[1~\
-:mk=\\E[8m:cb=\\E[1K:op=\\E[39;49m:Co#8:pa#64:AB=\\E[4%%dm:AF=\\E[3%%dm:cr=^M\
+:mk=\\E[8m:cb=\\E[1K:op=\\E[39;49m:Co#256:pa#32767\
+:AB=\\E[48;5;%%dm:AF=\\E[38;5;%%dm:cr=^M\
:bl=^G:do=^J:le=^H:ta=^I:se=\\E[27m:ue=\\E[24m\
:kb=^?:kD=^[[3~:sc=\\E7:rc=\\E8:r1=\\Ec:"
;; : -undefine ic
@@ -3285,133 +3286,125 @@ option is enabled. See `term-set-goto-process-mark'."
(setq term-current-row 0)
(setq term-current-column 1)
(term--reset-scroll-region)
- (setq term-insert-mode nil)
- ;; FIXME: No idea why this is here, it looks wrong. --Stef
- (setq term-ansi-face-already-done nil))
-
-(defun term--maybe-brighten-color (color bold)
- "Possibly convert COLOR to its bright variant.
-COLOR is an index into `ansi-term-color-vector'. If BOLD and
-`ansi-color-bold-is-bright' are non-nil and COLOR is a regular color,
-return the bright version of COLOR; otherwise, return COLOR."
- (if (and ansi-color-bold-is-bright bold (<= 1 color 8))
- (+ color 8)
- color))
+ (setq term-insert-mode nil))
+
+(defun term--color-as-hex (for-foreground)
+ "Return the current ANSI color as a hexadecimal color string.
+Use the current background color if FOR-FOREGROUND is nil,
+otherwise use the current foreground color."
+ (let ((color (if for-foreground term-ansi-current-color
+ term-ansi-current-bg-color)))
+ (or (ansi-color--code-as-hex (1- color))
+ (progn
+ (and ansi-color-bold-is-bright term-ansi-current-bold
+ (<= 1 color 8)
+ (setq color (+ color 8)))
+ (if for-foreground
+ (face-foreground (elt ansi-term-color-vector color)
+ nil 'default)
+ (face-background (elt ansi-term-color-vector color)
+ nil 'default))))))
;; New function to deal with ansi colorized output, as you can see you can
;; have any bold/underline/fg/bg/reverse combination. -mm
(defun term-handle-colors-array (parameter)
- (cond
-
- ;; Bold (terminfo: bold)
- ((eq parameter 1)
- (setq term-ansi-current-bold t))
-
- ;; Underline
- ((eq parameter 4)
- (setq term-ansi-current-underline t))
-
- ;; Blink (unsupported by Emacs), will be translated to bold.
- ;; This may change in the future though.
- ((eq parameter 5)
- (setq term-ansi-current-bold t))
-
- ;; Reverse (terminfo: smso)
- ((eq parameter 7)
- (setq term-ansi-current-reverse t))
-
- ;; Invisible
- ((eq parameter 8)
- (setq term-ansi-current-invisible t))
-
- ;; Reset underline (terminfo: rmul)
- ((eq parameter 24)
- (setq term-ansi-current-underline nil))
-
- ;; Reset reverse (terminfo: rmso)
- ((eq parameter 27)
- (setq term-ansi-current-reverse nil))
-
- ;; Foreground
- ((and (>= parameter 30) (<= parameter 37))
- (setq term-ansi-current-color (- parameter 29)))
-
- ;; Bright foreground
- ((and (>= parameter 90) (<= parameter 97))
- (setq term-ansi-current-color (- parameter 81)))
-
- ;; Reset foreground
- ((eq parameter 39)
- (setq term-ansi-current-color 0))
-
- ;; Background
- ((and (>= parameter 40) (<= parameter 47))
- (setq term-ansi-current-bg-color (- parameter 39)))
-
- ;; Bright foreground
- ((and (>= parameter 100) (<= parameter 107))
- (setq term-ansi-current-bg-color (- parameter 91)))
-
- ;; Reset background
- ((eq parameter 49)
- (setq term-ansi-current-bg-color 0))
-
- ;; 0 (Reset) or unknown (reset anyway)
- (t
- (term-ansi-reset)))
-
- ;; (message "Debug: U-%d R-%d B-%d I-%d D-%d F-%d B-%d"
- ;; term-ansi-current-underline
- ;; term-ansi-current-reverse
- ;; term-ansi-current-bold
- ;; term-ansi-current-invisible
- ;; term-ansi-face-already-done
- ;; term-ansi-current-color
- ;; term-ansi-current-bg-color)
-
- (unless term-ansi-face-already-done
- (let ((current-color (term--maybe-brighten-color
- term-ansi-current-color
- term-ansi-current-bold))
- (current-bg-color (term--maybe-brighten-color
- term-ansi-current-bg-color
- term-ansi-current-bold)))
- (if term-ansi-current-invisible
- (let ((color
- (if term-ansi-current-reverse
- (face-foreground
- (elt ansi-term-color-vector current-color)
- nil 'default)
- (face-background
- (elt ansi-term-color-vector current-bg-color)
- nil 'default))))
- (setq term-current-face
- (list :background color
- :foreground color))
- ) ;; No need to bother with anything else if it's invisible.
- (setq term-current-face
- (list :foreground
- (face-foreground
- (elt ansi-term-color-vector current-color)
- nil 'default)
- :background
- (face-background
- (elt ansi-term-color-vector current-bg-color)
- nil 'default)
- :inverse-video term-ansi-current-reverse))
-
- (when term-ansi-current-bold
- (setq term-current-face
- `(,term-current-face :inherit term-bold)))
-
- (when term-ansi-current-underline
- (setq term-current-face
- `(,term-current-face :inherit term-underline))))))
-
- ;; (message "Debug %S" term-current-face)
- ;; FIXME: shouldn't we set term-ansi-face-already-done to t here? --Stef
- (setq term-ansi-face-already-done nil))
+ (declare (obsolete term--handle-colors-list "28.1"))
+ (term--handle-colors-list (list parameter)))
+
+(defun term--handle-colors-list (parameters)
+ (while parameters
+ (pcase (pop parameters)
+ (1 (setq term-ansi-current-bold t)) ; (terminfo: bold)
+ (4 (setq term-ansi-current-underline t)) ; (terminfo: smul)
+ (5 (setq term-ansi-current-bold t)) ; (terminfo: bold)
+ (7 (setq term-ansi-current-reverse t)) ; (terminfo: smso, rev)
+ (8 (setq term-ansi-current-invisible t)) ; (terminfo: invis)
+ (24 (setq term-ansi-current-underline nil)) ; (terminfo: rmul)
+ (27 (setq term-ansi-current-reverse nil)) ; (terminfo: rmso)
+
+ ;; Foreground (terminfo: setaf)
+ ((and param (guard (<= 30 param 37)))
+ (setq term-ansi-current-color (- param 29)))
+
+ ;; Bright foreground (terminfo: setaf)
+ ((and param (guard (<= 90 param 97)))
+ (setq term-ansi-current-color (- param 81)))
+
+ ;; Extended foreground (terminfo: setaf)
+ (38
+ (pcase (pop parameters)
+ ;; 256 color
+ (5 (if (setq term-ansi-current-color (pop parameters))
+ (cl-incf term-ansi-current-color)
+ (term-ansi-reset)))
+ ;; Full 24-bit color
+ (2 (cl-loop with color = (1+ 256) ; Base
+ for i from 16 downto 0 by 8
+ if (pop parameters)
+ do (setq color (+ color (ash it i)))
+ else return (term-ansi-reset)
+ finally
+ (if (> color (+ 1 256 #xFFFFFF))
+ (term-ansi-reset)
+ (setq term-ansi-current-color color))))
+ (_ (term-ansi-reset))))
+
+ ;; Reset foreground (terminfo: op)
+ (39 (setq term-ansi-current-color 0))
+
+ ;; Background (terminfo: setab)
+ ((and param (guard (<= 40 param 47)))
+ (setq term-ansi-current-bg-color (- param 39)))
+
+ ;; Bright background (terminfo: setab)
+ ((and param (guard (<= 100 param 107)))
+ (setq term-ansi-current-bg-color (- param 91)))
+
+ ;; Extended background (terminfo: setab)
+ (48
+ (pcase (pop parameters)
+ ;; 256 color
+ (5 (if (setq term-ansi-current-bg-color (pop parameters))
+ (cl-incf term-ansi-current-bg-color)
+ (term-ansi-reset)))
+ ;; Full 24-bit color
+ (2 (cl-loop with color = (1+ 256) ; Base
+ for i from 16 downto 0 by 8
+ if (pop parameters)
+ do (setq color (+ color (ash it i)))
+ else return (term-ansi-reset)
+ finally
+ (if (> color (+ 1 256 #xFFFFFF))
+ (term-ansi-reset)
+ (setq term-ansi-current-bg-color color))))
+ (_ (term-ansi-reset))))
+
+ ;; Reset background (terminfo: op)
+ (49 (setq term-ansi-current-bg-color 0))
+
+ ;; 0 (Reset) (terminfo: sgr0) or unknown (reset anyway)
+ (_ (term-ansi-reset))))
+
+ (let (fg bg)
+ (if term-ansi-current-invisible
+ (setq bg (term--color-as-hex term-ansi-current-reverse)
+ fg bg)
+ (setq fg (term--color-as-hex t)
+ bg (term--color-as-hex nil)))
+ (setq term-current-face
+ `( :foreground ,fg
+ :background ,bg
+ ,@(unless term-ansi-current-invisible
+ (list :inverse-video term-ansi-current-reverse)))))
+
+ (when term-ansi-current-bold
+ (setq term-current-face
+ `(,term-current-face :inherit term-bold)))
+
+ (when term-ansi-current-underline
+ (setq term-current-face
+ `(,term-current-face :inherit term-underline))))
;; Handle a character assuming (eq terminal-state 2) -
@@ -3499,7 +3492,7 @@ return the bright version of COLOR; otherwise, return
COLOR."
;; \E[m - Set/reset modes, set bg/fg
;;(terminfo: smso,rmso,smul,rmul,rev,bold,sgr0,invis,op,setab,setaf)
((eq char ?m)
- (mapc #'term-handle-colors-array params))
+ (term--handle-colors-list params))
;; \E[6n - Report cursor position (terminfo: u7)
((eq char ?n)
diff --git a/test/lisp/term-tests.el b/test/lisp/term-tests.el
index 96b6d73..b8adc62 100644
--- a/test/lisp/term-tests.el
+++ b/test/lisp/term-tests.el
@@ -42,6 +42,9 @@
`( :foreground "unspecified-fg"
:background ,(face-background 'term-color-bright-yellow nil 'default)
:inverse-video nil))
+(defvar custom-color-fg-props
+ `( :foreground "#87FFFF"
+ :background "unspecified-bg" :inverse-video nil))
(defvar ansi-test-strings
`(("\e[33mHello World\e[0m"
@@ -71,7 +74,18 @@
,(propertize "Hello World" 'font-lock-face
`(,yellow-fg-props :inherit term-bold))
,(propertize "Hello World" 'font-lock-face
- `(,bright-yellow-fg-props :inherit term-bold)))))
+ `(,bright-yellow-fg-props :inherit term-bold)))
+ ("\e[38;5;3;1mHello World\e[0m"
+ ,(propertize "Hello World" 'font-lock-face
+ `(,yellow-fg-props :inherit term-bold))
+ ,(propertize "Hello World" 'font-lock-face
+ `(,bright-yellow-fg-props :inherit term-bold)))
+ ("\e[38;5;123;1mHello World\e[0m"
+ ,(propertize "Hello World" 'font-lock-face
+ `(,custom-color-fg-props :inherit term-bold)))
+ ("\e[38;2;135;255;255;1mHello World\e[0m"
+ ,(propertize "Hello World" 'font-lock-face
+ `(,custom-color-fg-props :inherit term-bold)))))
(defun term-test-screen-from-input (width height input &optional return-var)
(with-temp-buffer