aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWolfgang Jenkner2012-08-14 23:33:55 -0400
committerStefan Monnier2012-08-14 23:33:55 -0400
commit2f29c200d84a5f3a3b4b2cc09fbc5000e38ee21f (patch)
tree8581043b2f89fe95ad7c8a995b2b52892dd7f79b
parentb4f5e9df77f42a033b64c2be00a4c6ca7bcf0c58 (diff)
downloademacs-2f29c200d84a5f3a3b4b2cc09fbc5000e38ee21f.tar.gz
emacs-2f29c200d84a5f3a3b4b2cc09fbc5000e38ee21f.zip
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
-rw-r--r--lisp/ChangeLog15
-rw-r--r--lisp/ansi-color.el174
2 files changed, 111 insertions, 78 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index ddbb1c2d3df..824c0e2601b 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,18 @@
12012-08-15 Wolfgang Jenkner <wjenkner@inode.at>
2
3 Implement ANSI SGR parameters 22-27 (bug#12146).
4 * ansi-color.el (ansi-colors): Doc fix.
5 (ansi-color-context, ansi-color-context-region): Doc fix.
6 (ansi-color--find-face): New function.
7 (ansi-color-apply, ansi-color-apply-on-region): Use it.
8 Rename the local variable `face' to `codes' since it is now a list of
9 ansi codes. Doc fix.
10 (ansi-color-get-face): Remove.
11 (ansi-color-parse-sequence): New function, derived from
12 ansi-color-get-face.
13 (ansi-color-apply-sequence): Use it. Rewrite, and support ansi
14 codes 22-27.
15
12012-08-14 Stefan Monnier <monnier@iro.umontreal.ca> 162012-08-14 Stefan Monnier <monnier@iro.umontreal.ca>
2 17
3 * subr.el (read-passwd): Allow use from a minibuffer. 18 * subr.el (read-passwd): Allow use from a minibuffer.
diff --git a/lisp/ansi-color.el b/lisp/ansi-color.el
index 18b2c846274..8305aaf1199 100644
--- a/lisp/ansi-color.el
+++ b/lisp/ansi-color.el
@@ -83,7 +83,7 @@
83 "Translating SGR control sequences to faces. 83 "Translating SGR control sequences to faces.
84This translation effectively colorizes strings and regions based upon 84This translation effectively colorizes strings and regions based upon
85SGR control sequences embedded in the text. SGR (Select Graphic 85SGR control sequences embedded in the text. SGR (Select Graphic
86Rendition) control sequences are defined in section 3.8.117 of the 86Rendition) control sequences are defined in section 8.3.117 of the
87ECMA-48 standard \(identical to ISO/IEC 6429), which is freely available 87ECMA-48 standard \(identical to ISO/IEC 6429), which is freely available
88as a PDF file <URL:http://www.ecma.ch/ecma1/STAND/ECMA-048.HTM>." 88as a PDF file <URL:http://www.ecma.ch/ecma1/STAND/ECMA-048.HTM>."
89 :version "21.1" 89 :version "21.1"
@@ -236,9 +236,10 @@ This is a good function to put in `comint-output-filter-functions'."
236;; Working with strings 236;; Working with strings
237(defvar ansi-color-context nil 237(defvar ansi-color-context nil
238 "Context saved between two calls to `ansi-color-apply'. 238 "Context saved between two calls to `ansi-color-apply'.
239This is a list of the form (FACES FRAGMENT) or nil. FACES is a list of 239This is a list of the form (CODES FRAGMENT) or nil. CODES
240faces the last call to `ansi-color-apply' ended with, and FRAGMENT is a 240represents the state the last call to `ansi-color-apply' ended
241string starting with an escape sequence, possibly the start of a new 241with, currently a list of ansi codes, and FRAGMENT is a string
242starting with an escape sequence, possibly the start of a new
242escape sequence.") 243escape sequence.")
243(make-variable-buffer-local 'ansi-color-context) 244(make-variable-buffer-local 'ansi-color-context)
244 245
@@ -270,6 +271,20 @@ This function can be added to `comint-preoutput-filter-functions'."
270 (setq ansi-color-context (if fragment (list nil fragment)))) 271 (setq ansi-color-context (if fragment (list nil fragment))))
271 result)) 272 result))
272 273
274(defun ansi-color--find-face (codes)
275 "Return the face corresponding to CODES."
276 (let (faces)
277 (while codes
278 (let ((face (ansi-color-get-face-1 (pop codes))))
279 ;; In the (default underline) face, say, the value of the
280 ;; "underline" attribute of the `default' face wins.
281 (unless (eq face 'default)
282 (push face faces))))
283 ;; Avoid some long-lived conses in the common case.
284 (if (cdr faces)
285 (nreverse faces)
286 (car faces))))
287
273(defun ansi-color-apply (string) 288(defun ansi-color-apply (string)
274 "Translates SGR control sequences into text properties. 289 "Translates SGR control sequences into text properties.
275Delete all other control sequences without processing them. 290Delete all other control sequences without processing them.
@@ -280,12 +295,12 @@ are given in `ansi-color-faces-vector' and `ansi-color-names-vector'.
280See function `ansi-color-apply-sequence' for details. 295See function `ansi-color-apply-sequence' for details.
281 296
282Every call to this function will set and use the buffer-local variable 297Every call to this function will set and use the buffer-local variable
283`ansi-color-context' to save partial escape sequences and current face. 298`ansi-color-context' to save partial escape sequences and current ansi codes.
284This information will be used for the next call to `ansi-color-apply'. 299This information will be used for the next call to `ansi-color-apply'.
285Set `ansi-color-context' to nil if you don't want this. 300Set `ansi-color-context' to nil if you don't want this.
286 301
287This function can be added to `comint-preoutput-filter-functions'." 302This function can be added to `comint-preoutput-filter-functions'."
288 (let ((face (car ansi-color-context)) 303 (let ((codes (car ansi-color-context))
289 (start 0) end escape-sequence result 304 (start 0) end escape-sequence result
290 colorized-substring) 305 colorized-substring)
291 ;; If context was saved and is a string, prepend it. 306 ;; If context was saved and is a string, prepend it.
@@ -296,8 +311,8 @@ This function can be added to `comint-preoutput-filter-functions'."
296 (while (setq end (string-match ansi-color-regexp string start)) 311 (while (setq end (string-match ansi-color-regexp string start))
297 (setq escape-sequence (match-string 1 string)) 312 (setq escape-sequence (match-string 1 string))
298 ;; Colorize the old block from start to end using old face. 313 ;; Colorize the old block from start to end using old face.
299 (when face 314 (when codes
300 (put-text-property start end 'font-lock-face face string)) 315 (put-text-property start end 'font-lock-face (ansi-color--find-face codes) string))
301 (setq colorized-substring (substring string start end) 316 (setq colorized-substring (substring string start end)
302 start (match-end 0)) 317 start (match-end 0))
303 ;; Eliminate unrecognized ANSI sequences. 318 ;; Eliminate unrecognized ANSI sequences.
@@ -306,10 +321,10 @@ This function can be added to `comint-preoutput-filter-functions'."
306 (replace-match "" nil nil colorized-substring))) 321 (replace-match "" nil nil colorized-substring)))
307 (push colorized-substring result) 322 (push colorized-substring result)
308 ;; Create new face, by applying escape sequence parameters. 323 ;; Create new face, by applying escape sequence parameters.
309 (setq face (ansi-color-apply-sequence escape-sequence face))) 324 (setq codes (ansi-color-apply-sequence escape-sequence codes)))
310 ;; if the rest of the string should have a face, put it there 325 ;; if the rest of the string should have a face, put it there
311 (when face 326 (when codes
312 (put-text-property start (length string) 'font-lock-face face string)) 327 (put-text-property start (length string) 'font-lock-face (ansi-color--find-face codes) string))
313 ;; save context, add the remainder of the string to the result 328 ;; save context, add the remainder of the string to the result
314 (let (fragment) 329 (let (fragment)
315 (if (string-match "\033" string start) 330 (if (string-match "\033" string start)
@@ -317,17 +332,18 @@ This function can be added to `comint-preoutput-filter-functions'."
317 (setq fragment (substring string pos)) 332 (setq fragment (substring string pos))
318 (push (substring string start pos) result)) 333 (push (substring string start pos) result))
319 (push (substring string start) result)) 334 (push (substring string start) result))
320 (setq ansi-color-context (if (or face fragment) (list face fragment)))) 335 (setq ansi-color-context (if (or codes fragment) (list codes fragment))))
321 (apply 'concat (nreverse result)))) 336 (apply 'concat (nreverse result))))
322 337
323;; Working with regions 338;; Working with regions
324 339
325(defvar ansi-color-context-region nil 340(defvar ansi-color-context-region nil
326 "Context saved between two calls to `ansi-color-apply-on-region'. 341 "Context saved between two calls to `ansi-color-apply-on-region'.
327This is a list of the form (FACES MARKER) or nil. FACES is a list of 342This is a list of the form (CODES MARKER) or nil. CODES
328faces the last call to `ansi-color-apply-on-region' ended with, and 343represents the state the last call to `ansi-color-apply-on-region'
329MARKER is a buffer position within an escape sequence or the last 344ended with, currently a list of ansi codes, and MARKER is a
330position processed.") 345buffer position within an escape sequence or the last position
346processed.")
331(make-variable-buffer-local 'ansi-color-context-region) 347(make-variable-buffer-local 'ansi-color-context-region)
332 348
333(defun ansi-color-filter-region (begin end) 349(defun ansi-color-filter-region (begin end)
@@ -365,13 +381,14 @@ between BEGIN and END, using overlays. The colors used are given
365in `ansi-color-faces-vector' and `ansi-color-names-vector'. See 381in `ansi-color-faces-vector' and `ansi-color-names-vector'. See
366`ansi-color-apply-sequence' for details. 382`ansi-color-apply-sequence' for details.
367 383
368Every call to this function will set and use the buffer-local variable 384Every call to this function will set and use the buffer-local
369`ansi-color-context-region' to save position and current face. This 385variable `ansi-color-context-region' to save position and current
370information will be used for the next call to 386ansi codes. This information will be used for the next call to
371`ansi-color-apply-on-region'. Specifically, it will override BEGIN, the 387`ansi-color-apply-on-region'. Specifically, it will override
372start of the region and set the face with which to start. Set 388BEGIN, the start of the region and set the face with which to
373`ansi-color-context-region' to nil if you don't want this." 389start. Set `ansi-color-context-region' to nil if you don't want
374 (let ((face (car ansi-color-context-region)) 390this."
391 (let ((codes (car ansi-color-context-region))
375 (start-marker (or (cadr ansi-color-context-region) 392 (start-marker (or (cadr ansi-color-context-region)
376 (copy-marker begin))) 393 (copy-marker begin)))
377 (end-marker (copy-marker end)) 394 (end-marker (copy-marker end))
@@ -388,28 +405,27 @@ start of the region and set the face with which to start. Set
388 ;; Colorize the old block from start to end using old face. 405 ;; Colorize the old block from start to end using old face.
389 (funcall ansi-color-apply-face-function 406 (funcall ansi-color-apply-face-function
390 start-marker (match-beginning 0) 407 start-marker (match-beginning 0)
391 face) 408 (ansi-color--find-face codes))
392 ;; store escape sequence and new start position 409 ;; store escape sequence and new start position
393 (setq escape-sequence (match-string 1) 410 (setq escape-sequence (match-string 1)
394 start-marker (copy-marker (match-end 0))) 411 start-marker (copy-marker (match-end 0)))
395 ;; delete the escape sequence 412 ;; delete the escape sequence
396 (replace-match "") 413 (replace-match "")
397 ;; create new face by applying all the parameters in the escape 414 ;; Update the list of ansi codes.
398 ;; sequence 415 (setq codes (ansi-color-apply-sequence escape-sequence codes)))
399 (setq face (ansi-color-apply-sequence escape-sequence face)))
400 ;; search for the possible start of a new escape sequence 416 ;; search for the possible start of a new escape sequence
401 (if (re-search-forward "\033" end-marker t) 417 (if (re-search-forward "\033" end-marker t)
402 (progn 418 (progn
403 ;; if the rest of the region should have a face, put it there 419 ;; if the rest of the region should have a face, put it there
404 (funcall ansi-color-apply-face-function 420 (funcall ansi-color-apply-face-function
405 start-marker (point) face) 421 start-marker (point) (ansi-color--find-face codes))
406 ;; save face and point 422 ;; save codes and point
407 (setq ansi-color-context-region 423 (setq ansi-color-context-region
408 (list face (copy-marker (match-beginning 0))))) 424 (list codes (copy-marker (match-beginning 0)))))
409 ;; if the rest of the region should have a face, put it there 425 ;; if the rest of the region should have a face, put it there
410 (funcall ansi-color-apply-face-function 426 (funcall ansi-color-apply-face-function
411 start-marker end-marker face) 427 start-marker end-marker (ansi-color--find-face codes))
412 (setq ansi-color-context-region (if face (list face))))))) 428 (setq ansi-color-context-region (if codes (list codes)))))))
413 429
414(defun ansi-color-apply-overlay-face (beg end face) 430(defun ansi-color-apply-overlay-face (beg end face)
415 "Make an overlay from BEG to END, and apply face FACE. 431 "Make an overlay from BEG to END, and apply face FACE.
@@ -497,32 +513,56 @@ XEmacs uses `set-extent-face', Emacs uses `overlay-put'."
497 513
498;; Helper functions 514;; Helper functions
499 515
500(defun ansi-color-apply-sequence (escape-sequence faces) 516(defsubst ansi-color-parse-sequence (escape-seq)
501 "Apply ESCAPE-SEQ to FACES and return the new list of faces. 517 "Return the list of all the parameters in ESCAPE-SEQ.
502
503ESCAPE-SEQ is an escape sequences parsed by `ansi-color-get-face'.
504 518
505If the new faces start with the symbol `default', then the new 519ESCAPE-SEQ is a SGR control sequences such as \\033[34m. The parameter
506faces are returned. If the faces start with something else, 52034 is used by `ansi-color-get-face-1' to return a face definition.
507they are appended to the front of the FACES list, and the new
508list of faces is returned.
509 521
510If `ansi-color-get-face' returns nil, then we either got a 522Returns nil only if there's no match for `ansi-color-parameter-regexp'."
511null-sequence, or we stumbled upon some garbage. In either 523 (let ((i 0)
512case we return nil." 524 codes val)
513 (let ((new-faces (ansi-color-get-face escape-sequence))) 525 (while (string-match ansi-color-parameter-regexp escape-seq i)
514 (cond ((null new-faces) 526 (setq i (match-end 0)
515 nil) 527 val (string-to-number (match-string 1 escape-seq) 10))
516 ((eq (car new-faces) 'default) 528 ;; It so happens that (string-to-number "") => 0.
517 (cdr new-faces)) 529 (push val codes))
518 (t 530 (nreverse codes)))
519 ;; Like (append NEW-FACES FACES) 531
520 ;; but delete duplicates in FACES. 532(defun ansi-color-apply-sequence (escape-sequence codes)
521 (let ((modified-faces (copy-sequence faces))) 533 "Apply ESCAPE-SEQ to CODES and return the new list of codes.
522 (dolist (face (nreverse new-faces)) 534
523 (setq modified-faces (delete face modified-faces)) 535ESCAPE-SEQ is an escape sequence parsed by `ansi-color-parse-sequence'.
524 (push face modified-faces)) 536
525 modified-faces))))) 537If the new codes resulting from ESCAPE-SEQ start with 0, then the
538old codes are discarded and the remaining new codes are
539processed. Otherwise, for each new code: if it is 21-25 or 27-29
540delete appropriate parameters from the list of codes; any other
541code that makes sense is added to the list of codes. Finally,
542the so changed list of codes is returned."
543 (let ((new-codes (ansi-color-parse-sequence escape-sequence)))
544 (while new-codes
545 (setq codes
546 (let ((new (pop new-codes)))
547 (cond ((zerop new)
548 nil)
549 ((or (<= new 20)
550 (>= new 30))
551 (if (memq new codes)
552 codes
553 (cons new codes)))
554 ;; The standard says `21 doubly underlined' while
555 ;; http://en.wikipedia.org/wiki/ANSI_escape_code claims
556 ;; `21 Bright/Bold: off or Underline: Double'.
557 ((/= new 26)
558 (remq (- new 20)
559 (cond ((= new 22)
560 (remq 1 codes))
561 ((= new 25)
562 (remq 6 codes))
563 (t codes))))
564 (t codes)))))
565 codes))
526 566
527(defun ansi-color-make-color-map () 567(defun ansi-color-make-color-map ()
528 "Creates a vector of face definitions and returns it. 568 "Creates a vector of face definitions and returns it.
@@ -588,28 +628,6 @@ ANSI-CODE is used as an index into the vector."
588 (aref ansi-color-map ansi-code) 628 (aref ansi-color-map ansi-code)
589 (args-out-of-range nil))) 629 (args-out-of-range nil)))
590 630
591(defun ansi-color-get-face (escape-seq)
592 "Create a new face by applying all the parameters in ESCAPE-SEQ.
593
594Should any of the parameters result in the default face (usually this is
595the parameter 0), then the effect of all previous parameters is canceled.
596
597ESCAPE-SEQ is a SGR control sequences such as \\033[34m. The parameter
59834 is used by `ansi-color-get-face-1' to return a face definition."
599 (let ((i 0)
600 f val)
601 (while (string-match ansi-color-parameter-regexp escape-seq i)
602 (setq i (match-end 0)
603 val (ansi-color-get-face-1
604 (string-to-number (match-string 1 escape-seq) 10)))
605 (cond ((not val))
606 ((eq val 'default)
607 (setq f (list val)))
608 (t
609 (unless (member val f)
610 (push val f)))))
611 f))
612
613(provide 'ansi-color) 631(provide 'ansi-color)
614 632
615;;; ansi-color.el ends here 633;;; ansi-color.el ends here