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
This commit is contained in:
parent
0fa2279b90
commit
76895fcd0b
7 changed files with 149 additions and 128 deletions
7
etc/NEWS
7
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
|
||||
|
||||
|
|
18
etc/e/README
18
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).
|
||||
|
|
Binary file not shown.
|
@ -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,
|
||||
|
|
BIN
etc/e/eterm-direct
Normal file
BIN
etc/e/eterm-direct
Normal file
Binary file not shown.
221
lisp/term.el
221
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))
|
||||
(setq term-insert-mode 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))
|
||||
(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
|
||||
(declare (obsolete term--handle-colors-list "28.1"))
|
||||
(term--handle-colors-list (list parameter)))
|
||||
|
||||
;; Bold (terminfo: bold)
|
||||
((eq parameter 1)
|
||||
(setq term-ansi-current-bold t))
|
||||
(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)
|
||||
|
||||
;; Underline
|
||||
((eq parameter 4)
|
||||
(setq term-ansi-current-underline t))
|
||||
;; Foreground (terminfo: setaf)
|
||||
((and param (guard (<= 30 param 37)))
|
||||
(setq term-ansi-current-color (- param 29)))
|
||||
|
||||
;; 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))
|
||||
;; Bright foreground (terminfo: setaf)
|
||||
((and param (guard (<= 90 param 97)))
|
||||
(setq term-ansi-current-color (- param 81)))
|
||||
|
||||
;; Reverse (terminfo: smso)
|
||||
((eq parameter 7)
|
||||
(setq term-ansi-current-reverse t))
|
||||
;; 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))))
|
||||
|
||||
;; Invisible
|
||||
((eq parameter 8)
|
||||
(setq term-ansi-current-invisible t))
|
||||
;; Reset foreground (terminfo: op)
|
||||
(39 (setq term-ansi-current-color 0))
|
||||
|
||||
;; Reset underline (terminfo: rmul)
|
||||
((eq parameter 24)
|
||||
(setq term-ansi-current-underline nil))
|
||||
;; Background (terminfo: setab)
|
||||
((and param (guard (<= 40 param 47)))
|
||||
(setq term-ansi-current-bg-color (- param 39)))
|
||||
|
||||
;; Reset reverse (terminfo: rmso)
|
||||
((eq parameter 27)
|
||||
(setq term-ansi-current-reverse nil))
|
||||
;; Bright background (terminfo: setab)
|
||||
((and param (guard (<= 100 param 107)))
|
||||
(setq term-ansi-current-bg-color (- param 91)))
|
||||
|
||||
;; Foreground
|
||||
((and (>= parameter 30) (<= parameter 37))
|
||||
(setq term-ansi-current-color (- parameter 29)))
|
||||
;; 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))))
|
||||
|
||||
;; Bright foreground
|
||||
((and (>= parameter 90) (<= parameter 97))
|
||||
(setq term-ansi-current-color (- parameter 81)))
|
||||
;; Reset background (terminfo: op)
|
||||
(49 (setq term-ansi-current-bg-color 0))
|
||||
|
||||
;; Reset foreground
|
||||
((eq parameter 39)
|
||||
(setq term-ansi-current-color 0))
|
||||
;; 0 (Reset) (terminfo: sgr0) or unknown (reset anyway)
|
||||
(_ (term-ansi-reset))))
|
||||
|
||||
;; Background
|
||||
((and (>= parameter 40) (<= parameter 47))
|
||||
(setq term-ansi-current-bg-color (- parameter 39)))
|
||||
(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)))))
|
||||
|
||||
;; Bright foreground
|
||||
((and (>= parameter 100) (<= parameter 107))
|
||||
(setq term-ansi-current-bg-color (- parameter 91)))
|
||||
(when term-ansi-current-bold
|
||||
(setq term-current-face
|
||||
`(,term-current-face :inherit term-bold)))
|
||||
|
||||
;; 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))
|
||||
(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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue