4

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?

  • endlessparentheses.com/… ? – phils Jul 20 '16 at 3:02
  • 1
    Ah, maybe the ansi codes aren't colours – Mark Aufflick Jul 20 '16 at 3: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 3: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 Aufflick Jul 20 '16 at 3:42
  • 1
    Great. It's always a nice feeling when past-you solves present-you's problems :) – phils Jul 20 '16 at 4:22
4

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.

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.