1 ;;; ansi-color.el --- translate ANSI into text-properties
3 ;; Copyright (C) 1999, 2000 Free Software Foundation, Inc.
5 ;; Author: Alex Schroeder <alex@gnu.org>
6 ;; Maintainer: Alex Schroeder <alex@gnu.org>
8 ;; Keywords: comm processes
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software; you can redistribute it and/or modify it
13 ;; under the terms of the GNU General Public License as published by the
14 ;; Free Software Foundation; either version 2, or (at your option) any
17 ;; GNU Emacs is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ;; General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
29 ;; This file provides a function that takes a string containing Select
30 ;; Graphic Rendition (SGR) control sequences (formerly known as ANSI
31 ;; escape sequences) and tries to replace these with text-properties.
33 ;; This allows you to run ls --color=yes in shell-mode: If
34 ;; `ansi-color-for-shell-mode' is non-nil, the SGR control sequences are
35 ;; translated into text-properties, colorizing the ls output. If
36 ;; `ansi-color-for-shell-mode' is nil, the SGR control sequences are
37 ;; stripped, making the ls output legible.
39 ;; SGR control sequences are defined in section 3.8.117 of the ECMA-48
40 ;; standard (identical to ISO/IEC 6429), which is freely available as a
41 ;; PDF file <URL:http://www.ecma.ch/ecma1/STAND/ECMA-048.HTM>. The
42 ;; "Graphic Rendition Combination Mode (GRCM)" implemented is
43 ;; "cumulative mode" as defined in section 7.2.8. Cumulative mode means
44 ;; that whenever possible, SGR control sequences are combined (ie. blue
47 ;; The basic functions are:
49 ;; `ansi-color-apply' to colorize a string containing SGR control
52 ;; `ansi-color-filter-apply' to filter SGR control sequences from a
55 ;; `ansi-color-apply-on-region' to colorize a region containing SGR
58 ;; `ansi-color-filter-region' to filter SGR control sequences from a
61 ;; Instead of defining lots of new faces, this package uses
62 ;; text-properties as described in the elisp manual
63 ;; *Note (elisp)Special Properties::.
67 ;; Georges Brun-Cottan <gbruncot@emc.com> for improving ansi-color.el
68 ;; substantially by adding the code needed to cope with arbitrary chunks
69 ;; of output and the filter functions.
71 ;; Markus Kuhn <Markus.Kuhn@cl.cam.ac.uk> for pointing me to ECMA-48.
79 (defgroup ansi-colors nil
80 "Translating SGR control sequences to text-properties.
81 This translation effectively colorizes strings and regions based upon
82 SGR control sequences embedded in the text. SGR (Select Graphic
83 Rendition) control sequences are defined in section 3.8.117 of the
84 ECMA-48 standard \(identical to ISO/IEC 6429), which is freely available
85 as a PDF file <URL:http://www.ecma.ch/ecma1/STAND/ECMA-048.HTM>."
89 (defcustom ansi-color-faces-vector
90 [default bold default italic underline bold bold-italic modeline
]
91 "Faces used for SGR control sequences determining a face.
92 This vector holds the faces used for SGR control sequence parameters 0
95 Parameter Description Face used by default
100 4 underlined underline
101 5 slowly blinking bold
102 6 rapidly blinking bold-italic
103 7 negative image modeline
105 This vector is used by `ansi-color-make-color-map' to create a color
106 map. This color map is stored in the variable `ansi-color-map'."
107 :type
'(vector face face face face face face face face
)
108 :set
'ansi-color-map-update
109 :initialize
'custom-initialize-default
112 (defcustom ansi-color-names-vector
113 ["black" "red" "green" "yellow" "blue" "magenta" "cyan" "white"]
114 "Colors used for SGR control sequences determining a color.
115 This vector holds the colors used for SGR control sequences parameters
116 30 to 37 \(foreground colors) and 40 to 47 (background colors).
128 This vector is used by `ansi-color-make-color-map' to create a color
129 map. This color map is stored in the variable `ansi-color-map'."
130 :type
'(vector string string string string string string string string
)
131 :set
'ansi-color-map-update
132 :initialize
'custom-initialize-default
135 (defcustom ansi-color-for-shell-mode nil
136 "Determine wether font-lock or ansi-color get to fontify shell buffers.
138 If non-nil and `global-font-lock-mode' is non-nil, ansi-color will be
139 used. This adds `ansi-color-apply' to
140 `comint-preoutput-filter-functions' and removes
141 `ansi-color-filter-apply' for all shell-mode buffers.
143 If non-nil and global-font-lock-mode is nil, both `ansi-color-apply' and
144 `ansi-color-filter-apply' will be removed from
145 `comint-preoutput-filter-functions' for all shell-mode buffers.
147 If nil, font-lock will be used (if it is enabled). This adds
148 `ansi-color-filter-apply' to `comint-preoutput-filter-functions' and
149 removes `ansi-color-apply' for all shell-mode buffers."
152 :set
(function (lambda (symbol value
)
153 (set-default symbol value
)
155 (let ((buffers (buffer-list))
158 (setq buffer
(car buffers
)
159 buffers
(cdr buffers
))
161 (when (eq major-mode
'shell-mode
)
163 (if global-font-lock-mode
166 (remove-hook 'comint-preoutput-filter-functions
167 'ansi-color-filter-apply
)
168 (add-hook 'comint-preoutput-filter-functions
170 (remove-hook 'comint-preoutput-filter-functions
171 'ansi-color-filter-apply
)
172 (remove-hook 'comint-preoutput-filter-functions
174 (if global-font-lock-mode
176 (remove-hook 'comint-preoutput-filter-functions
178 (add-hook 'comint-preoutput-filter-functions
179 'ansi-color-filter-apply
))))))))
180 :initialize
'custom-initialize-reset
183 (defconst ansi-color-regexp
"\033\\[\\([0-9;]*\\)m"
184 "Regexp that matches SGR control sequences.")
186 (defconst ansi-color-parameter-regexp
"\\([0-9]*\\)[m;]"
187 "Regexp that matches SGR control sequence parameters.")
193 (defun ansi-color-filter-apply (s)
194 "Filter out all SGR control sequences from S.
196 This function can be added to `comint-preoutput-filter-functions'."
197 (while (string-match ansi-color-regexp s
)
198 (setq s
(replace-match "" t t s
)))
202 (defun ansi-color-filter-region (begin end
)
203 "Filter out all SGR control sequences from region START END.
205 Returns the first point it is safe to start with. Used to speedup
208 Design to cope with arbitrary chunk of output such as the ones get by
209 comint-output-filter-functions, e.g.:
211 \(defvar last-context nil)
212 \(make-variable-buffer-local 'last-context)
214 \(defun filter-out-color-in-buffer (s)
216 \(ansi-color-filter-region
219 \(if (marker-position comint-last-output-start)
220 \(marker-position comint-last-output-start)
222 \(marker-position (process-mark (get-buffer-process (current-buffer)))) ))
225 \(add-hook 'comint-output-filter-functions 'filter-out-color-in-buffer)
227 (let ((endm (copy-marker end
)))
230 (while (re-search-forward ansi-color-regexp endm t
)
232 (if (re-search-forward "\033" endm t
)
234 (marker-position endm
)))))
237 (defun ansi-color-apply (string)
238 "Translates SGR control sequences into text-properties.
240 Applies SGR control sequences setting foreground and background colors
241 to STRING and returns the result. The colors used are given in
242 `ansi-color-faces-vector' and `ansi-color-names-vector'.
244 This function can be added to `comint-preoutput-filter-functions'."
245 (let (face (start 0) end escape-sequence null-sequence result
)
246 ;; find the next escape sequence
247 (while (setq end
(string-match ansi-color-regexp string start
))
248 ;; store escape sequence
249 (setq escape-sequence
(match-string 1 string
)
250 null-sequence
(string-equal escape-sequence
""))
251 ;; colorize the old block from start to end using old face
253 (put-text-property start end
'face face string
))
254 (setq result
(concat result
(substring string start end
))
256 ;; create new face by applying all the parameters in the escape sequence
259 (setq face
(ansi-color-get-face escape-sequence
))))
260 (concat result
(substring string start
))))
263 (defun ansi-color-apply-on-region (begin end
&optional context
)
264 "Translates SGR control sequences into text-properties.
266 Applies SGR control sequences setting foreground and background colors
267 to text in region. The colors used are given in
268 `ansi-color-faces-vector' and `ansi-color-names-vector'.
269 Returns a context than can be used to speedup further processing.
270 Context is a (begin (start . face)) list.
272 Design to cope with arbitrary chunk of output such as the ones get by
273 comint-output-filter-functions, e.g.:
275 \(defvar last-context nil)
276 \(make-variable-buffer-local 'last-context)
278 \(defun ansi-output-filter (s)
280 \(ansi-color-apply-on-region
283 \(if (marker-position comint-last-output-start)
284 \(marker-position comint-last-output-start)
286 \(process-mark (get-buffer-process (current-buffer)))
290 \(add-hook 'comint-output-filter-functions 'ansi-output-filter)
292 (let ((endm (copy-marker end
))
293 (face (if (and context
(cdr context
))
294 (cdr (cdr context
))))
295 (face-start (if (and context
(cdr context
))
296 (car (cdr context
))))
297 (next-safe-start begin
)
303 ;; find the next escape sequence
304 (while (setq stop
(re-search-forward ansi-color-regexp endm t
))
305 ;; store escape sequence
306 (setq escape-sequence
(match-string 1))
307 (setq null-sequence
(string-equal (match-string 1) ""))
308 (setq next-safe-start
(match-beginning 0))
310 (put-text-property face-start next-safe-start
'face face
)) ; colorize
311 (replace-match "") ; delete the ANSI sequence
314 (setq face-start next-safe-start
)
315 (setq face
(ansi-color-get-face escape-sequence
))))
316 (setq next-safe-start
317 (if (re-search-forward "\033" endm t
)
319 (marker-position endm
))))
320 (cons next-safe-start
322 (cons face-start face
))) ))
326 (defun ansi-color-make-color-map ()
327 "Creates a vector of face definitions and returns it.
329 The index into the vector is an ANSI code. See the documentation of
330 `ansi-color-map' for an example.
332 The face definitions are based upon the variables
333 `ansi-color-faces-vector' and `ansi-color-names-vector'."
334 (let ((ansi-color-map (make-vector 50 nil
))
336 ;; miscellaneous attributes
338 (function (lambda (e)
339 (aset ansi-color-map index e
)
340 (setq index
(1+ index
)) ))
341 ansi-color-faces-vector
)
343 ;; foreground attributes
346 (function (lambda (e)
347 (aset ansi-color-map index
348 (cons 'foreground-color e
))
349 (setq index
(1+ index
)) ))
350 ansi-color-names-vector
)
352 ;; background attributes
355 (function (lambda (e)
356 (aset ansi-color-map index
357 (cons 'background-color e
))
358 (setq index
(1+ index
)) ))
359 ansi-color-names-vector
)
362 (defvar ansi-color-map
(ansi-color-make-color-map)
363 "A brand new color map suitable for ansi-color-get-face.
365 The value of this variable is usually constructed by
366 `ansi-color-make-color-map'. The values in the array are such that the
367 numbers included in an SGR control sequences point to the correct
368 foreground or background colors.
370 Example: The sequence \033[34m specifies a blue foreground. Therefore:
371 (aref ansi-color-map 34)
372 => \(foreground-color . \"blue\")")
374 (defun ansi-color-map-update (symbol value
)
375 "Update `ansi-color-map'.
377 Whenever the vectors used to construct `ansi-color-map' are changed,
378 this function is called. Therefore this function is listed as the :set
379 property of `ansi-color-faces-vector' and `ansi-color-names-vector'."
380 (set-default symbol value
)
381 (setq ansi-color-map
(ansi-color-make-color-map)))
383 (defun ansi-color-get-face-1 (ansi-code)
384 "Get face definition from `ansi-color-map'.
385 ANSI-CODE is used as an index into the vector."
387 (aref ansi-color-map ansi-code
)
388 ('args-out-of-range nil
)))
390 (defun ansi-color-get-face (escape-seq)
391 "Create a new face by applying all the parameters in ESCAPE-SEQ.
393 ESCAPE-SEQ is a SGR control sequences such as \033[34m. The parameter
394 34 is used by `ansi-color-get-face-1' to return a face definition."
395 (let ((ansi-color-r "[0-9][0-9]?")
398 (while (string-match ansi-color-r escape-seq i
)
399 (setq i
(match-end 0))
401 (ansi-color-get-face-1
402 (string-to-int (match-string 0 escape-seq
) 10))))
405 (provide 'ansi-color
)
407 ;;; ansi-color.el ends here