Simplified my org journaling code

This commit is contained in:
Howard Abrams 2024-09-02 15:27:32 -07:00
parent 2317cc5d37
commit f2287c59a0

View file

@ -25,33 +25,50 @@ A literate programming configuration file for extending the Journaling capabilit
;;; Code: ;;; Code:
#+end_src #+end_src
* Introduction * Introduction
Using the [[https://github.com/bastibe/org-journal][org-journal]] project to easily create /daily/ journal entries: 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.
#+begin_src emacs-lisp My requirements are simple:
(use-package org-journal
:after org
:config
(setq org-journal-dir "~/journal"
org-journal-date-format " "
org-journal-time-format ""
org-journal-file-type 'daily
org-journal-file-format "%Y%m%d")
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.
Where do I store these?
#+BEGIN_SRC emacs-lisp
(defvar org-journal-dir (expand-file-name "~/journal")
"Location of Journal entries, org files.")
#+END_SRC
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)) (ha-leader "f j" '("journal" . org-journal-new-entry))
#+END_SRC
;; In normal Org file, I like large headers, but in my Journal,
;; where each task is a header, I want them smaller:
(add-hook 'org-journal-mode-hook
(lambda ()
(set-face-attribute 'org-level-1 nil :height 1.2)
(set-face-attribute 'org-level-2 nil :height 1.1)
(set-face-attribute 'org-level-3 nil :height 1.0)))
;; But new files could use /my formatting/ (which is different
;; than the options available in the project):
(ha-auto-insert-file (rx "journal/" (zero-or-more any) (= 8 digit)) "journal"))
#+end_src
This depends on the following [[file:~/.doom.d/snippets/org-journal-mode/__journal][snippet/template file]]: This depends on the following [[file:~/.doom.d/snippets/org-journal-mode/__journal][snippet/template file]]:
@ -59,48 +76,57 @@ This depends on the following [[file:~/.doom.d/snippets/org-journal-mode/__journ
#+title: Journal Entry- `(ha-journal-file-datestamp)` #+title: Journal Entry- `(ha-journal-file-datestamp)`
$0 $0
#+end_src #+end_src
And the code to connect that template to those files:
#+BEGIN_SRC emacs-lisp
(ha-auto-insert-file (rx "journal/" (regexp org-journal-rx)) "journal"))
#+END_SRC
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). 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 * Journal Filename to Date
Since the Journal's filename represents a date, I should be able to get the "date" associated with a file. Since the Journal's filename represents a date, I should be able to get the "date" associated with a file.
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun ha-journal-file-date (&optional datename) (defun ha-journal-file-date (&optional datename)
"Returns a Lisp date-timestamp based on the format of the current filename, "Return a Lisp date-timestamp from current filename.
or DATENAME if given." If DATENAME is given, return that timestamp."
(unless datename (unless datename
(setq datename (buffer-file-name))) (setq datename (file-name-base (buffer-file-name))))
(let* ((datename-parser (rx (group (= 4 digit)) (when (string-match org-journal-rx datename)
(group (= 2 digit)) (let ((day (string-to-number (match-string 3 datename)))
(group (= 2 digit))))
(parsed-datename (string-match datename-parser datename))
(day (string-to-number (match-string 3 datename)))
(month (string-to-number (match-string 2 datename))) (month (string-to-number (match-string 2 datename)))
(year(string-to-number (match-string 1 datename)))) (year(string-to-number (match-string 1 datename))))
(encode-time 0 0 0 day month year))) (encode-time 0 0 0 day month year))))
#+end_src #+end_src
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
Using the "date" associated with a file, we can create our standard timestamp: Using the "date" associated with a file, we can create our standard timestamp:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun ha-journal-file-datestamp (&optional datename) (defun ha-journal-file-datestamp (&optional datename)
"Return a string of the buffer's date (based on the file's name)." "Return date string of DATENAME if given.
If nil, use the buffer's filename's date."
(format-time-string "%e %b %Y (%A)" (ha-journal-file-date datename))) (format-time-string "%e %b %Y (%A)" (ha-journal-file-date datename)))
#+end_src #+end_src
* Journal Capture * 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. 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.
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun org-journal-find-location () (defun org-journal-find-location ()
(org-journal-new-entry t) (org-journal-new-entry)
(org-narrow-to-subtree)
(goto-char (point-max))) (goto-char (point-max)))
(defvar org-capture-templates (list)) (defvar org-capture-templates (list))
(add-to-list 'org-capture-templates (add-to-list 'org-capture-templates
'("j" "Journal Task/Entry" plain '("j" "Journal Task/Entry" plain
(function org-journal-find-location) (function org-journal-find-location)
@ -111,16 +137,18 @@ Capturing a task (that when uncompleted, would then spillover to following days)
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... 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...
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun split-string-with-number (string) (defun split-string-with-number (str)
"Returns a list of three components of the string, the first is "Return list of three components of string, STR.
the text prior to any numbers, the second is the embedded number, The first is the text prior to any numbers, The second is the
and the third is the rest of the text in the string." embedded number, and the third is the rest of the text in the
(let* ((start (string-match "[0-9]+" string)) string."
(end (string-match "[^0-9]+" string start))) (let* ((ms (string-match (rx (one-or-more digit)) str)))
(if start (when ms
(list (substring string 0 start) (list (substring str 0 ms)
(substring string start end) (match-string 0 str)
(if end (substring string end) ""))))) (substring str
(+ ms
(length (match-string 0 str))))))))
#+end_src #+end_src
Which means that the following defines this function: Which means that the following defines this function:
@ -170,6 +198,14 @@ And this allows us to create two simple functions that can load the "next" and "
(interactive) (interactive)
(find-file (find-file-number-change '1-))) (find-file (find-file-number-change '1-)))
#+end_src #+end_src
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
* Technical Artifacts :noexport: * Technical Artifacts :noexport:
Let's =provide= a name so we can =require= this file. Let's =provide= a name so we can =require= this file.