diff options
| author | Wolfgang Jenkner | 2012-08-14 23:33:55 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2012-08-14 23:33:55 -0400 |
| commit | 2f29c200d84a5f3a3b4b2cc09fbc5000e38ee21f (patch) | |
| tree | 8581043b2f89fe95ad7c8a995b2b52892dd7f79b | |
| parent | b4f5e9df77f42a033b64c2be00a4c6ca7bcf0c58 (diff) | |
| download | emacs-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/ChangeLog | 15 | ||||
| -rw-r--r-- | lisp/ansi-color.el | 174 |
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 @@ | |||
| 1 | 2012-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 | |||
| 1 | 2012-08-14 Stefan Monnier <monnier@iro.umontreal.ca> | 16 | 2012-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. |
| 84 | This translation effectively colorizes strings and regions based upon | 84 | This translation effectively colorizes strings and regions based upon |
| 85 | SGR control sequences embedded in the text. SGR (Select Graphic | 85 | SGR control sequences embedded in the text. SGR (Select Graphic |
| 86 | Rendition) control sequences are defined in section 3.8.117 of the | 86 | Rendition) control sequences are defined in section 8.3.117 of the |
| 87 | ECMA-48 standard \(identical to ISO/IEC 6429), which is freely available | 87 | ECMA-48 standard \(identical to ISO/IEC 6429), which is freely available |
| 88 | as a PDF file <URL:http://www.ecma.ch/ecma1/STAND/ECMA-048.HTM>." | 88 | as 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'. |
| 239 | This is a list of the form (FACES FRAGMENT) or nil. FACES is a list of | 239 | This is a list of the form (CODES FRAGMENT) or nil. CODES |
| 240 | faces the last call to `ansi-color-apply' ended with, and FRAGMENT is a | 240 | represents the state the last call to `ansi-color-apply' ended |
| 241 | string starting with an escape sequence, possibly the start of a new | 241 | with, currently a list of ansi codes, and FRAGMENT is a string |
| 242 | starting with an escape sequence, possibly the start of a new | ||
| 242 | escape sequence.") | 243 | escape 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. |
| 275 | Delete all other control sequences without processing them. | 290 | Delete all other control sequences without processing them. |
| @@ -280,12 +295,12 @@ are given in `ansi-color-faces-vector' and `ansi-color-names-vector'. | |||
| 280 | See function `ansi-color-apply-sequence' for details. | 295 | See function `ansi-color-apply-sequence' for details. |
| 281 | 296 | ||
| 282 | Every call to this function will set and use the buffer-local variable | 297 | Every 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. |
| 284 | This information will be used for the next call to `ansi-color-apply'. | 299 | This information will be used for the next call to `ansi-color-apply'. |
| 285 | Set `ansi-color-context' to nil if you don't want this. | 300 | Set `ansi-color-context' to nil if you don't want this. |
| 286 | 301 | ||
| 287 | This function can be added to `comint-preoutput-filter-functions'." | 302 | This 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'. |
| 327 | This is a list of the form (FACES MARKER) or nil. FACES is a list of | 342 | This is a list of the form (CODES MARKER) or nil. CODES |
| 328 | faces the last call to `ansi-color-apply-on-region' ended with, and | 343 | represents the state the last call to `ansi-color-apply-on-region' |
| 329 | MARKER is a buffer position within an escape sequence or the last | 344 | ended with, currently a list of ansi codes, and MARKER is a |
| 330 | position processed.") | 345 | buffer position within an escape sequence or the last position |
| 346 | processed.") | ||
| 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 | |||
| 365 | in `ansi-color-faces-vector' and `ansi-color-names-vector'. See | 381 | in `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 | ||
| 368 | Every call to this function will set and use the buffer-local variable | 384 | Every call to this function will set and use the buffer-local |
| 369 | `ansi-color-context-region' to save position and current face. This | 385 | variable `ansi-color-context-region' to save position and current |
| 370 | information will be used for the next call to | 386 | ansi 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 |
| 372 | start of the region and set the face with which to start. Set | 388 | BEGIN, 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." | 389 | start. Set `ansi-color-context-region' to nil if you don't want |
| 374 | (let ((face (car ansi-color-context-region)) | 390 | this." |
| 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 | |||
| 503 | ESCAPE-SEQ is an escape sequences parsed by `ansi-color-get-face'. | ||
| 504 | 518 | ||
| 505 | If the new faces start with the symbol `default', then the new | 519 | ESCAPE-SEQ is a SGR control sequences such as \\033[34m. The parameter |
| 506 | faces are returned. If the faces start with something else, | 520 | 34 is used by `ansi-color-get-face-1' to return a face definition. |
| 507 | they are appended to the front of the FACES list, and the new | ||
| 508 | list of faces is returned. | ||
| 509 | 521 | ||
| 510 | If `ansi-color-get-face' returns nil, then we either got a | 522 | Returns nil only if there's no match for `ansi-color-parameter-regexp'." |
| 511 | null-sequence, or we stumbled upon some garbage. In either | 523 | (let ((i 0) |
| 512 | case 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)) | 535 | ESCAPE-SEQ is an escape sequence parsed by `ansi-color-parse-sequence'. |
| 524 | (push face modified-faces)) | 536 | |
| 525 | modified-faces))))) | 537 | If the new codes resulting from ESCAPE-SEQ start with 0, then the |
| 538 | old codes are discarded and the remaining new codes are | ||
| 539 | processed. Otherwise, for each new code: if it is 21-25 or 27-29 | ||
| 540 | delete appropriate parameters from the list of codes; any other | ||
| 541 | code that makes sense is added to the list of codes. Finally, | ||
| 542 | the 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 | |||
| 594 | Should any of the parameters result in the default face (usually this is | ||
| 595 | the parameter 0), then the effect of all previous parameters is canceled. | ||
| 596 | |||
| 597 | ESCAPE-SEQ is a SGR control sequences such as \\033[34m. The parameter | ||
| 598 | 34 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 |