Home GitHub Patreon
RSS Twitter

Turn on context-aware modes on shell command output

These days I work with PHP quite a lot; and especially with PHP calling REST APIs. I have written a little snippet to execute current buffer using async-shell-command and usually run small helper functions or little prototype scripts directly from Emacs as I develop them.

The return values (really the process output) is JSON most of the time. So why not have the output buffer in json-mode when appropriate?

First, let's write a little helper function to guess the appropriate major mode for a buffer. I only have the JSON logic1 there but you can of course add more "handlers" to check for XML or TAP output and so on. Let me know if you create something awesome!

(defun my-shell-guess-major-mode (buffer)
  "Guess major mode for the content of BUFFER."
  (with-current-buffer buffer
    (when (save-excursion
            (goto-char (point-min))
            (ignore-errors (json-read)))
      (json-mode)
      (json-mode-beautify))))

The little difficulty here is that the process runs asynchronously so I have to hook into the sentinel to know when things are finished. I do that with the next advice. If the process finished, try to parse the buffer and if it contains valid JSON turn on json-mode and beautify the content for easier reading.

(defadvice shell-command-sentinel (after enable-better-mode activate)
  (when (memq (process-status (ad-get-arg 0)) '(exit signal))
    (my-shell-guess-major-mode (process-buffer (ad-get-arg 0)))))

Similarly, we advice the synchronous version shell-command. This function returns the exit status if it runs synchronous command or comint-output-filter if it was used to start an asynchronous command. We can decide on that and either run the guess routine or leave it to the sentinel above.

(defadvice shell-command (after enable-better-mode activate)
  (unless (eq ad-return-value 'comint-output-filter)
    (-when-let (buffer (get-buffer "*Shell Command Output*"))
      (my-shell-guess-major-mode buffer))))

Footnotes:

1
This code depends on json (built-in) and json-mode (get from MELPA).

Last updated at: 2017-06-04 15:39
Found a typo? Edit on GitHub!