Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
org-mode file+function capture template example
;; I am not a regular emacs user and haven't played with lisp for many years.
;; I struggled for a couple of days trying to tweak org-capture-templates to
;; get my desired behavior for journal entries. I hope this helps someone!
;; I have been following the excellent guide
;; The "journal" template was the one I wanted to tweak. I use a date-based
;; journal filename, e.g. "". The contents of the
;; file consist of a top-level headline with a human-friendly date, followed
;; by second-level headline with the time and brief journal note. For example:
;; * Tuesday, 22 September, 2015 :tag1:tag2:
;; ** 03:39:39 PM EDT - Published my journal capture template to github
;; I first tried:
;; ("j" "Journal" entry (file+headline (format-time-string *my-journal-file-format*) (format-time-string *my-journal-headline-format**)
;; "** %<%H:%M:%S %p %Z> - %?\n" :clock-in t :clock-resume t)
;; But org complained that the headline wasn't a stringp... So I thought that
;; file+function might work better for me. The org-mode docs don't explain what
;; "function" should do (does it take args? should there be a return value?).
;; Searching around online I did not find many examples which were useful. I
;; looked at the source for the functionality that file+headline and file+datetree
;; use and eventually came up with the following.
(require 'org)
(defconst *my-org-dir* "~/Documents/personal-org")
(defconst *my-journal-file-format* (concat *my-org-dir* "/journal/%Y/%m/%d/"))
(defconst *my-journal-headline-format* "%A, %d %B, %Y :tag1:tag2:")
;; Code based mostly on file+headline part of org-capture-set-target-location
;; Look for a headline that matches whatever *my-journal-headline-format* is
;; If it's not there insert it; otherwise position the cursor at the end of the
;; subtree.
(defun jww/find-journal-tree ()
"Find or create my default journal tree"
(setq hd (format-time-string *my-journal-headline-format*))
(goto-char (point-min))
(unless (derived-mode-p 'org-mode)
"Target buffer \"%s\" for jww/find-journal-tree should be in Org mode"
(if (re-search-forward
(format org-complex-heading-regexp-format (regexp-quote hd))
nil t)
(goto-char (point-at-bol))
(goto-char (point-min))
(or (bolp) (insert "\n"))
(insert "* " hd "\n")
(beginning-of-line 0))
;; note the use of "plain" instead of "entry"; using "entry" made this a top-level
;; headline regardless of how many * I put in the template string (wtf?).
(setq org-capture-templates
(quote (("j" "Journal" plain (file+function (format-time-string *my-journal-file-format*) jww/find-journal-tree)
"** %<%H:%M:%S %p %Z> - %?\n" :clock-in t :clock-resume t))))
(provide 'init-org)
Copy link

Thanks, I inspired from this to write my own!

Copy link

evanlh commented Apr 7, 2022

Thanks! The "entry" top-level headline behavior was tripping me up, your comment helped immensely!

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