Emacs Running All The Time
Monday, October 1, 2007
I love working in Emacs; however, it can be irritatingly slow when starting up (it's funny how a few seconds seems sooo slllooowwww these days!). This is normally not a big deal, but you tend to notice it more if you want to view a document and you have to wait a few seconds for Emacs to startup. Like a lot of long-time Emacs users, I try to avoid this minor aggrevation by not shutting down Emacs. Unfortunately, in addition to using Emacs for "real work", I compulsively tinker with my Emacs setup. Not just every now and then, but a lot. And, although I know how to unload modules and back out changes, sometimes it is easier to just restart Emacs rather than try to "uninstall" things. The problem is that there are things that I don't want to kill (for example, I use Gnus for reading mail and Usenet news, jabber.el for IM, and ERC for IRC and I don't want to have to restart them all the time). So, I usually keep a terminal emacs running in addition to an instance of Aquamacs Emacs. I tend to use terminal Emacs as my "stable" Emacs for mail/IRC/IM/etc and Aquamacs Emacs as my main development Emacs. There are a number of things that I've done to facilitate this "multi-Emacs" setup:
- Emacs: I usually run with the latest version of Aquamacs Emacs (either release or CVS) as I consider it the best Emacs distribution for Mac OS X. Since the CVS versions of Aquamacs Emacs are based on the CVS version of Emacs, it's also a convenient way to stay current with the latest developments in CVS Emacs. So, I like to use the emacs executable that is installed with the Aquamacs application instead of whatever is currently in "/usr/bin/emacs". Therefore, I have my .bashrc and some shell scripts setup to ensure that my terminal Emacs is the same as the one included in Aquamacs (I can always override that by explicitly specifying "/usr/bin/emacs" if I need to).
- EmacsClient: I wanted to have a consistent way of sending things to Emacs regardless of whether I had a terminal Emacs open or Aquamacs Emacs and regardless of whether I was sending the document from a command line or from a Mac application to Emacs. I do this by having an "emacsclient.app" application setup (as described in an earlier post) as well as using the conventional emacsclient program. For a lot of document types, I set the default Application to be emacsclient.app. However, I can send any document to Emacs by right-clicking on a file and selecting "Open With" emacsclient.app.
- Bash/Screen: I posted previously on how great the "screen" utility is. I run my terminal Emacs inside of screen. However, I have my .bashrc and .screenrc initialization files setup so that every time I run Terminal.app I either get a new screen session or reconnect to an existing one (a variation on what is described here and here)
- emacsclient.app: Setup as described in my
'Using "Open with" Emacs on a Mac' post. This lets me use standard
Mac functionality to "Open With" emacsclient (either terminal
Emacs or Aquamacs Emacs as described below). Therefore, I can use
standard emacssclient functionality regardless of whether I'm
working at a command line or using Mac applications. And, it
doesn't matter if I have a terminal Emacs open or Aquamacs Emacs
open or both - both emacsclient and emacsclient.app will send a
document to whichever Emacs is currently running.
- .emacs: I have the following statements in my
.emacs file to start up emacsclient server support and so that (if Emacs is invoked by emacslient from a
command line) Emacs
knows which screen window to return to (using the screen window#
that is stored in the "~/.emacsclient-caller" file - see below):
(server-start) (add-hook 'after-init-hook 'server-start) (add-hook 'server-done-hook (lambda () (shell-command "screen -r -X select `cat ~/.emacsclient-caller`")))
- "e" bash script: The following bash script lets me send
documents to either emacsclient
(configured as the default "EDITOR" in my .bashrc file below and
which will send the document to either my screen-based terminal
Emacs instance or to a running instance of Aquamacs Emacs) or to
the Emacs executable that comes with Aquamacs Emacs
(configured as an "ALTERNATE_EDITOR" in my .bashrc file below) from a
command line by doing "e doc.txt". In addition, I have "emacs" aliased
to the "e" bash script in my .bashrc file (see below). This means
that a running instance of Emacs will normally be used; however, in the event that no instance of
Emacs is running, one will be started. The script also stores the
screen window# to a file called "~/.emacsclient-caller" so that
Emacs will know which screen window to return to after editing has
been completed:
#!/bin/sh echo $WINDOW >~/.emacsclient-caller screen -r -X select 1 emacsclient -n "$@"
- "em" bash script: The following bash script lets me open
a new instance of a terminal Emacs in a terminal:
#!/bin/sh -e /Applications/Aquamacs\ Emacs.app/Contents/MacOS/Aquamacs\ Emacs -nw --debug-init "$@"
- ".bashrc" init file: I configure my .bash_profile
file to call .bashrc so that login shells have the same user
configurations as regular shells. The following subset of my
.bashrc initialization
file configures "emacsclient" as my default editor and "em" as my
alternate editor. Therefore, if Emacs isn't running (for some strange
reason) when the default editor is invoked, it will be started (using the version of Emacs that comes
with Aquamacs Emacs). In addition, if screen was previously
disconnected, then it is reconnected (avoiding the startup time required for
firing up Emacs); otherwise, screen is started up (see
.screenrc below) with an instance of bash running in one screen window and an
instance of Emacs running in another screen window (this
is a key point: screen is always run or reconnected to whenever I start a
new Terminal.app):
#!/bin/bash export EDITOR=emacsclient export ALTERNATE_EDITOR=em alias emacs='~/bin/e' if [ "$PS1" != "" -a "${STARTED_SCREEN:-x}" = x -a "${SSH_TTY:-x}" ] then STARTED_SCREEN=1 ; export STARTED_SCREEN sleep 1 screen -RR && exit 0 # normally, execution of this rc script ends here... echo "Screen failed! continuing with normal bash startup" fi".screenrc" init file: My .screenrc file is pretty simple. Basically, I setup some convenience things, redefine the escape key as "C-z" (I find "C-z" a better alternative to the default "C-a" binding when using Emacs in screen as the standard Emacs "C-z" binding suspends Emacs and I normally have no need to suspend Emacs if it's running in screen), and specify two startup screens - "0" being a bash shell and "1" being Emacs (using the version of Emacs that comes with Aquamacs Emacs). The "selected" screen (e.g. - the one that you first see) is the bash shell since it's normally the command line that I want when I kick off Terminal.app (Emacs, in the meantime, initializes in the background in screen window #1 while I'm using bash in screen window #0).# Suppress screen's startup message startup_message off
# Define a bigger scrollback, default is 100 lines defscrollback 10000
# An alternative hardstatus to display a bar at the bottom listing the # windownames and highlighting the current windowname in blue. hardstatus alwayslastline "%{.bW}%-w%{.rW}%n %t%{-}%+w %=%{..G} %H %{..Y} %m/%d %C%a "
# Execute .bash_profile on startup shell -$SHELL
# Use C-z instead of C-a as this makes more sense for Emacs escape ^za
# Detach on hangup autodetach on
# ------------------------------------------------------------------------------ # STARTUP SCREENS # ------------------------------------------------------------------------------
screen -t Shell 0 bash screen -t Emacs em select 0
At some stage, the upcoming Emacs multi-tty support will probably eliminate a lot of the need for my current dual-Emacs configuration. However, in the meantime, it works nicely for me! :-)
Update-2007-10-02: I originally had in my .screenrc file these lines:
hardstatus on
hardstatus alwayslastline
hardstatus string "%{.bW}%-w%{.rW}%n %t%{-}%+w %=%{..G} %H %{..Y} %m/%d %C%a "
However, Johannes Weiner pointed out that this could be replaced by:hardstatus alwayslastline "%{.bW}%-w%{.rW}%n %t%{-}%+w %=%{..G} %H %{..Y} %m/%d %C%a "
So, I changed it. Somehow, it always feels nicer to remove lines of
code! ;-)

