6

I have a buffer with terminal escape codes in it and I want them to be colored:

screenshot of buffer with raw escape codes

I was able to do this with (ansi-color-apply-on-region (point-min) (point-max)):

screenshot of buffer with colored escape codes

However, my file is really big and that command is really slow. Is there any mode that colors terminal escape codes with font lock? I imagine that would be much faster.

NOTE: What I am looking at is a log file, not a shell buffer, so solutions involving M-x shell output are irrelevant to me.

Jackson
  • 1,218
  • 9
  • 19
  • 1
    "Is there any mode that colors terminal escape codes with font lock? I imagine that would be much faster." Converting ansi color escape codes to `font-lock-face` properties so that font-lock will show the colours is precisely what `ansi-color-apply` is doing. – phils Jun 26 '18 at 21:23
  • 1
    @phils, is there a minor or major mode based on `ansi-color-apply`? In the question it looks like the entire buffer is converted at once, which is time consuming for large buffers. Normally, font-lock rules are only applied to the visible part of a buffer which makes them much faster. – Lindydancer Jun 27 '18 at 04:37
  • @Lindydancer, I don't know, but is the font-lock system capable of editing/filtering buffer text beyond the text properties / faces? `ansi-color-apply` deletes the ansi escape code characters from the text and adds text properties in their place. I didn't think that was a task for which font-lock was suitable, but you'll know much better than I do. – phils Jun 27 '18 at 05:16
  • @phils, to make a font-lock package out of this, it would be much better to hide the ANSI sequences using the `invisible` text property without altering the actual text. In addition, ANSI sequences stretching over multiple lines may be problematic, since font-lock only highlights one or a few lines at a time. – Lindydancer Jun 27 '18 at 15:33
  • n.b. npostavs' answer has made me realise that `ansi-color-apply` and `ansi-color-apply-on-region` do things very differently! The former puts `font-lock-face` text properties on the string, while the latter creates overlays and places `face` properties on those. – phils Jun 27 '18 at 20:58

2 Answers2

1

If you set up your shell buffer to convert the escape codes to color automatically, it works much faster. I have the following config:

(add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
(ansi-color-for-comint-mode-on)

With this set, when I run a shell in emacs (i.e., M-x shell), the output is colored automatically each time it is produced. That means you only wait an undetectable short period for emacs to color each line of output, rather than waiting for the entire buffer to be colored like you're experiencing.

Tyler
  • 21,719
  • 1
  • 52
  • 92
  • This isn’t a shell buffer, it’s a log file. – Jackson Jun 26 '18 at 20:18
  • I'd be tempted to try M-x shell, then call cat to see if that's faster than what you've done – Tyler Jun 26 '18 at 22:44
  • 1
    That would probably work as a workaround for my problem, but for the sake of providing the “best” answer for this question, I’d like to be able to open the file using `find-file` and it “just works.” – Jackson Jul 18 '18 at 19:23
  • Sure, no problem. Did you try it? If it does work, that would give us a clue for coming up with a solution that 'just works' with find-file. – Tyler Jul 18 '18 at 19:37
  • `cat` in `M-x shell` provides color output even without your ELisp snippet. However, I think that trick is irrelevant to solving this question the “right” way, because it runs into the same problem I posed in my question, i.e. `cat` results in coloring an entire enormous file. I also don’t want to view the file in shell-mode, I want to view it in fundamental-mode. – Jackson Jul 18 '18 at 19:56
  • cat coloring an enormous file is only a problem if it's slow - was it? If it's faster than `ansi-color-apply-on-region`, then we can dig into the comint-mode code to find out how they color the text. If it's just as slow as what you're already doing then it's not worth any further effort – Tyler Jul 18 '18 at 20:08
  • Since comint.el uses `ansi-color-apply-on-region` (`comint-output-filter-functions` -> `ansi-color-process-output` -> `ansi-color-apply-on-region`), I guess it can't be any faster. Well, `ansi-color-process-ouput` can optionally use `ansi-color-filter-region` instead which could be faster since it just drops the escapes instead of interpreting them. – npostavs Jul 19 '18 at 00:07
  • @Tyler, yes, it’s slow. With respect to your statement “it’s not worth any further effort,” that is why I asked if there was font-lock support for this, i.e. “streaming” support for coloring ANSI. – Jackson Aug 05 '18 at 15:57
1

I was able to do this with (ansi-color-apply-on-region (point-min) (point-max)):

However, my file is really big and that command is really slow.

That function uses overlays by default, which will quickly become very slow when you have many of them. Try

(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)))

See also

npostavs
  • 9,033
  • 1
  • 21
  • 53
  • 1
    I tested this on a 70 MB log file and it took about 30 seconds, freezing Emacs in the process. Better, but still less-than-ideal. – Jackson Jul 18 '18 at 20:01