2023-12-03 18:57:36 +00:00
#+title : Org Journaling
#+author : Howard X. Abrams
#+date : 2020-09-24
#+tags : emacs org
2021-11-10 22:24:55 +00:00
A literate programming configuration file for extending the Journaling capabilities.
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp :exports none
2022-03-09 18:45:37 +00:00
;;; org-journaling --- Configuring journals in org. -*- lexical-binding: t; -* -
;;
2023-02-23 17:35:36 +00:00
;; © 2020-2023 Howard X. Abrams
2022-03-09 18:45:37 +00:00
;; This work is licensed under a Creative Commons Attribution 4.0 International License.
;; See http://creativecommons.org/licenses/by/4.0/
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams >
;; Maintainer: Howard X. Abrams
;; Created: September 24, 2020
;;
;; This file is not part of GNU Emacs.
;;
2024-09-28 15:37:22 +00:00
;;; Commentary:
;;
2022-03-09 18:45:37 +00:00
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
2024-10-19 20:34:01 +00:00
;; ~/src/hamacs/org-journaling.org
2022-03-09 18:45:37 +00:00
;; And tangle the file to recreate this one.
;;
;;; Code:
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
* Introduction
2024-09-02 22:27:32 +00:00
I used to use the [[https://github.com/bastibe/org-journal ][org-journal ]] project to create /daily/ journal entries, but what I like is one file per journal entry, and that project did more than I needed. I made my own.
2021-11-10 22:24:55 +00:00
2024-09-02 22:27:32 +00:00
My requirements are simple:
2021-11-10 22:24:55 +00:00
2024-09-02 22:27:32 +00:00
1. A directory of entries, =~/journal= . Each file is based on the date, e.g. =20241228= or =20130401= .
2. A minor mode to identify a journal entry as opposed to a regular org file.
2021-11-10 22:24:55 +00:00
2024-09-02 22:27:32 +00:00
Where do I store these?
2021-11-10 22:24:55 +00:00
2024-09-02 22:27:32 +00:00
#+BEGIN_SRC emacs-lisp
(defvar org-journal-dir (expand-file-name "~/journal")
"Location of Journal entries, org files.")
#+END_SRC
2021-11-10 22:24:55 +00:00
2024-09-02 22:27:32 +00:00
And what identifies a Journal file?
#+BEGIN_SRC emacs-lisp
(defvar org-journal-rx (rx (group (= 4 digit))
(optional "-")
(group (= 2 digit))
(optional "-")
(group (= 2 digit)))
"A regular expression that identifies journal file entries.")
#+END_SRC
And a function to create a new entry:
#+BEGIN_SRC emacs-lisp
(defun org-journal-new-entry ()
"Opens today's journal entry file."
(interactive)
(find-file (expand-file-name (format-time-string "%Y%m%d")
org-journal-dir)))
#+END_SRC
And connect files that /look/ like a Journal entry with =org-mode= :
#+BEGIN_SRC emacs-lisp
(add-to-list 'auto-mode-alist `(,org-journal-rx . org-mode))
#+END_SRC
And pull ‘ er up:
#+BEGIN_SRC emacs-lisp
(ha-leader "f j" '("journal" . org-journal-new-entry))
#+END_SRC
2021-11-10 22:24:55 +00:00
This depends on the following [[file:~/.doom.d/snippets/org-journal-mode/__journal ][snippet/template file ]]:
2024-10-19 20:34:01 +00:00
#+begin_src snippet :tangle ~/src/hamacs/templates/journal
2024-03-07 04:02:25 +00:00
#+title : Journal Entry- `(ha-journal-file-datestamp)`
2021-11-10 22:24:55 +00:00
$0
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
2024-09-02 22:27:32 +00:00
And the code to connect that template to those files:
#+BEGIN_SRC emacs-lisp
2024-09-28 15:37:22 +00:00
(ha-auto-insert-file (rx "journal/" (regexp org-journal-rx)) "journal")
2024-09-02 22:27:32 +00:00
#+END_SRC
2021-11-10 22:24:55 +00:00
Note that when I create a new journal entry, I want a title that should insert a date to match the file's name, not necessarily the current date (see below).
* Journal Filename to Date
Since the Journal's filename represents a date, I should be able to get the "date" associated with a file.
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2024-08-11 04:59:03 +00:00
(defun ha-journal-file-date (&optional datename)
2024-09-02 22:27:32 +00:00
"Return a Lisp date-timestamp from current filename.
If DATENAME is given, return that timestamp."
2024-08-11 04:59:03 +00:00
(unless datename
2024-09-02 22:27:32 +00:00
(setq datename (file-name-base (buffer-file-name))))
(when (string-match org-journal-rx datename)
(let ((day (string-to-number (match-string 3 datename)))
(month (string-to-number (match-string 2 datename)))
(year(string-to-number (match-string 1 datename))))
(encode-time 0 0 0 day month year))))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
2024-09-02 22:27:32 +00:00
And some unit tests to validate this function:
#+begin_src emacs-lisp :tangle no
(ert-deftest ha-journal-file-date-test ()
(should (equal (ha-journal-file-date "20240817") '(26304 19056)))
(should (equal (ha-journal-file-date "2024-08-17") '(26304 19056))))
#+end_src
2021-11-10 22:24:55 +00:00
Using the "date" associated with a file, we can create our standard timestamp:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2024-08-11 04:59:03 +00:00
(defun ha-journal-file-datestamp (&optional datename)
2024-09-02 22:27:32 +00:00
"Return date string of DATENAME if given.
If nil, use the buffer's filename's date."
2024-08-11 04:59:03 +00:00
(format-time-string "%e %b %Y (%A)" (ha-journal-file-date datename)))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
* Journal Capture
Capturing a task (that when uncompleted, would then spillover to following days) could go to the daily journal entry. This requires a special function that opens today's journal, but specifies a non-nil prefix argument in order to inhibit inserting the heading, as =org-capture= will insert the heading.
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2024-08-11 04:59:03 +00:00
(defun org-journal-find-location ()
2024-09-28 15:37:22 +00:00
"Create or load a new journal buffer. Used for `org-capture'."
2024-09-02 22:27:32 +00:00
(org-journal-new-entry)
2024-08-11 04:59:03 +00:00
(goto-char (point-max)))
(defvar org-capture-templates (list))
2024-09-02 22:27:32 +00:00
2024-08-11 04:59:03 +00:00
(add-to-list 'org-capture-templates
'("j" "Journal Task/Entry" plain
(function org-journal-find-location)
"* %?\n\n %i\n\n From: %a"
:empty-lines 1 :jump-to-captured t :immediate-finish t))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
* Next and Previous File
Sometimes it is obvious what is the /next file/ based on the one I'm currently reading. For instance, in my journal entries, the filename is a number that can be incremented. Same with presentation files...
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2024-09-02 22:27:32 +00:00
(defun split-string-with-number (str)
"Return list of three components of string, STR.
The first is the text prior to any numbers, The second is the
embedded number, and the third is the rest of the text in the
string."
(let* ((ms (string-match (rx (one-or-more digit)) str)))
(when ms
(list (substring str 0 ms)
(match-string 0 str)
(substring str
(+ ms
(length (match-string 0 str))))))))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
Which means that the following defines this function:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp :tangle no
2024-08-11 04:59:03 +00:00
(ert-deftest split-string-default-separatorsg-with-number-test ()
(should (equal (split-string-with-number "abc42xyz") '("abc" "42" "xyz")))
(should (equal (split-string-with-number "42xyz") '("" "42" "xyz")))
(should (equal (split-string-with-number "abc42") '("abc" "42" "")))
(should (equal (split-string-with-number "20140424") '("" "20140424" "")))
(should (null (split-string-with-number "abcxyz"))))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
Given this splitter function, we create a function that takes some sort of operator and return a new filename based on the conversion that happens:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2024-08-11 04:59:03 +00:00
(defun find-file-number-change (f)
2024-09-28 15:37:22 +00:00
"Return a filename based on applying F to current buffer.
Where F would be something like `1+' or `1-'."
2024-08-11 04:59:03 +00:00
(let* ((filename (buffer-file-name))
(parts (split-string-with-number
(file-name-base filename)))
(new-name (number-to-string
(funcall f (string-to-number (nth 1 parts))))))
(concat (file-name-directory filename)
(nth 0 parts)
new-name
(nth 2 parts))))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
And this allows us to create two simple functions that can load the "next" and "previous" files:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2024-08-11 04:59:03 +00:00
(defun find-file-increment ()
2024-09-28 15:37:22 +00:00
"Load file that is _one more_ than the file in current buffer.
This requires that the current file contain a number that can be
incremented."
2024-08-11 04:59:03 +00:00
(interactive)
(find-file (find-file-number-change '1+)))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2024-08-11 04:59:03 +00:00
(defun find-file-decrement ()
2024-09-28 15:37:22 +00:00
"Load file that is _one less_ than the file in current buffer.
This requires that the current file contain a number that can be
decremented."
2024-08-11 04:59:03 +00:00
(interactive)
(find-file (find-file-number-change '1-)))
#+end_src
2024-09-02 22:27:32 +00:00
And we could bind those:
#+BEGIN_SRC emacs-lisp
(ha-leader "f +" '("next file" . find-file-increment)
"f -" '("previous file" . find-file-decrement))
#+END_SRC
2024-08-11 04:59:03 +00:00
* Technical Artifacts :noexport:
2021-11-10 22:24:55 +00:00
Let's =provide= a n ame so we can =require= this file.
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp :exports none
2021-11-10 22:24:55 +00:00
(provide 'ha-org-journaling)
;;; ha-org-journaling.el ends here
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-10 22:24:55 +00:00
Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~
2024-03-07 04:02:25 +00:00
#+description : A literate programming configuration file for extending the Journaling capabilities.
2021-11-10 22:24:55 +00:00
2024-03-07 04:02:25 +00:00
#+property : header-args:sh :tangle no
#+property : header-args:emacs-lisp :tangle yes
#+property : header-args :results none :eval no-export :comments no mkdirp yes
2021-11-10 22:24:55 +00:00
2024-03-07 04:02:25 +00:00
#+options : num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+options : skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt : view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js