7

When you run various "modern" compilation or test tools you get ANSI codes in the output. I'd like to either render them correctly (ideally) or otherwise strip them. I've tried applying either term-mode-hook to the compilation-minor-mode as suggested in this superuser post, and also a similar approach using shell-mode as per this stackoverflow post, to no avail.

Does anyone have this working?

Mark
  • 1,409
  • 1
  • 15
  • 20
  • http://endlessparentheses.com/ansi-colors-in-the-compilation-buffer-output.html ? – phils Jul 20 '16 at 03:02
  • 1
    Ah, maybe the ansi codes aren't colours – Mark Jul 20 '16 at 03:13
  • Running the command from a shell in an `ansi-term` buffer, does it render correctly? (or in `shell-mode` for that matter, although the latter seems less likely). If so, show us your attempt to integrate the working one with `compilation-minor-mode` ? – phils Jul 20 '16 at 03:39
  • Yes, but it turns out that's because past-me wrote a comint-mode preoutput filter to strip non-colour ansi sequences :) I'm going to see how to add that to compilation-mode and report back. – Mark Jul 20 '16 at 03:42
  • 1
    Great. It's always a nice feeling when past-you solves present-you's problems :) – phils Jul 20 '16 at 04:22
  • I'm also very interested in the answer to this for sbt-mode https://github.com/ensime/emacs-sbt-mode/issues/100 – fommil Jul 06 '17 at 12:51
  • The proper solution is to teach your tool *not* to print such sequences when run in a dumb shell. – wasamasa Feb 01 '18 at 20:13

1 Answers1

10

There are two parts to this. First, process ANSI colors :

;; Stolen from (http://endlessparentheses.com/ansi-colors-in-the-compilation-buffer-output.html)
(require 'ansi-color)
(defun endless/colorize-compilation ()
  "Colorize from `compilation-filter-start' to `point'."
  (let ((inhibit-read-only t))
    (ansi-color-apply-on-region
     compilation-filter-start (point))))

(add-hook 'compilation-filter-hook
          #'endless/colorize-compilation)

Next, filter out unwanted ANSI escape sequences, like the ones intended for ttys but Emacs doesn't know/care about:

;; Stolen from (https://oleksandrmanzyuk.wordpress.com/2011/11/05/better-emacs-shell-part-i/)
(defun regexp-alternatives (regexps)
  "Return the alternation of a list of regexps."
  (mapconcat (lambda (regexp)
               (concat "\\(?:" regexp "\\)"))
             regexps "\\|"))

(defvar non-sgr-control-sequence-regexp nil
  "Regexp that matches non-SGR control sequences.")

(setq non-sgr-control-sequence-regexp
      (regexp-alternatives
       '(;; icon name escape sequences
         "\033\\][0-2];.*?\007"
         ;; non-SGR CSI escape sequences
         "\033\\[\\??[0-9;]*[^0-9;m]"
         ;; noop
         "\012\033\\[2K\033\\[1F"
         )))

(defun filter-non-sgr-control-sequences-in-region (begin end)
  (save-excursion
    (goto-char begin)
    (while (re-search-forward
            non-sgr-control-sequence-regexp end t)
      (replace-match ""))))

(defun filter-non-sgr-control-sequences-in-output (ignored)
  (let ((start-marker
         (or comint-last-output-start
             (point-min-marker)))
        (end-marker
         (process-mark
          (get-buffer-process (current-buffer)))))
    (filter-non-sgr-control-sequences-in-region
     start-marker
     end-marker)))

(add-hook 'comint-output-filter-functions
          'filter-non-sgr-control-sequences-in-output)

Note that the second part is actually getting applied to all comint derivatives, not just compilation-mode (It also fixes issues in, e.g. shell). If this is not what you want, then add the hook to compilation-filter-hook instead.

anarcat
  • 133
  • 6
mnewt
  • 258
  • 2
  • 9