Install Emacs on Mac OS X: daemon, automator app and multiple themes

This is a quick note on the setup of Emacs on Mac OS X.

Install Emacs on OS X

The simplest way to install Emacs on Mac is using Homebrew. Here is the commands:

1
2
$ brew update
$ brew install emacs --with-cocoa --with-gnutls

If you want to try a patched Emacs with transparent titlebar, install as follows

1
2
$ brew tap d12frosted/emacs-plus
$ brew install emacs-plus --with-natural-title-bar

(You may add --without-spacemacs-icon, if you keep only one version of Emacs.)

Run Emacs as daemon

If you have run the Emacs, you can start an Emacs server by the emacs-lisp function (server-start). Then you could anywhere open files in or create a frame of this Emacs by emacsclient -nc. However, this is rarely used. An alternative way is to run Emacs as daemon

1
$ emacs --daemon

and open emacsclient in the same way.

In my Mac, I run multiple Emacs servers (each Emacs process as daemon is called one server) to allow using multiple color themes (e.g. I use a light theme for text editing like Markdown, LaTeX, and use dark themes for coding). The way to do it is giving each daemon process a name so as to connect to it later via emacsclient:

1
2
$ emacs --daemon=main
$ emacs --daemon=coding

and later you could connect to them, e.g. the main server, by

1
emacsclient -nc --socket-name=main

To stop an Emacs server (the daemon process), you could call (kill-emacs) in any Emacs frame connecting to this daemon. Alternatively, use system command kill with process ID listed and grepped by ps aux | grep -i 'emacs --daemon'.

To quickly start Emacs as daemon, connect to and kill them, I wrote a bash script function emacs-server-func.sh on my github repository, which provides commands es, ec, ecc:

  • es: list all Emacs daemon processes
  • es start: start main and coding servers by default
    • es start m or es start main: start main server
    • es start c or es start coding: start coding server
    • es start YOURNAME: start a sever with your given name
  • es stop: stop all Emacs daemon processes
    • es stop m or es stop main: stop main server, similar for coding
    • es stop YOURNAME: start the sever with the given name
  • ec FILENAME: connecting to the main server and open the file FILENAME in a new frame
  • ecc FILENAME: connecting to the coding server and open the file FILENAME in a new frame.

Alternatively, we could use Alfred.app to perform the same tasks. We need its PowerPack in order to build Workflows. Create an Input using Keyword, with the keyword es start (with Argument Optional, enabling with space), shown as

Then, connect to an Action of Run Script, in which choose bash and fill the following codes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
EMACS=/usr/local/bin/emacs

if [[ -z $1 ]]; then
$EMACS --daemon=main
$EMACS --daemon=coding
else
case $1 in
m)
$EMACS --daemon=main
;;
c)
$EMACS --daemon=coding
;;
*)
$EMACS --daemon="$1"
;;
esac
fi

Similarly create workflows for es stop, ec, etc. The scripts are the same as the codes provided in emacs-server-func.sh. We just copy and paste them into Alfred and create shortcuts to run them. The whole workflow looks as

and you could find the workflow, named Emacs.alfredworkflow, on my github repository.

Since Mac OS X by default only allows one Emacs instance (not via daemon), we may use the follow code to open multiple ones, which is created as a workflow in Alfred triggered by em new:

1
open -a /usr/local/opt/emacs-plus/Emacs.app -n

Create an app via Automator to open files in Emacs clients by double-clicks

Automator is a built-in OS X app for creating custom automated user workflows for just about any installed app you might have or even OS functionality. Suppose we could like to open files by default in the Emacs client named as main. You may open files in the Emacs.app, however, with too much user-defined configurations, it is too slow to be acceptable. Using the daemon mode and opening in clients are a better choice.

The following shows how to create an app that allows opening files in Emacs clients.

  1. Launch Automator and create a new document. Select Application as its type.

  2. Search the Actions palette on the left for the Run AppleScript action and add it to your Automator document.

  3. In the Run AppleScript building block, copy and paste the following codes:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    on run {input, parameters}

    set posixPath to POSIX path of input

    tell application "iTerm" to do shell script "/usr/local/bin/emacsclient -nc --socket-name=main " & quoted form of posixPath

    return input

    end run

    and Save it with name, e.g. Open in Emacs.app in the file format Application.

  4. Now you go to the files with specific extensions, right-click it, choose Get Info and choose our app Open in Emacs.app as the application to open it. If you prefer to using Emacs to open all files of this extension, click Change All....

Load different themes for Emacs servers/daemons

In practice, I prefer light themes for text editing (like Markdown, LaTeX, org mode) and dark themes for programming. To achieve, we run multiple Emacs daemon processes, e.g. main and coding. Now we will describe how to load different themes in your .emacs or init.el configurations.

First of all, we need to prepare a few variables and loading functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
;; Variables
(defvar zyue-theme nil
"A symbol representing the color theme to load.")
(defvar zyue-font nil
"The default font to use. Expects a `font-spec'.")

;; Init or reload functions
(defun zyue-init-ui (&optional frame)
;; load theme
(when zyue-theme
(load-theme zyue-theme t))
;; if you customize modeline, place it here...
;; load font
(when (display-graphic-p)
(when (fontp zyue-font)
(set-frame-font zyue-font nil (if frame (list frame) t))
(set-face-attribute 'fixed-pitch frame :font zyue-font))))

(defun zyue-reload-ui-in-daemon (frame)
"Reload the theme (and font) in an daemon frame."
(when (or (daemonp) (not (display-graphic-p)))
(with-selected-frame frame
(run-with-timer 0.1 nil #'zyue-init-ui))))

These functions are essential since the theme has to be loaded when the frame is firstly created, instead of when the elisp files are loaded during emacs --daemon.

Then we define different themes for each severs (the daemon processes), for examples:

1
2
3
4
(when *is-mac* (setq zyue-theme 'doom-nord-light))
(when *is-server-main* (setq zyue-theme 'solarized))
(when *is-terminal* (setq zyue-theme 'spacemacs-dark))
(when *is-server-coding* (setq zyue-theme 'atom-one-dark))

where these constant variables are defined as follows in advance for convenience:

1
2
3
4
5
(defconst *is-mac* (string-equal system-type "darwin"))
(defconst *is-linux* (string-equal system-type "gnu/linux"))
(defconst *is-terminal* (not (or (display-graphic-p) (daemonp))))
(defconst *is-server-main* (string-equal "main" (daemonp)))
(defconst *is-server-coding* (string-equal "coding" (daemonp)))

The last step is to load or reload themes depending on specific Emacs running modes:

1
2
3
4
;; Load the theme and fonts
(if (daemonp)
(add-hook 'after-make-frame-functions #'zyue-reload-ui-in-daemon)
(zyue-init-ui))