www.howardism.org
Babblings of an aging geek in love with the Absurd, his family, and his own hubris.... oh, and Lisp.

Journaling with org-mode

Regardless of whether you are into capturing personal data as part of the Quantified Self movement, or simply like to reflect on your day, I thought Emacs and org-mode would be a good approach to journaling.

This essay is almost a historical account of what I did and how I adapted over the few years of org-mode-journaling. While I’m not using every technique described, perhaps you might find the parts that you like?

Single File

I began my journal as a single file, ~/journal.org. Each day, I would add one or more events by using a capture template:1

(setq org-capture-templates
  '(    ;; ... other templates

    ("j" "Journal Entry"
         entry (file+datetree "~/journal.org")
         "* %?"
         :empty-lines 1)

        ;; ... other templates
    ))

Depending on how you bind it (I have mine set to C-c c), I see a menu of locations where I could shoot quick notes (I’ll explain that code later).

Select a capture template
=========================

[n]    General Sprint Note
[m]    Meeting Note
[j]    Journal Entry
[s]    Scrum Status
[t]    Start Sub-task w/Comments
[c]    Close Sub-task
-------------------------------------------
[C]    Customize org-capture-templates
[q]    Abort

Selecting j brings up a narrowed perspective of the my journal file, only showing where I would enter a 4th level heading:

**** ▯

After entering the contents of the heading and the journal entry, I hit C-c C-c to return to my previous buffer.

Assuming I was beginning with an empty file, the entire journal buffer would have a first-level heading for the year, followed by a subheading for month, etc. as in:

* 2014
** 2014-10 October
*** 2014-10-18 Wednesday
**** Pumpkin Patch Trip

     Traveled to Sauvie Island to acquire the required
     holiday decorations, but with constipated traffic, we...

Interconnections

What is a capture template? Clearly the first two strings (the j and the Journal Entry) labels the menu. The list:

(file+datetree "~/journal.org")

Contains the file to load, and specifies the header tree I describe above (if interested, the org-mode documentation lists others). The next string, * %?, is the template, and this is probably the simplest option, as it inserts only an empty header).

An org-mode journal became a way for me to stitch my notes, blogs and other thoughts together; especially useful since my notes spanned work, personal and technical categories. So I soon appended my template with some template expansion goodies:

(setq org-capture-templates '(
    ("j" "Journal Entry"
         entry (file+datetree "~/journal.org")
         "* Event: %?\n\n  %i\n\n  From: %a"
         :empty-lines 1)
))

Assuming I selected the part of my previous paragraph when I opted for a new journal entry, my journal entry would begin with:

* Event: ▯

  An =org-mode= journal became a way for me to stitch my notes,
  blogs and other thoughts together; especially useful since my
  notes spanned work, personal and technical categories.

  From: I̲n̲t̲e̲r̲c̲o̲n̲n̲e̲c̲t̲i̲o̲n̲s̲

Where the selected text is copied into the journal (the %i marker), and a link is created to the file active when I started the entry (because of the %a). And the line, Interconnections is a clickable link to the header within that file.

One Entry Per File

While useful, one large journal file became unwieldy, so I split my monolith into separate files, one per day. I created a directory, ~/journal/, and each filename would have the day’s date.

To create a new journal file, I first created a function to create the file’s name:

(defun get-journal-file-today ()
  "Return filename for today's journal entry."
  (let ((daily-name (format-time-string "%Y%m%d")))
    (expand-file-name (concat org-journal-dir daily-name))))

Then a simple function to load that file:

(defun journal-file-today ()
  "Create and load a journal file based on today's date."
  (interactive)
  (find-file (get-journal-file-today)))

(global-set-key (kbd "C-c f j") 'journal-file-today)

Since the time stamp for the file’s name was YYYYMMDD, I move back and forth between entries with a couple simple functions2, and the org-journal project lets me create the entry by selecting the day in Emacs’ calendar.

These files do not have a .org extension, but we’re talking about Emacs, so I use this code to have org-mode be the default mode for filenames consisting of nothing but numbers:

(add-to-list 'auto-mode-alist '(".*/[0-9]*$" . org-mode))

Snippet

In case I decide to export my journal, I wanted each file to have a title with the date. Easy enough using Yasnippet:

# -*- mode: snippet -*-
# name: Journal Template
# key: journal
# --
#+TITLE: Journal Entry- `(format-time-string "%Y-%m-%d (%A)")`

$0

However, this inserts today’s date, and I really wanted the date associated with the file’s name:

(defun journal-file-insert ()
  "Insert's the journal heading based on the file's name."
  (interactive)
  (when (string-match "\\(20[0-9][0-9]\\)\\([0-9][0-9]\\)\\([0-9][0-9]\\)"
                      (buffer-name))
    (let ((year  (string-to-number (match-string 1 (buffer-name))))
          (month (string-to-number (match-string 2 (buffer-name))))
          (day   (string-to-number (match-string 3 (buffer-name))))
          (datim nil))
      (setq datim (encode-time 0 0 0 day month year))
      (insert (format-time-string
                 "#+TITLE: Journal Entry- %Y-%b-%d (%A)\n\n" datim)))))

Also, I really wanted to have this information inserted automatically without having to trigger the snippet…yeah, we’re talking auto-insert.

(add-hook 'find-file-hook 'auto-insert)
(add-to-list 'auto-insert-alist '(".*/[0-9]*$" . journal-file-insert))

Any way I start a new journal entry, I now have a default header template.

Interconnecting the Multi-File Journal

While pulling up Emacs’ calendar or calling my journal-file-today function is good when I’m writing the fun I had on the weekend, I still want to interconnect my other org-mode files with the org-capture-template, by calling my get-journal-file-today function I already wrote:

(setq org-capture-templates '(
    ;; ...
    ("j" "Journal Note"
         entry (file (get-journal-file-today))
         "* Event: %?\n\n  %i\n\n  From: %a"
         :empty-lines 1)
    ;; ..
))

Now when I am making notes in a blog entry, I can easily write about it in my journal.

Footnotes:

1

A capture template allows you to quickly insert a “section” to an org-mode file. This requires at least a new header to be inserted, so you need a string that begins with an asterisks and a space: “* "

The rest is gravy.

2

The find-file-increment (bound to C-c f +) will load a file that is one more than the value in the current file, and since that is a journal entry of the date, it would load the next day’s journal entry.

I also have the find-file-decrement (bound to C-c f -) to go back a day.