why I don’t run shells inside Emacs

This article describes my first attempt at running shells inside Emacs, which didn’t work out. I tried again recently, and I think I’m going to stick with it, so this is preserved for posterity only!

When I’m working, I pretty much live inside Emacs. It’s my IDE, debugger, text editor, email composer, spreadsheet, file manager, and all-around scratch pad. The only other app that’s as ubiquitous in my work routine is my shell, tcsh inside rxvt.

I’ve customized the hell out of both Emacs and tcsh to make them work the way I want. I have Emacs whipped into shape, and tcsh too, mostly, except for one thing: copying without the mouse.

I hate the mouse. It’s good for some things, but not my work. Switching between the mouse and the keyboard wreaks havoc on my muscle memory and fine-motor flow. Thanks to Emacs, Ion, Pine, Gaim, and Firefox‘s blessed Find As You Type, I can do pretty much everything with the keyboard. The only thing I can’t do with the keyboard is copy text from a terminal. And that irks me.

A voice in the back of my head had been nagging me about this for a while. Ryan, you can fix this, it said, and you know how. It was right. I didn’t want to admit it, but I did know how, and it did too. Why don’t you try running your shells inside Emacs?

I had a laundry list of reasons. Half of my .cshrc would be usless. I’d forgo acoc‘s wonderful colorized command output. Emacs’ shell-mode would never do history management as well as native tcsh. And I wouldn’t get that cool transparent background. :P

The voice in the back of my head didn’t go away, though. Every time I reached for the mouse to copy something out of a terminal, it piped up. You could fix that, you know. Sigh. I knew.

Finally, it got the better of me. I wrote try emacs shell mode on my todo list, and a few days later, I tried it. M-x shell was all it took, and it worked surprisingly well. What’s more, most of my reasons for avoiding it were unfounded. Here’s what worked well:

  • Copying with the keyboard. This goes without saying. What’s more, all of my Emacs muscle memory for navigating and searching now applied to the entire contents of my shell!

  • Session management. All of my shells were at my fingertips, regardless of which Emacs frame I was currently in. No more context switching into a different Ion frame on a different virtual desktop just to check on a command. Even better, I could continue using my work shells at home, over VPN, just by connecting to my work Emacs!

  • Tcsh integration. Emacs was surprisingly sociable with tcsh. It obeyed my .cshrc, read and wrote its command history to ~/.history, and generally played nice.

  • ANSI color codes. Wonder of wonders, Emacs could even display color codes properly. Just do M-x ansi-color-for-comint-mode-on.

  • Find-file-at-point. This handy little elisp function opens, in a new Emacs buffer, the filename that your cursor is currently on. After an ls, if I wanted to look at a file, I just moved up to it and hit M-C-f (bound to find-file-at-point), and Emacs would open the file. Slick.

It wasn’t perfect, though. Here’s what didn’t work well:

  • Not a terminal. I gained keyboard navigation at the expense of having a true terminal emulator. I couldn’t run any app that needed anything more than a dumb terminal – Pine, BitTorrent, less, and others. I could use Term mode, which provides a full terminal, but that defeats the purpose of running inside emacs.

  • History. As I suspected, command history wasn’t quite as good as stock tcsh. It worked ok for history in the current shell’s session, but I didn’t have access to history from previous sessions.

  • Remote completion. Emacs could tab-complete local files and directories, but as soon as I sshed to another machine, it was helpless. OK, that’s not quite true…I tried TRAMP, and it worked, but it was unbearably, unusably slow. Sigh.

  • Case-sensitive completion. File and directory tab completion is case sensitive. It doesn’t obey the completion-ignore-case variable. Boo.

  • Slow color code parsing. My joy at finding ansi-color-for-comint-mode-on turned to horror as soon as I did an ls -l in a large directory. Emacs’ color code parsing is dog slow. I could use ansi-color-for-comint-mode-filter to just ignore the color codes, but I couldn’t bear to go without them.

So, you might ask, what was the verdict? The jury’s still out. I’m currently wrestling GNU screen into submission, since it also allows keyboard navigation and copy/paste over shell contents. So far, I’ve synchronized its paste buffer and the X selection, and I’ve Emacs-ified its copy-scrollback mode. Stay tuned for more!

Here’s what I added to my .emacs to make shell-mode behave the way I wanted:

(custom-set-variables
 '(comint-scroll-to-bottom-on-input t)  ; always insert at the bottom
 '(comint-scroll-to-bottom-on-output t) ; always add output at the bottom
 '(comint-scroll-show-maximum-output t) ; scroll to show max possible output
 '(comint-completion-autolist t)        ; show completion list when ambiguous
 '(comint-input-ignoredups t)           ; no duplicates in command history
 '(comint-completion-addsuffix t)       ; insert space/slash after file completion
 )

; interpret and use ansi color codes in shell output windows
(ansi-color-for-comint-mode-on)

; make completion buffers disappear after 3 seconds.
(add-hook 'completion-setup-hook
  (lambda () (run-at-time 3 nil
    (lambda () (delete-windows-on "*Completions*")))))

;; run a few shells.
(shell "*shell5*")
(shell "*shell6*")
(shell "*shell7*")

; C-5, 6, 7 to switch to shells
(global-set-key [(control \5)]
  (lambda () (interactive) (switch-to-buffer "*shell5*")))
(global-set-key [(control \6)]
  (lambda () (interactive) (switch-to-buffer "*shell6*")))
(global-set-key [(control \7)]
  (lambda () (interactive) (switch-to-buffer "*shell7*")))

12 thoughts on “why I don’t run shells inside Emacs

  1. fascinating! i’d never heard of ControlMaster before.

    sadly, the machines i work on regularly all have openssh 3.7.x, and some of them would be bears to upgrade. still, i’ll keep it in mind. thanks!

  2. Natesh says:

    Have you faced any interference from shell-mode on the existing buffers’ fontifiaction?
    Whenever I open a shell in the emacs, all the existing
    lisp and c files loose their fontification especially the font-lock-comment-face. I have set it to “lightcoral” which gets reset to white.

  3. I had similar gripes initially, but Term mode seems to do the job. Switching between line-mode (“C-c C-j” ) and char-mode (“C-c C-k”), you have a terminal emulator which you can treat as a Shell mode buffer when you need to.

  4. Anonymous says:

    What drives me nuts is filename completion in and winxp cygwin xemacs subshell always adds “\” rather than “/” The documentation for comint-completion-addsuffix says it will add “/” but on my systems it always adds “\”

  5. Mike Makuch says:

    “I hate the mouse.” – Finally someone else get’s it! The mouse is great for people new to a gui. It’s intuitive and easy to use. But it’s for beginners. It’s a detriment to productivity. I’ve spent the past 20+ years tweaking my environments to avoid it.

  6. Pingback: Open Emacs dired buffer from dired | fortune datko

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>