atomontage/xterm-color

ANSI & xterm-256 color text property translator for Emacs

Emacs Lisp

238

57 commits

updated Jun 21, 2026

See the code

README

[[https://opensource.org/licenses/BSD-2-Clause][https://img.shields.io/badge/license-BSD-blue.svg]]
[[https://melpa.org/#/xterm-color][https://melpa.org/packages/xterm-color-badge.svg]]
[[http://stable.melpa.org/#/xterm-color][https://stable.melpa.org/packages/xterm-color-badge.svg]]

xterm-color.el is an ANSI control sequence to text-property translator.

Translation takes place through state machine emulation which provides
a far more accurate, comprehensive result than ansi-color.el that is
built-into Emacs, without compromising on performance.

* Features
+ Regular ANSI color support
+ XTERM 256 color support
+ Truecolor (24-bit) support
+ AIXTERM bright foreground color
+ AIXTERM bright background color (since 1.8)
+ Use bold instead of bright (since 1.8)
+ When colorizing entire buffers, optional overlay support (since 2.0)
+ Optionally, filter out ANSI control sequences (since 2.0)
+ Works with compilation-mode
+ Works with eshell

* Install
Simple and most flexible method:

#+BEGIN_SRC emacs-lisp
;; Clone this repository, ensure directory of xterm-color.el is in your
;; load-path and add the following to your init file:
(require 'xterm-color)

;; You should byte-compile xterm-color.el for maximum performance.
#+END_SRC

Alternatively, you can install through [[https://melpa.org/][MELPA]] or some other Emacs package
manager.

* Usage
Interactively or from Emacs Lisp, call ~xterm-color-colorize-buffer~ to
colorize an entire buffer.

You may customize ~xterm-color-debug~ (default nil, if non-nil you will get warnings
in *Messages* when unsupported escape sequences are encountered),
~xterm-color-use-bold-for-bright~ (default nil), ~xterm-color-names~,
~xterm-color-names-bright~. Additionally, you may set ~xterm-color-preserve-properties~
to t (default nil, should be set to t only if using xterm-color with eshell, see below).

If you want to filter out all ANSI control sequences, without applying them to the text,
set ~xterm-color-render~ to nil.

A buffer-local face attribute cache is used since 1.8 to improve performance.
This means that if changes are made to ~xterm-color-names~ or ~xterm-color-names-bright~ or
~xterm-color-use-bold-for-bright~ at runtime, ~xterm-color-clear-cache~ should be called
in a buffer with activated xterm-color for changes to take effect in that buffer.

** Comint
You can replace ansi-color.el with xterm-color for all comint buffers,
but you should take into account modes that propertize strings and pass them
through ~comint-preoutput-filter-functions~ since ~xterm-color-filter~ will
strip all text properties (this is done to get maximum performance). The
recommended configuration that avoids issues is to remove ~ansi-color-process-output~
from the buffer-local ~comint-output-filter-functions~ and add ~xterm-color-filter~ as
the *first* hook in the buffer-local ~comint-preoutput-filter-functions~ for any
comint-based mode that you would like it to affect (e.g. shell-mode).

Additionally, it is recommended to disable font-locking for shell-mode buffers
[[https://github.com/atomontage/xterm-color/issues/28][since it interacts badly with comint and drastically affects performance]].
Font locking in shell-mode buffers is superfluous since xterm-color.el will
handle faces fine by itself.

Example configuration for shell-mode (M-x shell):

#+BEGIN_SRC emacs-lisp
(add-hook 'shell-mode-hook
          (lambda ()
            ;; Disable font-locking in this buffer to improve performance
            (font-lock-mode -1)
            ;; Prevent font-locking from being re-enabled in this buffer
            (make-local-variable 'font-lock-function)
            (setq font-lock-function (lambda (_) nil))
            ;; Replace ansi-color-process-output with xterm-color-filter
            (make-local-variable 'comint-output-filter-functions)
            (remove-hook 'comint-output-filter-functions 'ansi-color-process-output t)
            (add-hook 'comint-preoutput-filter-functions 'xterm-color-filter nil t)))

;; Also set TERM accordingly (xterm-256color) in the shell itself.
#+END_SRC

Using ~compilation-shell-minor-mode~ (NOTE: not the same as compilation-mode)
with shell-mode buffers that have xterm-color enabled is NOT recommended,
as ~compilation-shell-minor-mode~ depends on font-locking and causes severe
performance degradation. Omit the statements that disable font-locking in the
shell-mode example configuration if you need it.

** Eshell
An example configuration for eshell:

#+BEGIN_SRC emacs-lisp
(require 'eshell) ; or use with-eval-after-load

(add-hook 'eshell-before-prompt-hook
          (lambda ()
            (setq xterm-color-preserve-properties t)))

(add-to-list 'eshell-preoutput-filter-functions 'xterm-color-filter)
(setq eshell-output-filter-functions (remove 'eshell-handle-ansi-color eshell-output-filter-functions))
(setenv "TERM" "xterm-256color")
#+END_SRC

** Compilation buffers
For standalone compilation buffers use the following configuration:

#+BEGIN_SRC emacs-lisp
(setq compilation-environment '("TERM=xterm-256color"))

(define-advice compilation-filter (:around (f proc string) xterm-color)
  (funcall f proc (xterm-color-filter string)))
#+END_SRC

*NOTE*: This compilation-mode configuration will break
[[https://github.com/Wilfred/ag.el][ag.el]] and [[https://github.com/dajva/rg.el][rg.el]],
since these packages expect ANSI control sequences to be part of
compilation output so that they can be used for matching. Suggestions
for these packages to improve the way they perform matching
[[https://github.com/dajva/rg.el/issues/65][already]] [[https://github.com/atomontage/xterm-color/issues/37][exist]].

** Other
For programmatic usage from Emacs Lisp, call ~xterm-color-filter~ to propertize
strings that you can then insert into a buffer. All state is kept in
buffer-local variables which means that control sequences can span
~xterm-color-filter~ calls (state machine processing).

Example:

#+BEGIN_SRC emacs-lisp
(let ((buffer (generate-new-buffer "*xterm-color-test*")))
  (with-current-buffer buffer
    (insert (xterm-color-filter "\x1b[0;1;3;4"))
    (insert (xterm-color-filter ";35"))
    (insert (xterm-color-filter ";51mThis is only a test"))
    (insert (xterm-color-filter "\x1b[0m")))
  (switch-to-buffer buffer))
#+END_SRC

* Test
#+BEGIN_SRC emacs-lisp
M-x xterm-color-test

For comint or eshell:

M-x shell || M-x eshell

perl tests/xterm-colortest && perl tests/256colors2.pl

printf "\x1b[0;1;3;4;35;51mThis is only a test\x1b[0m\n"

Comparison with ansi-color.el:

M-x xterm-color-test-raw then M-x xterm-color-colorize-buffer

and contrast with

M-x xterm-color-test-raw then M-: (ansi-color-apply-on-region (point-min) (point-max))

#+END_SRC

[[file:img/xterm-color.png][file:img/xterm-color-thumb.png]]  [[file:img/godwars2.png][file:img/godwars2-thumb.png]]  [[file:img/wttr.png][file:img/wttr-thumb.png]]

* License
The code comes with a BSD-style license so you can basically do with it
whatever you want.

* Author
xristos (AT) sdf (DOT) org

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

atomontage

45 commits

kira-bruneau

2 commits

dannywillems

2 commits

dandavison

1 commits

atomontage/xterm-color

ANSI & xterm-256 color text property translator for Emacs

Emacs Lisp

238

57 commits

updated Jun 21, 2026

See the code

README

[[https://opensource.org/licenses/BSD-2-Clause][https://img.shields.io/badge/license-BSD-blue.svg]]
[[https://melpa.org/#/xterm-color][https://melpa.org/packages/xterm-color-badge.svg]]
[[http://stable.melpa.org/#/xterm-color][https://stable.melpa.org/packages/xterm-color-badge.svg]]

xterm-color.el is an ANSI control sequence to text-property translator.

Translation takes place through state machine emulation which provides
a far more accurate, comprehensive result than ansi-color.el that is
built-into Emacs, without compromising on performance.

* Features
+ Regular ANSI color support
+ XTERM 256 color support
+ Truecolor (24-bit) support
+ AIXTERM bright foreground color
+ AIXTERM bright background color (since 1.8)
+ Use bold instead of bright (since 1.8)
+ When colorizing entire buffers, optional overlay support (since 2.0)
+ Optionally, filter out ANSI control sequences (since 2.0)
+ Works with compilation-mode
+ Works with eshell

* Install
Simple and most flexible method:

#+BEGIN_SRC emacs-lisp
;; Clone this repository, ensure directory of xterm-color.el is in your
;; load-path and add the following to your init file:
(require 'xterm-color)

;; You should byte-compile xterm-color.el for maximum performance.
#+END_SRC

Alternatively, you can install through [[https://melpa.org/][MELPA]] or some other Emacs package
manager.

* Usage
Interactively or from Emacs Lisp, call ~xterm-color-colorize-buffer~ to
colorize an entire buffer.

You may customize ~xterm-color-debug~ (default nil, if non-nil you will get warnings
in *Messages* when unsupported escape sequences are encountered),
~xterm-color-use-bold-for-bright~ (default nil), ~xterm-color-names~,
~xterm-color-names-bright~. Additionally, you may set ~xterm-color-preserve-properties~
to t (default nil, should be set to t only if using xterm-color with eshell, see below).

If you want to filter out all ANSI control sequences, without applying them to the text,
set ~xterm-color-render~ to nil.

A buffer-local face attribute cache is used since 1.8 to improve performance.
This means that if changes are made to ~xterm-color-names~ or ~xterm-color-names-bright~ or
~xterm-color-use-bold-for-bright~ at runtime, ~xterm-color-clear-cache~ should be called
in a buffer with activated xterm-color for changes to take effect in that buffer.

** Comint
You can replace ansi-color.el with xterm-color for all comint buffers,
but you should take into account modes that propertize strings and pass them
through ~comint-preoutput-filter-functions~ since ~xterm-color-filter~ will
strip all text properties (this is done to get maximum performance). The
recommended configuration that avoids issues is to remove ~ansi-color-process-output~
from the buffer-local ~comint-output-filter-functions~ and add ~xterm-color-filter~ as
the *first* hook in the buffer-local ~comint-preoutput-filter-functions~ for any
comint-based mode that you would like it to affect (e.g. shell-mode).

Additionally, it is recommended to disable font-locking for shell-mode buffers
[[https://github.com/atomontage/xterm-color/issues/28][since it interacts badly with comint and drastically affects performance]].
Font locking in shell-mode buffers is superfluous since xterm-color.el will
handle faces fine by itself.

Example configuration for shell-mode (M-x shell):

#+BEGIN_SRC emacs-lisp
(add-hook 'shell-mode-hook
          (lambda ()
            ;; Disable font-locking in this buffer to improve performance
            (font-lock-mode -1)
            ;; Prevent font-locking from being re-enabled in this buffer
            (make-local-variable 'font-lock-function)
            (setq font-lock-function (lambda (_) nil))
            ;; Replace ansi-color-process-output with xterm-color-filter
            (make-local-variable 'comint-output-filter-functions)
            (remove-hook 'comint-output-filter-functions 'ansi-color-process-output t)
            (add-hook 'comint-preoutput-filter-functions 'xterm-color-filter nil t)))

;; Also set TERM accordingly (xterm-256color) in the shell itself.
#+END_SRC

Using ~compilation-shell-minor-mode~ (NOTE: not the same as compilation-mode)
with shell-mode buffers that have xterm-color enabled is NOT recommended,
as ~compilation-shell-minor-mode~ depends on font-locking and causes severe
performance degradation. Omit the statements that disable font-locking in the
shell-mode example configuration if you need it.

** Eshell
An example configuration for eshell:

#+BEGIN_SRC emacs-lisp
(require 'eshell) ; or use with-eval-after-load

(add-hook 'eshell-before-prompt-hook
          (lambda ()
            (setq xterm-color-preserve-properties t)))

(add-to-list 'eshell-preoutput-filter-functions 'xterm-color-filter)
(setq eshell-output-filter-functions (remove 'eshell-handle-ansi-color eshell-output-filter-functions))
(setenv "TERM" "xterm-256color")
#+END_SRC

** Compilation buffers
For standalone compilation buffers use the following configuration:

#+BEGIN_SRC emacs-lisp
(setq compilation-environment '("TERM=xterm-256color"))

(define-advice compilation-filter (:around (f proc string) xterm-color)
  (funcall f proc (xterm-color-filter string)))
#+END_SRC

*NOTE*: This compilation-mode configuration will break
[[https://github.com/Wilfred/ag.el][ag.el]] and [[https://github.com/dajva/rg.el][rg.el]],
since these packages expect ANSI control sequences to be part of
compilation output so that they can be used for matching. Suggestions
for these packages to improve the way they perform matching
[[https://github.com/dajva/rg.el/issues/65][already]] [[https://github.com/atomontage/xterm-color/issues/37][exist]].

** Other
For programmatic usage from Emacs Lisp, call ~xterm-color-filter~ to propertize
strings that you can then insert into a buffer. All state is kept in
buffer-local variables which means that control sequences can span
~xterm-color-filter~ calls (state machine processing).

Example:

#+BEGIN_SRC emacs-lisp
(let ((buffer (generate-new-buffer "*xterm-color-test*")))
  (with-current-buffer buffer
    (insert (xterm-color-filter "\x1b[0;1;3;4"))
    (insert (xterm-color-filter ";35"))
    (insert (xterm-color-filter ";51mThis is only a test"))
    (insert (xterm-color-filter "\x1b[0m")))
  (switch-to-buffer buffer))
#+END_SRC

* Test
#+BEGIN_SRC emacs-lisp
M-x xterm-color-test

For comint or eshell:

M-x shell || M-x eshell

perl tests/xterm-colortest && perl tests/256colors2.pl

printf "\x1b[0;1;3;4;35;51mThis is only a test\x1b[0m\n"

Comparison with ansi-color.el:

M-x xterm-color-test-raw then M-x xterm-color-colorize-buffer

and contrast with

M-x xterm-color-test-raw then M-: (ansi-color-apply-on-region (point-min) (point-max))

#+END_SRC

[[file:img/xterm-color.png][file:img/xterm-color-thumb.png]]  [[file:img/godwars2.png][file:img/godwars2-thumb.png]]  [[file:img/wttr.png][file:img/wttr-thumb.png]]

* License
The code comes with a BSD-style license so you can basically do with it
whatever you want.

* Author
xristos (AT) sdf (DOT) org

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

atomontage

45 commits

kira-bruneau

2 commits

dannywillems

2 commits

dandavison

1 commits

Languages

Emacs Lisp

64.3%

Perl

35.7%