Previously I posted this question to Stack Overflow regarding rendering ansi-colors. The tip worked but performance very bad; when I open a 15 MB log file, emacs just locks up.

I decided to try installing tty-format.el. This had the same result (i.e. poor performance).

Then I tried to install xterm-color.el but I couldn't figure out how to make it work; the lisp file loaded but it's not clear to me how to make ANSI codes colorize.

Is there a solution for poor performance in rendering ANSI colors? Thanks!

  • Can you provide a (sanitised) sample which demonstrates the issue? – phils Jul 6 '16 at 5:29
  • n.b. Obviously not 15 MB of sample, but something representative of the issue when repeated enough times. e.g. seq 10000 | xargs -I{} cat sample.log >>verybig.log – phils Jul 6 '16 at 6:11
  • re: xterm-color.el, looks like it works on strings only. You could try applying it to the result of (buffer-string), but it would likely be even slower (the fact that it works on strings implies the author was only considering colorizing short texts, not whole files). – npostavs Jul 6 '16 at 11:11

Your problem was way more fun than expected to look into !

The function used in the solution described in your link,

(ansi-color-apply-on-region (point-min) (point-max))

is the one to go for in my opinion as it is the function used in comint-mode itself, so I guess pretty "low level" and well integrated in emacs.

One idea would be to apply it only on the visible region by replacing point-min and point-max by window-start and window-end and putting it into the window-scroll-functions:

(require 'ansi-color)

(defun my-colorize-buffer-window (win)
  (ansi-color-apply-on-region (window-start win) (window-end win t)))

(defun my-colorize-buffer (win _start)
    (mapc #'my-colorize-buffer-window (get-buffer-window-list (window-buffer win) nil 'visible)))

(add-hook 'window-scroll-functions 'my-colorize-buffer)

(I copied the way linum-mode updates line numbers)

Though using that on a 323Mo log file seems to have good performance (smooth and no freeze when it works), I am still facing some issues with that solution.

In particular:

  • infinite loops that go away when resizing frame
  • infinite loops that do not go away when resizing frame
  • some ansi sequences do not decode right away but only on the second scroll showing them
  • ansi-color-apply-on-region actually modifies the buffer's contents, whereas format-decode-buffer with tty-format doesnt. It might by worthy looking into that.

Have to go now, I might update later on !

| improve this answer | |
  • No idea about the loops, but the decoding glitches do not surprise me. After all, ANSI escape sequences consist of more than a byte, so you could end up cutting off the beginning or start of one which would lead to it not being recognized. The easiest way of fixing this is to start decoding from the beginning of the buffer, the very thing you wish to avoid... – wasamasa Jul 8 '16 at 17:16
  • As for format-decode-buffer, it reimplements with-silent-modifications for whatever reason. Yay, old code. – wasamasa Jul 8 '16 at 17:20
  • "(I copied the way linum-mode updates line numbers)" - maybe look at how nlinum-mode works? It's description says "This is like linum-mode, but uses jit-lock to be (hopefully) more efficient." – npostavs Jul 8 '16 at 17:45
  • @waxamasa: I know about this issue but it is not the case. It is a different one as the sequence itself is decoded only the second time I display this text (ansi not decoded -> scroll up -> scroll down -> ansi decoded) – deb0ch Jul 9 '16 at 11:32

Reportedly, using text properties instead of overlays improves performance noticeably:

(let ((ansi-color-apply-face-function
       (lambda (beg end face)
         (when face
           (put-text-property beg end 'face face)))))
  (ansi-color-apply-on-region (point-min) (point-max)))
| improve this answer | |

Use https://github.com/atomontage/xterm-color

It's noticeably faster than ansi-color.el and now supports interactive buffer colorization (via M-x xterm-color-colorize-buffer).

| improve this answer | |

Then I tried to install xterm-color.el but I couldn't figure out how to make it work; the lisp file loaded but it's not clear to me how to make ANSI codes colorize.

Here's the relevant part of my config, if it's helpful:

(use-package xterm-color
  :ensure t
  (setq comint-output-filter-functions
        (remove 'ansi-color-process-output comint-output-filter-functions))

  (add-hook 'shell-mode-hook
            (lambda ()
              (add-hook 'comint-preoutput-filter-functions
                        'xterm-color-filter nil t))))

This gives me ANSI colours in shell-mode, although some programs need to be "tricked" into emitting colours, e.g. by using TERM=xterm-256color, since shell-mode defaults to TERM=dumb. Pretending to be xterm can cause problems though, since applications might try to use fancy things like ncurses which shell-mode can't handle.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.