Emacs Angel

Updated [2015-08-19 Wed]

Motivation

I have been running more emacs sessions these days (GTD, 2 for django contract, 2 for personal project, misc editing), and noticing the two seconds of lag it takes to load the emacs init file.

A good solution to this is run emacs as a daemon, where sessions are just clients on the background emacs process.

Objectives

Setup generic emacs daemon with systemd

Hooking it into post activate on virtual environments

Setup Systemd Service Daemon

Ahh, the systemd saga continues. Template services make complete sense now, and I’ve got a taste of --user mode, which I think this demonstrates is a great feature.

The below command sets up the service template file. A template file is convenient, as emacs daemons can be spawned arbitrarily using

systemctl --user start emacs@daemonname.service

Which uses a systemd template unit that you can create with the following command

mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/emacs.service << EOF
[Unit]
Description=Emacs: the extensible, self-documenting text editor

[Service]
Type=forking
ExecStart=/usr/bin/emacs --daemon=%i
ExecStop=/usr/bin/emacsclient --socket-name=%i --eval "(kill-emacs)"
Restart=always

[Install]
WantedBy=default.target
EOF

Enabling the ‘main’ instance of the template emacs daemon service will start that one at boot. I almost always go straigh to GTD after boot so it’s nice to have it immediately available.

systemctl --user start emacs@main.service
systemctl --user enable emacs@main.service
systemctl --user status emacs@main
emacs@main.service - Emacs: the extensible, self-documenting text editor
   Loaded: loaded (/home/joth/.config/systemd/user/emacs@.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2015-07-27 00:17:18 ADT; 1min 7s ago
 Main PID: 16863 (emacs)
   CGroup: /user.slice/user-1000.slice/user@1000.service/emacs.slice/emacs@main.service
           ├─16863 /usr/bin/emacs --daemon=main
           ├─16964 sh
           └─16965 systemctl --user status emacs@main

Jul 27 00:17:17 archLenFlex emacs[16862]: [yas] Loading for `lisp-interaction-mode', just-in-time: (lambda nil (yas--load-directory-1 (quote /home/joth/.emacs.d/elpa/yasnippet-20150415.244/snippets/lisp-interaction-mode) (quote lisp-interaction-mode)))!
Jul 27 00:17:17 archLenFlex emacs[16862]: [yas] Loading compiled snippets from /home/joth/.emacs.d/elpa/yasnippet-20150415.244/snippets/lisp-interaction-mode
Jul 27 00:17:17 archLenFlex emacs[16862]: [yas] Loading for `emacs-lisp-mode', just-in-time: (lambda nil (yas--load-directory-1 (quote /home/joth/.emacs.d/elpa/yasnippet-20150415.244/snippets/emacs-lisp-mode) (quote emacs-lisp-mode)))!
Jul 27 00:17:17 archLenFlex emacs[16862]: [yas] Loading compiled snippets from /home/joth/.emacs.d/elpa/yasnippet-20150415.244/snippets/emacs-lisp-mode
Jul 27 00:17:18 archLenFlex emacs[16862]: [yas] Loading for `prog-mode', just-in-time: (lambda nil (yas--load-directory-1 (quote /home/joth/.emacs.d/elpa/yasnippet-20150415.244/snippets/prog-mode) (quote prog-mode)))!
Jul 27 00:17:18 archLenFlex emacs[16862]: [yas] Loading compiled snippets from /home/joth/.emacs.d/elpa/yasnippet-20150415.244/snippets/prog-mode
Jul 27 00:17:18 archLenFlex emacs[16862]: Loading /home/joth/.emacs.d/elpa/yasnippet-20150415.244/snippets/prog-mode/.yas-setup...
Jul 27 00:17:18 archLenFlex emacs[16862]: Loading /home/joth/.emacs.d/elpa/yasnippet-20150415.244/snippets/prog-mode/.yas-setup...done
Jul 27 00:17:18 archLenFlex emacs[16862]: Starting Emacs daemon.
Jul 27 00:17:18 archLenFlex systemd[461]: Started Emacs: the extensible, self-documenting text editor.

Then we can start an emacs session with

emacsclient -c --socket-name=main file

I replaced a few environment variables in my .zshrc

cat ~/dotfiles/zsh/.zshrc | grep emacsclient
export EDITOR="emacsclient -c --socket-name=main"
export VISUAL="emacsclient -c --socket-name=main"
alias gtd="emacsclient -c --socket-name=main /home/$USER/projects/9999-99-99_master/computing.org"

Setup Virtual Environment Hooks

What I want is to run something like the following with virtualenvwrapper

workon someproj

From there, the following should happen (these can go in the default postactivate virtualenv script).

  • get variables
export VENV_NAME=$(echo ${VIRTUAL_ENV} | awk -F "/" '{print $(NF)}')
export PROJ_DIR=$(cat ${VIRTUAL_ENV}/.project)
  • spawn an emacs daemon
systemctl --user start emacs@${VENV_NAME}.service
  • alias emacs to emacsclient
alias emacs=$(emacsclient -c --socket-name=${VENV_NAME})
  • open a client of that daemon probably on the project directory
emacs $PROJ_DIR
  • export VISUAL and EDITOR using emacsclient
export VISUAL=emacs
export EDITOR=emacs
  • finally, open emacsclient on the project directory
$VISUAL $PROJ_DIR

Works like a charm. Here’s the systemd status after activating a couple virtual environments and the gtd.

systemctl --user status | tail -n 15
├─461 /usr/lib/systemd/systemd --user
├─463 (sd-pam)  
└─emacs.slice
  ├─emacs@main.service
  │ ├─16863 /usr/bin/emacs --daemon=main
  │ ├─18467 /usr/sbin/idn --quiet --idna-to-ascii --usestd3asciirules
  │ ├─22898 sh
  │ ├─22899 systemctl --user status
  │ └─22900 tail -n 15
  ├─emacs@8888-88-88_jothamcity.service
  │ └─22506 /usr/bin/emacs --daemon=8888-88-88_jothamcity
  ├─emacs@6666-66-66_ocp-cms.service
  │ └─21744 /usr/bin/emacs --daemon=6666-66-66_ocp-cms
  └─emacs@9999-99-99_omegatrain.service
    └─21640 /usr/bin/emacs --daemon=9999-99-99_omegatrain

For reference, here’s the full postactive I am currently [2015-08-19 Wed] using.

cat ~/venvs/postactivate

And here’s the predeactivate hook

cat ~/venvs/predeactivate

Hackish, for now, and better than it used to be!

Bugs/Issues [1/4]

  • [X] For one thing, the emacsclient frames do not start maximized. Add the following to .emacs: [2015-08-19 Wed]
(add-to-list 'default-frame-alist
    '(fullscreen . fullboth))

I had set ~’initial-frame-alist~ before, watch out for that as it only maximizes the first client frame.

  • [ ] Magit commit buffers, when closed, don’t return to the magit status buffer.

  • [ ] Killing the buffer that the client opens on kills the whole frame

  • [ ] Currently, daemon is stopped when venv is deactivated while the venv may be active in other shells

I’m sure a few more bugs will crop up, I will update this post with solutions as I come across them. Post in comments if you have any suggestions!

Resources

Archwiki link

Go Top
comments powered by Disqus