Emacs rocks.
Recently, though, I’ve been trying to streamline my use of the shell in Emacs. Here’s my Halloween Emacs tips.
Added support for multiple shells
The first, glaring problem with shells in Emacs is that you can only have one.
I added a function that creates a shell based on the remote host of a buffer. It also names the shell after that host, so if I’m working on /example.com:/some/file
, I’ll get a shell called *shell:example.com*
(instead of just *shell*
). This allows you to have multiple shells open simultaneously, one per host. [Credit: EmacsWiki]
(interactive)
(let* ((tramp-path (when (tramp-tramp-file-p default-directory)
(tramp-dissect-file-name default-directory)))
(host (tramp-file-name-real-host tramp-path))
(user (if (tramp-file-name-user tramp-path)
(format "%s@" (tramp-file-name-user tramp-path)) ""))
(new-buffer-name (format "*shell:%s%s*" user host)))
(shell (if host new-buffer-name buffer))))
Added F9 shell toggle
Then I added the F9 global key binding to toggle between the current buffer and the related shell for the current buffer’s host, creating that shell as necessary.
(interactive)
(if (string-match "^\\*shell[\\*:]" (buffer-name) )
(previous-buffer)
(my/shell buffer)))
(global-set-key [f9] 'my/shell-toggle)
Handle shell termination
There are several ways you could handle terminating the shell. You could, just for shell buffers, turn off the prompt that asks you if you’re sure you want to kill a buffer with an active subprocess running in it, and then use C-x k. But this would kill bash rudely, and bash would not save your command history. You could tell bash to save command history after every command. But this leaves executed commands between different shells all intermixed in the history file and shells would no longer have their own individual session history. That doesn’t appeal to me.
So I opted for another method: detect when the shell terminates and close the emacs buffer. This way, you can hit Ctrl-D (same as you would in a terminal window), bash saves its history, and everyone’s happy.
(lambda ()
(set-process-sentinel (get-buffer-process (current-buffer))
'my/shell-process-sentinel)))
(defun my/shell-process-sentinel (process state)
;; show shell status and kill the buffer automatically
(message "shell: %s" state)
(if (or
(string-match "exited abnormally with code.*" state)
(string-match "finished" state))
(kill-buffer (current-buffer))))
Nice.