Skip to content

Instantly share code, notes, and snippets.

@redblobgames
Last active November 19, 2022 17:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save redblobgames/ca9d0808f13a42f9c6e64c913e2a5894 to your computer and use it in GitHub Desktop.
Save redblobgames/ca9d0808f13a42f9c6e64c913e2a5894 to your computer and use it in GitHub Desktop.
my journal code
;;; my-diary.el ---
;;; Commentary:
;; My daily diary, inspired by org-journal, but customized for my needs.
;;
;; My keybindings: H-j to open the diary file (global binding), then
;; H-j to add an entry (local binding). C-c C-b, C-c C-f to move days
;; (like org-journal). H-i to search previous headings for completion,
;; so that I can reuse headings and then analyze them.
;;; Code:
(require 'org)
;;; Configuration variables
(defvar my-diary-directory "~/Documents/journal/")
(defvar my-diary-filename
(expand-file-name (concat my-diary-directory "%Y-%m-%d.org")))
(defvar my-diary-filename-regexp
(concat "\\`"
(s-replace "%Y-%m-%d" "\\(....-..-..\\)"
(regexp-quote my-diary-filename))
"\\'"))
(add-to-list 'auto-mode-alist (cons my-diary-filename-regexp 'my-diary-mode))
;;; Helper functions
(defun my-diary-filename-to-date (filename)
(let ((str (cadr (s-match my-diary-filename-regexp (expand-file-name filename)))))
(when str
(let* ((time (parse-time-string str))
(date (list (nth 4 time) (nth 3 time) (nth 5 time))))
date))))
(defun my-diary-calendar-day-to-time (calendar-day)
(encode-time 0 0 0 (nth 1 calendar-day) (nth 0 calendar-day) (nth 2 calendar-day)))
(defun my-diary-change-day (dir)
"Construct filename 'dir' days from current filename"
(let* ((day (calendar-absolute-from-gregorian (my-diary-filename-to-date (buffer-file-name))))
(newday (calendar-gregorian-from-absolute (+ day dir))))
(format-time-string
my-diary-filename
(my-diary-calendar-day-to-time newday))))
;;; Commands
(defun my-diary-today ()
"Open today's diary file"
(interactive)
(find-file (format-time-string my-diary-filename))
(when (equal (point-max) 1)
(let ((calendar-date (my-diary-filename-to-date (buffer-file-name))))
(insert (format-time-string
(read-file-into-string
(concat my-diary-directory "/diary-template.org"))
(my-diary-calendar-day-to-time calendar-date)))))
(goto-char (point-max)))
(defun my-diary-new-entry ()
"Make a new entry, rounded to the nearest 5 minutes"
(interactive)
(my-diary-today)
(insert (format-time-string "** %H:%M " (org-current-time 5))))
(defun my-diary-search-headings ()
"Search through previous headings for completion"
(interactive)
(let ((headings (split-string
(shell-command-to-string
(concat "rg --no-filename '^[*][*] \\d\\d:\\d\\d (.*)$' --replace '$1' " my-diary-directory " | sort | uniq -c | sort -n -r | perl -pe 's/^ *\\d* *//'"))
"\n")))
(insert (concat (completing-read "Headings: " headings) "\n"))))
(defun my-diary-next-day ()
"Move to the next day's diary file"
(interactive)
(find-file (my-diary-change-day 1)))
(defun my-diary-prev-day ()
"Move to the previous day's diary file"
(interactive)
(find-file (my-diary-change-day -1)))
(define-derived-mode my-diary-mode org-mode "Journal"
"Mode for writing diary entries"
(face-remap-add-relative 'default :family "Iosevka Aile"
:weight 'medium :height 1.1)
(turn-on-visual-line-mode)
(run-mode-hooks))
(provide 'my-diary)
;;; my-diary.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment