Gnu Emacs With Server Mode on OS-X
I have used the Emacs for Mac OS-X distribution of Gnu Emacs for years and have been quite pleased with it. Emacs' server-mode is great in that it makes for very fast load times and it allows sharing buffer contents, kill rings, etc., between windows. However, I found it a little tricky to get it working perfectly on OS-X. These are my notes on how to install and configure it so that server-mode works correctly on OS-X.
Under server-mode, the emacsclient program is used to edit files. I have another note about how to configure emacsclient as an OS-X application. This may be worth reading as well.
Configuration
-
Install Emacs for Mac OS-X.
-
Update your path:
# Emacs.app binaries must come before /usr/bin # Using the apple provided emacsclient with a different # emacs in server mode is a common source of problems. PATH=/Applications/Emacs.app/Contents/MacOS/bin:$PATH
-
Create a script named ec that invokes emacsclient with desired options. Note that this returns immediately; it does not wait for the edit to complete.
#!/usr/bin/env bash # -n --> Return immediately, don't wait for edit to complete # -a "" --> If emacs is not running in server mode, start it # -c --> create a new frame instead of using existing frame emacsclient -n -a "" -c "$@"
-
Create a script named ecw that invokes emacsclient with desired options. Note that this command will not exit until the edit is complete.
#!/usr/bin/env bash # -a "" --> If emacs is not running in server mode, start it # -c --> create a new frame instead of using existing frame emacsclient -a "" -c "$@"
-
Many programs use the EDITOR and VISUAL environment variables to determine which editor to use. Set these up to use ecw.
export EDITOR=ecw export VISUAL=ecw
Whenever you run ec or ecw, an emacs daemon in server-mode will be started, if one doesn't already exist.
This is the simplest emacs setup that I've used and it works well.
Lagniappe
While not specific to OS-X, this is germane to server-mode emacs.
I'm used to killing buffers with C-x k. When first starting to use server-mode, I started seeing warnings about a client still using the buffer that I was killing. For my (simple) use case, the following allows me to keep using my old muscle-memory to kill the buffer yet avoid the warning:
(add-hook 'server-switch-hook (lambda () (when (current-local-map) (use-local-map (copy-keymap (current-local-map)))) (when server-buffer-clients (local-set-key (kbd "C-x k") 'server-edit))))
Alternate Configuration
Before I settled on the above approach, I configured OS-X to automatically start emacs in server-mode at boot time. This does work, but it isn't as clean as the above. Here's how to do it:
-
Create the plist ~/Library/LaunchAgents/org.fsf.gnu.emacs.plist using an editor:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>org.fsf.gnu.emacs</string> <key>ProgramArguments</key> <array> <string>/Applications/Emacs.app/Contents/MacOS/Emacs</string> <string>--daemon</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <!-- restart if it terminates --> <key>ServiceDescription</key> <string>Gnu Emacs Daemon</string> </dict> </plist>
-
Install the plist:
$ launchctl load -w ~/Library/LaunchAgents/org.fsf.gnu.emacs.plist
Note: I made up org.fsf.gnu.emacs. There's nothing magic about it; it could be any unique value.
Now OS-X will start emacs in daemon mode and keep it running for you. I have had weird things happen with this approach, which is why I converted to the first method presented.
'Open-with' and emacsclient
After creating an emacsclient app via Platypus, I was disappointed to notice that the app did not appear in the 'Open With' menu when a supported file was control-clicked on.
Here is how I took care of this issue.
/Applications/emacsclient.app/Contents/Info.plist contains an array of file extensions that it knows how to open; they are stored under the CFBundleDocumentTypes key. The array was empty, containing only a *.
I concluded that a suitable array could be copied from Emacs.app and set about trying to insert it into emacsclient's Info.plist. After a few hours of PlistBuddy and plutil, I was unable to get what I inserted as the array to be properly recognized.
This is why I don't like binary / non-editable configuration files. There's no reason that the plist shouldn't be editable text: YAML, XML, JSON, or whatever. A binary format for configuration files that can only be modified with dedicated tools is insane.
I finally dropped back to Python, having bypassed it initially, assuming that programming be the long road to get it done. I was surprised to see that Python includes plistlib in its standard distribution. It made things ridiculously easy.
The following code grabs the array from Emacs.app, and inserts it into a copy of Info.plist taken from emacsclient.capp. It saves the modified result as Info.plist.new in the current directory.
#!/usr/bin/env python # Run this script from the command line with the path to the Emacs.app # Info.plist as the first argument and the path to the emacsclient.app # Info.plist as the second argument. 'Info.plist.new' will be created # in current direcotory. import sys, plistlib, os (pl_from, pl_to) = sys.argv[1:] plf = plistlib.readPlist(pl_from) plt = plistlib.readPlist(pl_to) plt['CFBundleDocumentTypes'] = plf['CFBundleDocumentTypes'] plistlib.writePlist(plt, os.path.basename(pl_to) + '.new')
When this completes, copy the resulting Info.plist.new to emacsclient.app/Contents/Info.plist (after making a backup copy).
So that OS-X picks up the change, it is necessary to run lsregister, which causes Launch Services to rebuild its database. Here's how:
# Valid for OS-X 10.11.2. If the path is invalid on yours, # run 'locate lsregister' to find it. % F=/System/Library/Frameworks/CoreServices.framework % F+=/Versions/A/Frameworks/LaunchServices.framework % F+=/Versions/A/Support/lsregister % $F -lint -kill -r -domain local -domain system -domain user
This may take 15-30 seconds to run. After that, emacsclient will appear in your 'Open With' menu the next time you use it; it's worth the effort.