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?
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
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
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...
What is a capture template? Clearly the first two strings (the
j and the
Journal Entry) labels the menu. The list:
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).
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
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
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))
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
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
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.
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.
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
I also have the
find-file-decrement (bound to
C-c f -) to go back