1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-30 04:10:54 -08:00

Implement ANSI SGR parameters 22-27.

* lisp/ansi-color.el (ansi-colors): Doc fix.
(ansi-color-context, ansi-color-context-region): Doc fix.
(ansi-color--find-face): New function.
(ansi-color-apply, ansi-color-apply-on-region): Use it.
Rename the local variable `face' to `codes' since it is now a list of
ansi codes.  Doc fix.
(ansi-color-get-face): Remove.
(ansi-color-parse-sequence): New function, derived from
ansi-color-get-face.
(ansi-color-apply-sequence): Use it.  Rewrite, and support ansi
codes 22-27.

Fixes: debbugs:12146
This commit is contained in:
Wolfgang Jenkner 2012-08-14 23:33:55 -04:00 committed by Stefan Monnier
parent b4f5e9df77
commit 2f29c200d8
2 changed files with 110 additions and 77 deletions

View file

@ -1,3 +1,18 @@
2012-08-15 Wolfgang Jenkner <wjenkner@inode.at>
Implement ANSI SGR parameters 22-27 (bug#12146).
* ansi-color.el (ansi-colors): Doc fix.
(ansi-color-context, ansi-color-context-region): Doc fix.
(ansi-color--find-face): New function.
(ansi-color-apply, ansi-color-apply-on-region): Use it.
Rename the local variable `face' to `codes' since it is now a list of
ansi codes. Doc fix.
(ansi-color-get-face): Remove.
(ansi-color-parse-sequence): New function, derived from
ansi-color-get-face.
(ansi-color-apply-sequence): Use it. Rewrite, and support ansi
codes 22-27.
2012-08-14 Stefan Monnier <monnier@iro.umontreal.ca>
* subr.el (read-passwd): Allow use from a minibuffer.

View file

@ -83,7 +83,7 @@
"Translating SGR control sequences to faces.
This translation effectively colorizes strings and regions based upon
SGR control sequences embedded in the text. SGR (Select Graphic
Rendition) control sequences are defined in section 3.8.117 of the
Rendition) control sequences are defined in section 8.3.117 of the
ECMA-48 standard \(identical to ISO/IEC 6429), which is freely available
as a PDF file <URL:http://www.ecma.ch/ecma1/STAND/ECMA-048.HTM>."
:version "21.1"
@ -236,9 +236,10 @@ This is a good function to put in `comint-output-filter-functions'."
;; Working with strings
(defvar ansi-color-context nil
"Context saved between two calls to `ansi-color-apply'.
This is a list of the form (FACES FRAGMENT) or nil. FACES is a list of
faces the last call to `ansi-color-apply' ended with, and FRAGMENT is a
string starting with an escape sequence, possibly the start of a new
This is a list of the form (CODES FRAGMENT) or nil. CODES
represents the state the last call to `ansi-color-apply' ended
with, currently a list of ansi codes, and FRAGMENT is a string
starting with an escape sequence, possibly the start of a new
escape sequence.")
(make-variable-buffer-local 'ansi-color-context)
@ -270,6 +271,20 @@ This function can be added to `comint-preoutput-filter-functions'."
(setq ansi-color-context (if fragment (list nil fragment))))
result))
(defun ansi-color--find-face (codes)
"Return the face corresponding to CODES."
(let (faces)
(while codes
(let ((face (ansi-color-get-face-1 (pop codes))))
;; In the (default underline) face, say, the value of the
;; "underline" attribute of the `default' face wins.
(unless (eq face 'default)
(push face faces))))
;; Avoid some long-lived conses in the common case.
(if (cdr faces)
(nreverse faces)
(car faces))))
(defun ansi-color-apply (string)
"Translates SGR control sequences into text properties.
Delete all other control sequences without processing them.
@ -280,12 +295,12 @@ are given in `ansi-color-faces-vector' and `ansi-color-names-vector'.
See function `ansi-color-apply-sequence' for details.
Every call to this function will set and use the buffer-local variable
`ansi-color-context' to save partial escape sequences and current face.
`ansi-color-context' to save partial escape sequences and current ansi codes.
This information will be used for the next call to `ansi-color-apply'.
Set `ansi-color-context' to nil if you don't want this.
This function can be added to `comint-preoutput-filter-functions'."
(let ((face (car ansi-color-context))
(let ((codes (car ansi-color-context))
(start 0) end escape-sequence result
colorized-substring)
;; If context was saved and is a string, prepend it.
@ -296,8 +311,8 @@ This function can be added to `comint-preoutput-filter-functions'."
(while (setq end (string-match ansi-color-regexp string start))
(setq escape-sequence (match-string 1 string))
;; Colorize the old block from start to end using old face.
(when face
(put-text-property start end 'font-lock-face face string))
(when codes
(put-text-property start end 'font-lock-face (ansi-color--find-face codes) string))
(setq colorized-substring (substring string start end)
start (match-end 0))
;; Eliminate unrecognized ANSI sequences.
@ -306,10 +321,10 @@ This function can be added to `comint-preoutput-filter-functions'."
(replace-match "" nil nil colorized-substring)))
(push colorized-substring result)
;; Create new face, by applying escape sequence parameters.
(setq face (ansi-color-apply-sequence escape-sequence face)))
(setq codes (ansi-color-apply-sequence escape-sequence codes)))
;; if the rest of the string should have a face, put it there
(when face
(put-text-property start (length string) 'font-lock-face face string))
(when codes
(put-text-property start (length string) 'font-lock-face (ansi-color--find-face codes) string))
;; save context, add the remainder of the string to the result
(let (fragment)
(if (string-match "\033" string start)
@ -317,17 +332,18 @@ This function can be added to `comint-preoutput-filter-functions'."
(setq fragment (substring string pos))
(push (substring string start pos) result))
(push (substring string start) result))
(setq ansi-color-context (if (or face fragment) (list face fragment))))
(setq ansi-color-context (if (or codes fragment) (list codes fragment))))
(apply 'concat (nreverse result))))
;; Working with regions
(defvar ansi-color-context-region nil
"Context saved between two calls to `ansi-color-apply-on-region'.
This is a list of the form (FACES MARKER) or nil. FACES is a list of
faces the last call to `ansi-color-apply-on-region' ended with, and
MARKER is a buffer position within an escape sequence or the last
position processed.")
This is a list of the form (CODES MARKER) or nil. CODES
represents the state the last call to `ansi-color-apply-on-region'
ended with, currently a list of ansi codes, and MARKER is a
buffer position within an escape sequence or the last position
processed.")
(make-variable-buffer-local 'ansi-color-context-region)
(defun ansi-color-filter-region (begin end)
@ -365,13 +381,14 @@ between BEGIN and END, using overlays. The colors used are given
in `ansi-color-faces-vector' and `ansi-color-names-vector'. See
`ansi-color-apply-sequence' for details.
Every call to this function will set and use the buffer-local variable
`ansi-color-context-region' to save position and current face. This
information will be used for the next call to
`ansi-color-apply-on-region'. Specifically, it will override BEGIN, the
start of the region and set the face with which to start. Set
`ansi-color-context-region' to nil if you don't want this."
(let ((face (car ansi-color-context-region))
Every call to this function will set and use the buffer-local
variable `ansi-color-context-region' to save position and current
ansi codes. This information will be used for the next call to
`ansi-color-apply-on-region'. Specifically, it will override
BEGIN, the start of the region and set the face with which to
start. Set `ansi-color-context-region' to nil if you don't want
this."
(let ((codes (car ansi-color-context-region))
(start-marker (or (cadr ansi-color-context-region)
(copy-marker begin)))
(end-marker (copy-marker end))
@ -388,28 +405,27 @@ start of the region and set the face with which to start. Set
;; Colorize the old block from start to end using old face.
(funcall ansi-color-apply-face-function
start-marker (match-beginning 0)
face)
(ansi-color--find-face codes))
;; store escape sequence and new start position
(setq escape-sequence (match-string 1)
start-marker (copy-marker (match-end 0)))
;; delete the escape sequence
(replace-match "")
;; create new face by applying all the parameters in the escape
;; sequence
(setq face (ansi-color-apply-sequence escape-sequence face)))
;; Update the list of ansi codes.
(setq codes (ansi-color-apply-sequence escape-sequence codes)))
;; search for the possible start of a new escape sequence
(if (re-search-forward "\033" end-marker t)
(progn
;; if the rest of the region should have a face, put it there
(funcall ansi-color-apply-face-function
start-marker (point) face)
;; save face and point
start-marker (point) (ansi-color--find-face codes))
;; save codes and point
(setq ansi-color-context-region
(list face (copy-marker (match-beginning 0)))))
(list codes (copy-marker (match-beginning 0)))))
;; if the rest of the region should have a face, put it there
(funcall ansi-color-apply-face-function
start-marker end-marker face)
(setq ansi-color-context-region (if face (list face)))))))
start-marker end-marker (ansi-color--find-face codes))
(setq ansi-color-context-region (if codes (list codes)))))))
(defun ansi-color-apply-overlay-face (beg end face)
"Make an overlay from BEG to END, and apply face FACE.
@ -497,32 +513,56 @@ XEmacs uses `set-extent-face', Emacs uses `overlay-put'."
;; Helper functions
(defun ansi-color-apply-sequence (escape-sequence faces)
"Apply ESCAPE-SEQ to FACES and return the new list of faces.
(defsubst ansi-color-parse-sequence (escape-seq)
"Return the list of all the parameters in ESCAPE-SEQ.
ESCAPE-SEQ is an escape sequences parsed by `ansi-color-get-face'.
ESCAPE-SEQ is a SGR control sequences such as \\033[34m. The parameter
34 is used by `ansi-color-get-face-1' to return a face definition.
If the new faces start with the symbol `default', then the new
faces are returned. If the faces start with something else,
they are appended to the front of the FACES list, and the new
list of faces is returned.
Returns nil only if there's no match for `ansi-color-parameter-regexp'."
(let ((i 0)
codes val)
(while (string-match ansi-color-parameter-regexp escape-seq i)
(setq i (match-end 0)
val (string-to-number (match-string 1 escape-seq) 10))
;; It so happens that (string-to-number "") => 0.
(push val codes))
(nreverse codes)))
If `ansi-color-get-face' returns nil, then we either got a
null-sequence, or we stumbled upon some garbage. In either
case we return nil."
(let ((new-faces (ansi-color-get-face escape-sequence)))
(cond ((null new-faces)
nil)
((eq (car new-faces) 'default)
(cdr new-faces))
(t
;; Like (append NEW-FACES FACES)
;; but delete duplicates in FACES.
(let ((modified-faces (copy-sequence faces)))
(dolist (face (nreverse new-faces))
(setq modified-faces (delete face modified-faces))
(push face modified-faces))
modified-faces)))))
(defun ansi-color-apply-sequence (escape-sequence codes)
"Apply ESCAPE-SEQ to CODES and return the new list of codes.
ESCAPE-SEQ is an escape sequence parsed by `ansi-color-parse-sequence'.
If the new codes resulting from ESCAPE-SEQ start with 0, then the
old codes are discarded and the remaining new codes are
processed. Otherwise, for each new code: if it is 21-25 or 27-29
delete appropriate parameters from the list of codes; any other
code that makes sense is added to the list of codes. Finally,
the so changed list of codes is returned."
(let ((new-codes (ansi-color-parse-sequence escape-sequence)))
(while new-codes
(setq codes
(let ((new (pop new-codes)))
(cond ((zerop new)
nil)
((or (<= new 20)
(>= new 30))
(if (memq new codes)
codes
(cons new codes)))
;; The standard says `21 doubly underlined' while
;; http://en.wikipedia.org/wiki/ANSI_escape_code claims
;; `21 Bright/Bold: off or Underline: Double'.
((/= new 26)
(remq (- new 20)
(cond ((= new 22)
(remq 1 codes))
((= new 25)
(remq 6 codes))
(t codes))))
(t codes)))))
codes))
(defun ansi-color-make-color-map ()
"Creates a vector of face definitions and returns it.
@ -588,28 +628,6 @@ ANSI-CODE is used as an index into the vector."
(aref ansi-color-map ansi-code)
(args-out-of-range nil)))
(defun ansi-color-get-face (escape-seq)
"Create a new face by applying all the parameters in ESCAPE-SEQ.
Should any of the parameters result in the default face (usually this is
the parameter 0), then the effect of all previous parameters is canceled.
ESCAPE-SEQ is a SGR control sequences such as \\033[34m. The parameter
34 is used by `ansi-color-get-face-1' to return a face definition."
(let ((i 0)
f val)
(while (string-match ansi-color-parameter-regexp escape-seq i)
(setq i (match-end 0)
val (ansi-color-get-face-1
(string-to-number (match-string 1 escape-seq) 10)))
(cond ((not val))
((eq val 'default)
(setq f (list val)))
(t
(unless (member val f)
(push val f)))))
f))
(provide 'ansi-color)
;;; ansi-color.el ends here