Using the (function …) target in org-capture


In the past two months, I’ve started diving into Emacs.

(Skip to “The problem” for information about org-capture’s (file …) target.)

Over the past 6-8 months, I’ve been struggling to find a good way to store my notes, todos, and other random data. I tried bending Google Drive to what I needed, but it felt clunky and didn’t have the level of configurability (is that a word?) I was looking for. I tried Notion, but their interface wasn’t fast enough (I’m on Linux, so I was using the web client). Notion wasn’t that slow, but I noticed that I often didn’t write things down because of the slight delay between the desire to write something down and the ability to write it down. Plus, as a programmer, it drove me a little crazy that I couldn’t customize every last little thing.

During my note-taking adventures, org-mode for Emacs kept popping up. I kept pointedly ignoring it, because I’d tried Spacemacs a while back and was a little overwhelmed by its huge feature set…but my curiosity about org-mode won out, and one fateful night I downloaded vanilla Emacs.

Aaaand fast-forward a couple months (and far too much time messing with config settings): I’m an Emacs/org-mode convert! I love how I can already do most of my non-browser work from inside Emacs. It really does do everything…including Tetris. What more do you need to know?!

The problem

I’ve set up some org-capture templates for org-mode structures I found myself using often, but something was missing: there was no built-in way to automatically create a new file when invoking an org-capture template. In the org-capture documentation, there’s a section describing the different target options.1 The only one that would allow me to create a new file when I used the template was this one:

(function function-finding-location)

Most general way: write your own function which both visits the file and moves point to the right location.

Unfortunately, that doesn’t give a lot of guidance on what function-finding-function should look like. Does it take any arguments? Does it return anything?

After stepping through org-capture.el quite a few times, and understanding how some of the other target options (like file+function) work, I figured it out. function-finding-function needs to a) open the buffer that you want to insert the template into, and b) move the point to the spot in that buffer where you want to insert the template. This would probably be obvious to someone who’s familiar with Emacs/Emacs Lisp, but it took me quite a while to puzzle through.2

The solution

Here’s what my working function-finding-function looks like in practice:

(defun open-new-project-file ()
   (let ((fpath (read-file-name "Project file name: "
                               (org-subdir "/projects")
                               nil nil nil)))
    (find-file fpath)
    (goto-char (point-min))))

Step by step:

  1. Prompt the user for the file to write to (defaults to the /projects subfolder of my org config):
    (let ((fpath (read-file-name "Project file name: "
                                 (org-subdir "/projects")
                                 nil nil nil)) ...)
    

    org-subdir is a tiny utility function I defined:

    (defun org-subdir (subdir)
      (concat "~/org" subdir))
    
  2. Create a buffer for the new project file, and open that buffer in the current window:
    (find-file fpath)
    
  3. Place the point3 at the very beginning of the buffer.
    (goto-char (point-min))
    

That’s all there is to it! I hope this prevents someone else from spending as long on this as I did. If you have any issues, questions, or just want to talk org-mode, feel free to email me!


1 This article is a better introduction to org-capture than the official documentation.

2 It mostly took so long because I spent far too long thinking the issue had to do with my target, when the actual issue was that I’d chosen the wrong capture template type. Yyou can find the documentation for valid type values here, on the same page as the target documentation I linked earlier.

3 More documentation on point-related functions here.