Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carwin/dd24caa2d30ad3693938c7aa6b889afb to your computer and use it in GitHub Desktop.
Save carwin/dd24caa2d30ad3693938c7aa6b889afb to your computer and use it in GitHub Desktop.
Emacs as an Org capture server

Emacs as an Org capture server

The Concept

The general idea is to run an Emacs server as a daemon which clients can quickly connect to via a bash script. The client executes org-capture and the frame closes upon finalizing or aborting the capture.

Running a server daemon

The first step is to get an Emacs daemon running as a server with your name of choice. For this example, I’m going to use “capture” as the server name.

From your terminal start the server daemon and give it a name

emacs --daemon="capture"

Running a client

You can connect to this server with a client by launching Emacs from the terminal with the following arguments:

emacsclient --create-frame \
            --socket-name 'capture' \
            --alternate-editor='' \
            --no-wait

We’ll make a script out of this and add more args after we set up things on the Elisp side.

Elisp setup

In your init.el file (or in a separate package if you like) define the functions that make this server’s clients run org-capture and close.

my/delete-capture-frame

This function deletes the frame after you finalize a capture:

(defun my/delete-capture-frame (&rest _)
  "Delete frame with its name frame-parameter set to \"capture\"."
  (if (equal "capture" (frame-parameter nil 'name))
      (delete-frame)))
(advice-add 'org-capture-finalize :after #'my/delete-capture-frame)

my/org-capture-frame

This function is a modernized version of the info found here:

This function runs when creating a new client. It selects the correct frame and forces org-capture to show the template selection menu in the main window instead of splitting it. It also closes the frame if you abort the capture process.

(defun my/org-capture-frame ()
  "Run org-capture in its own frame."
  (interactive)
  (require 'cl-lib)
  (select-frame-by-name "capture")
  (delete-other-windows)
  (cl-letf (((symbol-function 'switch-to-buffer-other-window) #'switch-to-buffer))
    (condition-case err
        (org-capture)
      ;; "q" signals (error "Abort") in `org-capture'
      ;; delete the newly created frame in this scenario.
      (user-error (when (string= (cadr err) "Abort")
                    (delete-frame))))))

Now you can flesh out the emacsclient command from earlier to run your dedicated org-capture server:

#!/bin/bash
emacsclient --create-frame \
            --socket-name 'capture' \
            --alternate-editor='' \
            --frame-parameters='(quote (name . "capture"))' \
            --no-wait \
            --eval "(my/org-capture-frame)"

You can bind this script to a global keybinding and you should be able to capture from ‘outside’ Emacs via that binding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment