How do I run (async-)shell-command
(but no other shell-mode
buffers) in view-mode
?
-
I looked for (async-)shell-command-hook but didn't find anything. – Mattias Bengtsson Feb 16 '15 at 9:24
Do can define your another wrapper function that calls async-shell-command
which enables the view-mode
after calling that function.
(defun my/async-shell-command (command &optional output-buffer error-buffer)
(interactive
(list
(read-shell-command "Async shell command: " nil nil
(let ((filename
(cond
(buffer-file-name)
((eq major-mode 'dired-mode)
(dired-get-filename nil t)))))
(and filename (file-relative-name filename))))
current-prefix-arg
shell-command-default-error-buffer))
;; call the original function
(async-shell-command command output-buffer error-buffer)
;; switch to the shell command output buffer
(switch-to-buffer "*Async Shell Command*")
;; enable `view-mode'
(view-mode))
and bind that to the default async-shell-command
binding M-&
.
Note that the (interactive (list ..))
portion of the wrapper funciton is copied directly from the source code for async-shell-command
. It's useful to retain the input arguments of the original function when writing a wrapper around it. The same applies if you were to use advice-add
(emacs 24.4+) instead.
Similar thing can be done for shell-command
too.
-
Awesome! I'll try this out tomorrow. From reading your code though I'm guessing that the last three lines would be enough, right? I don't quite understand the interactive part. – Mattias Bengtsson Feb 16 '15 at 23:31
-
@MattiasBengtsson Nope, you will need the interactive part too. I have simply copied that portion from the source code. That portion is important as it creates the mechanism for entering the shell command in the minibuffer. – Kaushal Modi Feb 16 '15 at 23:40
-
Did you test it? I'm wondering whether you won't get errors for trying to print on a readonly buffer. – Malabarba Feb 17 '15 at 0:21
-
@Malabarba I tested this to work as the OP wants. Calling this function, executes the async command as entered in the minibuffer and then makes the async buffer read-only (
view-mode
). So when you try to type in that buffer, you won't be able to. Re-executing the same command, discards the old async buffer and shows the new content, again making that read-only. – Kaushal Modi Feb 17 '15 at 0:40 -
Hm it doesn't quite work. The original async-shell-command will popup and then the switch-to-buffer call will add a new view of the buffer. The old view won't be in view-mode. See here: youtube.com/watch?v=DI4I-KOGA90 Hm, I wonder if it would be possible to construct an advice around (async-)shell-command? I'll try that out later this week. – Mattias Bengtsson Feb 17 '15 at 18:04
Seeing as there are no shell command hooks, an alternative is to use advice. All asynchronous processes need to specify a sentinel function, which in the case of shell commands is shell-command-sentinel
:
(define-advice shell-command-sentinel (:after (process _msg) my-view-output)
"Enable `view-mode' in `*Async Shell Command*' buffers."
(let ((buffer (process-buffer process)))
(and (memq (process-status process) '(exit signal))
(buffer-live-p buffer)
(with-current-buffer buffer
(view-mode)))))
Synchronous shell commands, on the other hand, all go through the command shell-command-on-region
:
(define-advice shell-command-on-region (:after (&rest _) my-view-output)
"Enable `view-mode' in `*Shell Command Output*' buffer."
(let ((buffer (get-buffer "*Shell Command Output*")))
(when (buffer-live-p buffer)
(with-current-buffer buffer
(view-mode)))))
The latter advice is actually a bit more brittle than the former because it hard-codes the buffer name, but it should work for most cases. You should tweak it to suit your needs and use of shell commands.