changelog shortlog graph tags branches files raw help

Mercurial > core / changeset: emacs org libraries and upgrades

changeset 604: 74a55d5decce
parent 603: 6e5be24bf789
child 605: 3734c596d103
author: Richard Westhaver <ellis@rwest.io>
date: Sat, 17 Aug 2024 23:42:08 -0400
files: emacs/default.el emacs/lib/inbox.el emacs/lib/inbox/api.el emacs/lib/inbox/config.el emacs/lib/org-expiry.el emacs/lib/publish.el emacs/lib/ulang.el emacs/lib/uml-mode.el lisp/lib/dat/dat.asd lisp/lib/dat/handlebars.lisp lisp/lib/dat/pkg.lisp readme.org
description: emacs org libraries and upgrades
     1.1--- a/emacs/default.el	Fri Aug 16 21:27:00 2024 -0400
     1.2+++ b/emacs/default.el	Sat Aug 17 23:42:08 2024 -0400
     1.3@@ -69,6 +69,7 @@
     1.4 
     1.5 (defvar default-theme 'leuven-dark)
     1.6 (defvar company-source-directory (join-paths user-home-directory "comp"))
     1.7+(defvar company-org-directory (join-paths company-source-directory "org"))
     1.8 (defvar company-domain "compiler.company")
     1.9 (defvar company-name "The Compiler Company, LLC")
    1.10 (defvar company-vc-domain "vc.compiler.company")
    1.11@@ -747,7 +748,7 @@
    1.12 (setq org-id-link-to-org-use-id t)
    1.13 ;; capture templates
    1.14 (setq org-capture-templates
    1.15-      '(("t" "task" entry (file "inbox.org") "* %^{title}\n- %?" :prepend t)
    1.16+      '(("t" "task" entry (file "core.org") "* %^{title}\n- %?" :prepend t)
    1.17         ("1" "current-task-item" item (clock) "%i%?")
    1.18         ("2" "current-task-checkbox" checkitem (clock) "%i%?")
    1.19         ("3" "current-task-region" plain (clock) "%i" :immediate-finish t :empty-lines 1)
    1.20@@ -758,6 +759,10 @@
    1.21         ("i" "idea" entry (file "inbox.org") "* OUTLINE %?\n:notes:\n:end:\n- _outline_ [/]\n  - [ ] \n  - [ ] \n- _refs_" :prepend t)
    1.22         ("b" "bug" entry (file "inbox.org") "* FIX %?\n- _review_\n- _fix_\n- _test_" :prepend t)
    1.23         ("r" "research" entry (file "inbox.org") "* RESEARCH %?\n:notes:\n:end:\n- _refs_" :prepend t)))
    1.24+
    1.25+(setq org-default-notes-file (join-paths org-directory "inbox.org")
    1.26+      org-capture-use-agenda-date t)
    1.27+
    1.28 (setq org-html-htmlize-output-type 'css
    1.29       org-html-head-include-default-style nil
    1.30       ;; cc default
    1.31@@ -786,21 +791,24 @@
    1.32 
    1.33         org-refile-targets '((nil :maxlevel . 3)
    1.34                              (org-agenda-files :maxlevel . 3))
    1.35-        org-agenda-files (list "inbox.org")
    1.36+        ;; org-agenda-files (list "inbox.org")
    1.37+        org-agenda-include-diary t
    1.38+        org-agenda-include-inactive-timestamps t
    1.39         org-confirm-babel-evaluate nil
    1.40         org-src-fontify-natively t
    1.41         org-src-tabs-act-natively t
    1.42         org-footnote-section nil
    1.43         org-log-into-drawer t
    1.44+        org-log-refile 'time
    1.45+        org-log-redeadline 'time
    1.46         org-log-states-order-reversed nil
    1.47         org-clock-persist 'history)
    1.48 
    1.49-(setq org-stuck-projects '("+PROJECT/-DONE" ("NEXT") nil ""))
    1.50-
    1.51 (add-hook 'after-init-hook #'org-clock-persistence-insinuate)
    1.52 
    1.53 ;; archive
    1.54 (setq org-archive-location "archive.org::")
    1.55+
    1.56 (defun extract-org-directory-titles-as-list (&optional dir)
    1.57   (interactive "D")
    1.58   (print
    1.59@@ -978,6 +986,9 @@
    1.60        t nil))))
    1.61 
    1.62 ;;;; Agenda
    1.63+(require 'org-agenda)
    1.64+(cl-pushnew '("w" "Work in progress tasks" ((todo "WIP") (agenda))) org-agenda-custom-commands)
    1.65+
    1.66 (defvar org-agenda-overriding-header)
    1.67 (defvar org-agenda-sorting-strategy)
    1.68 (defvar org-agenda-restrict)
    1.69@@ -1080,17 +1091,14 @@
    1.70                                        :html translation-html
    1.71                                        :utf-8 translation-utf-8)))))))
    1.72 
    1.73-;;; Glossary
    1.74-(use-package org-glossary
    1.75-  :vc (:url "https://github.com/tecosaur/org-glossary.git" :branch "master")
    1.76-  :after org)
    1.77+;;; Dictionary
    1.78+(setq dictionary-server "compiler.company"
    1.79+      switch-to-buffer-obey-display-actions t)
    1.80 
    1.81-;;; Dictionary
    1.82-(setq switch-to-buffer-obey-display-actions t)
    1.83-(add-to-list 'display-buffer-alist
    1.84-   '("^\\*Dictionary\\*" display-buffer-in-side-window
    1.85-     (side . right)))
    1.86-
    1.87+;;; Ispell
    1.88+;; requires aspell and a hunspell dictionary (hunspell-en_us)
    1.89+(setq-default ispell-program-name "aspell")
    1.90+(add-hook 'mail-send-hook  #'ispell-message)
    1.91 
    1.92 ;;; Skel
    1.93 (add-to-load-path user-emacs-lib-directory)
     2.1--- a/emacs/lib/inbox.el	Fri Aug 16 21:27:00 2024 -0400
     2.2+++ b/emacs/lib/inbox.el	Sat Aug 17 23:42:08 2024 -0400
     2.3@@ -19,8 +19,8 @@
     2.4 
     2.5 ;;; Commentary:
     2.6 
     2.7-;; This is The Compiler Company inbox system. The main interface is
     2.8-;; the inbox.org file which manages personal tasks.
     2.9+;; This is the elisp interface to the CC Inbox system. The main
    2.10+;; interface is the inbox.org file which manages personal tasks.
    2.11 
    2.12 ;; Users may use `org-capture' to insert tasks and notes into their
    2.13 ;; own `org-inbox-file' and refactor them to a more sensible
    2.14@@ -28,10 +28,16 @@
    2.15 
    2.16 ;;; Code:
    2.17 (require 'org)
    2.18+(require 'org-agenda)
    2.19 (require 'default)
    2.20+(require 'uml-mode)
    2.21+(require 'eieio)
    2.22+(require 'org-expiry)
    2.23+
    2.24 (defgroup inbox nil
    2.25-  "RW Inbox")
    2.26+  "CC Inbox")
    2.27 
    2.28+;;; Vars
    2.29 (defcustom org-inbox-file
    2.30   (concat (file-name-as-directory org-directory) "inbox.org")
    2.31   "Custom inbox file location."
    2.32@@ -44,6 +50,15 @@
    2.33   :type 'string
    2.34   :group 'inbox)
    2.35 
    2.36+(defvar org-inbox-buffer-name "*Inbox*"
    2.37+  "The name of the org-inbox buffer.")
    2.38+
    2.39+(defvar org-inbox-properties
    2.40+  '("NEXT" "PREV" "FROM" "TO" "OWNER" "PROJECT" "BLOCKER"))
    2.41+
    2.42+(defvar org-inbox-db-schema
    2.43+  '(id file node edge contents properties schedule))
    2.44+;;; Utils
    2.45 ;; `org-archive-all-done' doesn't work the way we want. This function
    2.46 ;; will archive all done tasks in the current subtree, or the whole file
    2.47 ;; if prefix arg is given.
    2.48@@ -200,5 +215,19 @@
    2.49   (interactive)
    2.50   (org-sort-entries nil ?f #'org-sort-todo-priority #'org-sort-compare-todo-priority))
    2.51 
    2.52+(defun org-inbox-open ()
    2.53+  "Open `org-inbox-file' or switch to its buffer if already open."
    2.54+  (interactive)
    2.55+  (if-let ((inbox (get-buffer org-inbox-buffer-name)))
    2.56+      (switch-to-buffer inbox)
    2.57+    (find-file org-inbox-file)
    2.58+    (rename-buffer org-inbox-buffer-name)))
    2.59+
    2.60+(defun org-inbox-close ()
    2.61+  "Close the org-inbox and associated buffers."
    2.62+  (interactive)
    2.63+  (when-let ((inbox (get-buffer org-inbox-buffer-name)))
    2.64+    (kill-buffer inbox)))
    2.65+
    2.66 (provide 'inbox)
    2.67 ;; inbox.el ends here
     5.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2+++ b/emacs/lib/org-expiry.el	Sat Aug 17 23:42:08 2024 -0400
     5.3@@ -0,0 +1,370 @@
     5.4+;;; org-expiry.el --- expiry mechanism for Org entries  -*- lexical-binding: t; -*-
     5.5+;;
     5.6+;; Copyright 2007-2021 Free Software Foundation, Inc.
     5.7+;;
     5.8+;; Author: Bastien Guerry <bzg@gnu.org>
     5.9+;; Version: 0.2
    5.10+;; Keywords: org, expiry
    5.11+;; Homepage: https://git.sr.ht/~bzg/org-contrib
    5.12+
    5.13+;; This file is not part of GNU Emacs.
    5.14+
    5.15+;; This program is free software; you can redistribute it and/or modify
    5.16+;; it under the terms of the GNU General Public License as published by
    5.17+;; the Free Software Foundation; either version 3, or (at your option)
    5.18+;; any later version.
    5.19+;;
    5.20+;; This program is distributed in the hope that it will be useful,
    5.21+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.22+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.23+;; GNU General Public License for more details.
    5.24+;;
    5.25+;; You should have received a copy of the GNU General Public License
    5.26+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
    5.27+;;
    5.28+;;; Commentary:
    5.29+;;
    5.30+;; This gives you a chance to get rid of old entries in your Org files
    5.31+;; by expiring them.
    5.32+;;
    5.33+;; By default, entries that have no EXPIRY property are considered to be
    5.34+;; new (i.e. 0 day old) and only entries older than one year go to the
    5.35+;; expiry process, which consist in adding the ARCHIVE tag.  None of
    5.36+;; your tasks will be deleted with the default settings.
    5.37+;;
    5.38+;; When does an entry expires?
    5.39+;;
    5.40+;; Consider this entry:
    5.41+;;
    5.42+;; * Stop watching TV
    5.43+;;   :PROPERTIES:
    5.44+;;   :CREATED:  <2008-01-07 lun 08:01>
    5.45+;;   :EXPIRY:   <2008-01-09 08:01>
    5.46+;;   :END:
    5.47+;;
    5.48+;; This entry will expire on the 9th, january 2008.
    5.49+
    5.50+;; * Stop watching TV
    5.51+;;   :PROPERTIES:
    5.52+;;   :CREATED:  <2008-01-07 lun 08:01>
    5.53+;;   :EXPIRY:   +1w
    5.54+;;   :END:
    5.55+;;
    5.56+;; This entry will expire on the 14th, january 2008, one week after its
    5.57+;; creation date.
    5.58+;;
    5.59+;; What happen when an entry is expired?  Nothing until you explicitly
    5.60+;; M-x org-expiry-process-entries When doing this, org-expiry will check
    5.61+;; for expired entries and request permission to process them.
    5.62+;;
    5.63+;; Processing an expired entries means calling the function associated
    5.64+;; with `org-expiry-handler-function'; the default is to add the tag
    5.65+;; :ARCHIVE:, but you can also add a EXPIRED keyword or even archive
    5.66+;; the subtree.
    5.67+;;
    5.68+;; Is this useful?  Well, when you're in a brainstorming session, it
    5.69+;; might be useful to know about the creation date of an entry, and be
    5.70+;; able to archive those entries that are more than xxx days/weeks old.
    5.71+;;
    5.72+;; When you're in such a session, you can insinuate org-expiry like
    5.73+;; this: M-x org-expiry-insinuate
    5.74+;;
    5.75+;; Then, each time you're pressing M-RET to insert an item, the CREATION
    5.76+;; property will be automatically added.  Same when you're scheduling or
    5.77+;; deadlining items.  You can deinsinuate: M-x org-expiry-deinsinuate
    5.78+
    5.79+;;; Code:
    5.80+
    5.81+(require 'org)
    5.82+
    5.83+;;; User variables:
    5.84+
    5.85+(defgroup org-expiry nil
    5.86+  "Org expiry process."
    5.87+  :tag "Org Expiry"
    5.88+  :group 'org)
    5.89+
    5.90+(defcustom org-expiry-inactive-timestamps nil
    5.91+  "Insert inactive timestamps for created/expired properties."
    5.92+  :type 'boolean
    5.93+  :group 'org-expiry)
    5.94+
    5.95+(defcustom org-expiry-created-property-name "CREATED"
    5.96+  "The name of the property for setting the creation date."
    5.97+  :type 'string
    5.98+  :group 'org-expiry)
    5.99+
   5.100+(defcustom org-expiry-expiry-property-name "EXPIRY"
   5.101+  "The name of the property for setting the expiry date/delay."
   5.102+  :type 'string
   5.103+  :group 'org-expiry)
   5.104+
   5.105+(defcustom org-expiry-keyword "EXPIRED"
   5.106+  "The default keyword for `org-expiry-add-keyword'."
   5.107+  :type 'string
   5.108+  :group 'org-expiry)
   5.109+
   5.110+(defcustom org-expiry-wait "+1y"
   5.111+  "Time span between the creation date and the expiry.
   5.112+The default value for this variable (\"+1y\") means that entries
   5.113+will expire if there are at least one year old.
   5.114+
   5.115+If the expiry delay cannot be retrieved from the entry or the
   5.116+subtree above, the expiry process compares the expiry delay with
   5.117+`org-expiry-wait'.  This can be either an ISO date or a relative
   5.118+time specification.  See `org-read-date' for details."
   5.119+  :type 'string
   5.120+  :group 'org-expiry)
   5.121+
   5.122+(defcustom org-expiry-created-date "+0d"
   5.123+  "The default creation date.
   5.124+The default value of this variable (\"+0d\") means that entries
   5.125+without a creation date will be handled as if they were created
   5.126+today.
   5.127+
   5.128+If the creation date cannot be retrieved from the entry or the
   5.129+subtree above, the expiry process will compare the expiry delay
   5.130+with this date.  This can be either an ISO date or a relative
   5.131+time specification.  See `org-read-date' for details on relative
   5.132+time specifications."
   5.133+  :type 'string
   5.134+  :group 'org-expiry)
   5.135+
   5.136+(defcustom org-expiry-handler-function 'org-toggle-archive-tag
   5.137+  "Function to process expired entries.
   5.138+Possible candidates for this function are:
   5.139+
   5.140+`org-toggle-archive-tag'
   5.141+`org-expiry-add-keyword'
   5.142+`org-expiry-archive-subtree'"
   5.143+  :type 'function
   5.144+  :group 'org-expiry)
   5.145+
   5.146+(defcustom org-expiry-confirm-flag t
   5.147+  "Non-nil means confirm expiration process."
   5.148+  :type '(choice
   5.149+	  (const :tag "Always require confirmation" t)
   5.150+	  (const :tag "Do not require confirmation" nil)
   5.151+	  (const :tag "Require confirmation in interactive expiry process"
   5.152+		 interactive))
   5.153+  :group 'org-expiry)
   5.154+
   5.155+(defcustom org-expiry-advised-functions
   5.156+  '(org-scheduled org-deadline org-time-stamp)
   5.157+  "A list of advised functions.
   5.158+`org-expiry-insinuate' will activate the expiry advice for these
   5.159+functions.  `org-expiry-deinsinuate' will deactivate them."
   5.160+  :type 'boolean
   5.161+  :group 'list)
   5.162+
   5.163+;;; Advices and insinuation:
   5.164+
   5.165+(define-advice org-schedule (:after (&rest _) org-schedule-update-created)
   5.166+  "Update the creation-date property when calling `org-schedule'."
   5.167+  (org-expiry-insert-created))
   5.168+
   5.169+(define-advice org-deadline (:after (&rest _) org-deadline-update-created)
   5.170+  "Update the creation-date property when calling `org-deadline'."
   5.171+  (org-expiry-insert-created))
   5.172+
   5.173+(define-advice org-time-stamp (:after (&rest _) org-time-stamp-update-created)
   5.174+  "Update the creation-date property when calling `org-time-stamp'."
   5.175+  (org-expiry-insert-created))
   5.176+
   5.177+(defun org-expiry-insinuate (&optional arg)
   5.178+  "Add hooks and activate advices for org-expiry.
   5.179+If ARG, also add a hook to `before-save-hook' in `org-mode' and
   5.180+restart `org-mode' if necessary."
   5.181+  (interactive "P")
   5.182+  (ad-activate 'org-schedule)
   5.183+  (ad-activate 'org-time-stamp)
   5.184+  (ad-activate 'org-deadline)
   5.185+  (add-hook 'org-insert-heading-hook 'org-expiry-insert-created)
   5.186+  (add-hook 'org-after-todo-state-change-hook 'org-expiry-insert-created)
   5.187+  (add-hook 'org-after-tags-change-hook 'org-expiry-insert-created)
   5.188+  (when arg
   5.189+    (add-hook 'org-mode-hook
   5.190+	      (lambda() (add-hook 'before-save-hook
   5.191+				  'org-expiry-process-entries t t)))
   5.192+    ;; need this to refresh org-mode hooks
   5.193+    (when (eq major-mode 'org-mode)
   5.194+      (org-mode)
   5.195+      (if (called-interactively-p 'any)
   5.196+	  (message "Org-expiry insinuated, `org-mode' restarted.")))))
   5.197+
   5.198+(defun org-expiry-deinsinuate (&optional arg)
   5.199+  "Remove hooks and deactivate advices for org-expiry.
   5.200+If ARG, also remove org-expiry hook in Org's `before-save-hook'
   5.201+and restart `org-mode' if necessary."
   5.202+  (interactive "P")
   5.203+  (advice-remove 'org-schedule #'org-schedule@org-schedule-update-created)
   5.204+  (advice-remove 'org-time-stamp #'org-time-stamp@org-time-stamp-update-created)
   5.205+  (advice-remove 'org-deadline #'org-deadline@org-deadline-update-created)
   5.206+  (remove-hook 'org-insert-heading-hook 'org-expiry-insert-created)
   5.207+  (remove-hook 'org-after-todo-state-change-hook 'org-expiry-insert-created)
   5.208+  (remove-hook 'org-after-tags-change-hook 'org-expiry-insert-created)
   5.209+  (remove-hook 'org-mode-hook
   5.210+	       (lambda() (add-hook 'before-save-hook
   5.211+				   'org-expiry-process-entries t t)))
   5.212+  (when arg
   5.213+    ;; need this to refresh org-mode hooks
   5.214+    (when (eq major-mode 'org-mode)
   5.215+      (org-mode)
   5.216+      (if (called-interactively-p 'any)
   5.217+	  (message "Org-expiry de-insinuated, `org-mode' restarted.")))))
   5.218+
   5.219+;;; org-expiry-expired-p:
   5.220+
   5.221+(defun org-expiry-expired-p ()
   5.222+  "Check if the entry at point is expired.
   5.223+Return nil if the entry is not expired.  Otherwise return the
   5.224+amount of time between today and the expiry date.
   5.225+
   5.226+If there is no creation date, use `org-expiry-created-date'.
   5.227+If there is no expiry date, use `org-expiry-wait'."
   5.228+  (let* ((ex-prop org-expiry-expiry-property-name)
   5.229+	 (cr-prop org-expiry-created-property-name)
   5.230+	 (ct (current-time))
   5.231+	 (cr (org-read-date nil t (or (org-entry-get (point) cr-prop t)
   5.232+				      org-expiry-created-date)))
   5.233+	 (ex-field (or (org-entry-get (point) ex-prop t) org-expiry-wait))
   5.234+	 (ex (if (string-match "^[ \t]?[+-]" ex-field)
   5.235+		 (time-add cr (time-subtract (org-read-date nil t ex-field) ct))
   5.236+	       (org-read-date nil t ex-field))))
   5.237+    (if (time-less-p ex ct)
   5.238+	(time-subtract ct ex))))
   5.239+
   5.240+;;; Expire an entry or a region/buffer:
   5.241+
   5.242+(defun org-expiry-process-entry (&optional force)
   5.243+  "Call `org-expiry-handler-function' on entry.
   5.244+If FORCE is non-nil, don't require confirmation from the user.
   5.245+Otherwise rely on `org-expiry-confirm-flag' to decide."
   5.246+  (interactive "P")
   5.247+  (save-excursion
   5.248+    (when (called-interactively-p 'interactive) (org-reveal))
   5.249+    (when (org-expiry-expired-p)
   5.250+      (org-back-to-heading)
   5.251+      (looking-at org-complex-heading-regexp)
   5.252+      (let* ((ov (make-overlay (point) (match-end 0)))
   5.253+	     (e (org-expiry-expired-p))
   5.254+	     (d (time-to-number-of-days e)))
   5.255+	(overlay-put ov 'face 'secondary-selection)
   5.256+	(if (or force
   5.257+		(null org-expiry-confirm-flag)
   5.258+		(and (eq org-expiry-confirm-flag 'interactive)
   5.259+		     (not (called-interactively-p 'interactive)))
   5.260+		(and org-expiry-confirm-flag
   5.261+		     (y-or-n-p (format "Entry expired by %d days.  Process? " d))))
   5.262+	    (funcall org-expiry-handler-function))
   5.263+	(delete-overlay ov)))))
   5.264+
   5.265+(defun org-expiry-process-entries (_ _)
   5.266+  "Process all expired entries between BEG and END.
   5.267+The expiry process will run the function defined by
   5.268+`org-expiry-handler-functions'."
   5.269+  (interactive "r")
   5.270+  (save-excursion
   5.271+    (let ((beg (if (org-region-active-p)
   5.272+		   (region-beginning) (point-min)))
   5.273+	  (end (if (org-region-active-p)
   5.274+		   (region-end) (point-max))))
   5.275+      (goto-char beg)
   5.276+      (let ((expired 0) (processed 0))
   5.277+	(while (and (outline-next-heading) (< (point) end))
   5.278+	  (when (org-expiry-expired-p)
   5.279+	    (setq expired (1+ expired))
   5.280+	    (if (if (called-interactively-p 'any)
   5.281+		    (call-interactively 'org-expiry-process-entry)
   5.282+		  (org-expiry-process-entry))
   5.283+		(setq processed (1+ processed)))))
   5.284+	(if (equal expired 0)
   5.285+	    (message "No expired entry")
   5.286+	  (message "Processed %d on %d expired entries"
   5.287+		   processed expired))))))
   5.288+
   5.289+;;; Insert created/expiry property:
   5.290+(defun org-expiry-format-timestamp (timestr inactive)
   5.291+  "Properly format TIMESTR into an org (in)active timestamp"
   5.292+  (format (if inactive "[%s]" "<%s>") timestr))
   5.293+
   5.294+(defun org-expiry-insert-created (&optional arg)
   5.295+  "Insert or update a property with the creation date.
   5.296+If ARG, always update it.  With one `C-u' prefix, silently update
   5.297+to today's date.  With two `C-u' prefixes, prompt the user for to
   5.298+update the date."
   5.299+  (interactive "P")
   5.300+  (let* ((d (org-entry-get (point) org-expiry-created-property-name))
   5.301+	 d-time d-hour timestr)
   5.302+    (when (or (null d) arg)
   5.303+      ;; update if no date or non-nil prefix argument
   5.304+      ;; FIXME Use `org-time-string-to-time'
   5.305+      (setq d-time (if d (org-time-string-to-time d)
   5.306+		     (current-time)))
   5.307+      (setq d-hour (format-time-string "%H:%M" d-time))
   5.308+      (setq timestr
   5.309+	    ;; two C-u prefixes will call org-read-date
   5.310+            (org-expiry-format-timestamp
   5.311+             (if (equal arg '(16))
   5.312+                 (org-read-date nil nil nil nil d-time d-hour)
   5.313+               (format-time-string
   5.314+                (replace-regexp-in-string "\\(^<\\|>$\\)" ""
   5.315+                                          (cdr org-time-stamp-formats))))
   5.316+             org-expiry-inactive-timestamps))
   5.317+      (save-excursion
   5.318+	(org-entry-put
   5.319+	 (point) org-expiry-created-property-name timestr)))))
   5.320+
   5.321+(defun org-expiry-insert-expiry (&optional today)
   5.322+  "Insert a property with the expiry date.
   5.323+With one `C-u' prefix, don't prompt interactively for the date
   5.324+and insert today's date."
   5.325+  (interactive "P")
   5.326+  (let* ((d (org-entry-get (point) org-expiry-expiry-property-name))
   5.327+	 d-time d-hour timestr)
   5.328+    (setq d-time (if d (org-time-string-to-time d)
   5.329+		   (current-time)))
   5.330+    (setq d-hour (format-time-string "%H:%M" d-time))
   5.331+    (setq timestr (org-expiry-format-timestamp
   5.332+                   (if today
   5.333+                       (format-time-string
   5.334+                        (replace-regexp-in-string "\\(^<\\|>$\\)" ""
   5.335+                                                  (cdr org-time-stamp-formats)))
   5.336+                     (org-read-date nil nil nil nil d-time d-hour))
   5.337+                   org-expiry-inactive-timestamps))
   5.338+    ;; maybe transform to inactive timestamp
   5.339+    (if org-expiry-inactive-timestamps
   5.340+	(setq timestr (concat "[" (substring timestr 1 -1) "]")))
   5.341+
   5.342+    (save-excursion
   5.343+      (org-entry-put
   5.344+       (point) org-expiry-expiry-property-name timestr))))
   5.345+
   5.346+;;; Functions to process expired entries:
   5.347+
   5.348+(defun org-expiry-archive-subtree ()
   5.349+  "Archive the entry at point if it is expired."
   5.350+  (interactive)
   5.351+  (save-excursion
   5.352+    (if (org-expiry-expired-p)
   5.353+	(org-archive-subtree)
   5.354+      (if (called-interactively-p 'any)
   5.355+	  (message "Entry at point is not expired.")))))
   5.356+
   5.357+(defun org-expiry-add-keyword (&optional keyword)
   5.358+  "Add KEYWORD to the entry at point if it is expired."
   5.359+  (interactive "sKeyword: ")
   5.360+  (if (or (member keyword org-todo-keywords-1)
   5.361+	  (setq keyword org-expiry-keyword))
   5.362+      (save-excursion
   5.363+	(if (org-expiry-expired-p)
   5.364+	    (org-todo keyword)
   5.365+	  (if (called-interactively-p 'any)
   5.366+	      (message "Entry at point is not expired."))))
   5.367+    (error "\"%s\" is not a to-do keyword in this buffer" keyword)))
   5.368+
   5.369+;; FIXME what about using org-refile ?
   5.370+
   5.371+(provide 'org-expiry)
   5.372+
   5.373+;;; org-expiry.el ends here
     6.1--- a/emacs/lib/publish.el	Fri Aug 16 21:27:00 2024 -0400
     6.2+++ b/emacs/lib/publish.el	Sat Aug 17 23:42:08 2024 -0400
     6.3@@ -122,3 +122,5 @@
     6.4   (let ((default-directory project-dir))
     6.5     (message (format "publishing from %s" default-directory))    
     6.6     (org-publish "compiler.company" force async)))
     6.7+(provide 'publish)
     6.8+;;; publish.el ends here
     7.1--- a/emacs/lib/ulang.el	Fri Aug 16 21:27:00 2024 -0400
     7.2+++ b/emacs/lib/ulang.el	Sat Aug 17 23:42:08 2024 -0400
     7.3@@ -26,7 +26,8 @@
     7.4 ;;; Code:
     7.5 (require 'org)
     7.6 (require 'ox)
     7.7-
     7.8+(require 'inbox)
     7.9+(require 'publish)
    7.10 (defvar ulang-links-history nil)
    7.11 (defvar ulang-files-history nil)
    7.12 
    7.13@@ -43,6 +44,9 @@
    7.14 
    7.15 (org-export-translate-to-lang (list '("Table of Contents" "Index")) "ulang")
    7.16 
    7.17+;; todo keywords
    7.18+(setq org-stuck-projects '("+PROJECT+LEVEL=2|HOLD|WAIT|TEST|DRAFT|REVIEW|KLUDGE/-DONE" ("NEXT") nil ""))
    7.19+
    7.20 (setq org-todo-keywords
    7.21       '((type "TBD(0!)" "TODO(t!)" "|")
    7.22         (type "WIP(w!)" "|")
    7.23@@ -70,6 +74,7 @@
    7.24         ("WIP" . (:foreground "darkorchid2" :weight bold))
    7.25         ("NOPE" . (:foreground "hotpink" :weight bold :background "darkgreen"))))
    7.26 
    7.27+;; link abbrevs
    7.28 (setq org-link-abbrev-alist
    7.29       '(("vc" . "https://vc.compiler.company/%s")
    7.30         ("comp" . "https://compiler.company/%s")
     8.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2+++ b/emacs/lib/uml-mode.el	Sat Aug 17 23:42:08 2024 -0400
     8.3@@ -0,0 +1,581 @@
     8.4+;;; uml-mode.el --- Minor mode for ascii uml sequence diagrams -*- lexical-binding: t -*-
     8.5+
     8.6+;; Copyright (C) 2015-2020 Ian Martins
     8.7+
     8.8+;; Author: Ian Martins <ianxm@jhu.edu>
     8.9+;; URL: http://github.com/ianxm/emacs-uml
    8.10+;; Version: 0.0.4
    8.11+;; Keywords: docs
    8.12+;; Package-Requires: ((emacs "24.4") seq)
    8.13+
    8.14+;; This file is not part of GNU Emacs.
    8.15+
    8.16+;; This program is free software: you can redistribute it and/or modify
    8.17+;; it under the terms of the GNU General Public License as published by
    8.18+;; the Free Software Foundation, either version 3 of the License, or
    8.19+;; (at your option) any later version.
    8.20+
    8.21+;; This program is distributed in the hope that it will be useful,
    8.22+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.23+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.24+;; GNU General Public License for more details.
    8.25+
    8.26+;; For a full copy of the GNU General Public License
    8.27+;; see <http://www.gnu.org/licenses/>.
    8.28+
    8.29+;;; Commentary:
    8.30+
    8.31+;; provides functions that help in writing ascii uml sequence diagrams.
    8.32+
    8.33+;;; Code:
    8.34+
    8.35+(require 'seq)
    8.36+(require 'subr-x)
    8.37+
    8.38+(defun uml-forward-timeline ()
    8.39+  "Move the point to the next timeline bar."
    8.40+  (interactive)
    8.41+  (let ((start (point))
    8.42+        word)
    8.43+    (forward-word)
    8.44+    (setq word (point))
    8.45+    (goto-char start)
    8.46+    (forward-char)
    8.47+    (while (and
    8.48+            (not (eq ?| (char-after)))
    8.49+            (< (point) word))
    8.50+      (forward-char))))
    8.51+
    8.52+(defun uml-back-timeline ()
    8.53+  "Move the point to the previous timeline bar."
    8.54+  (interactive)
    8.55+  (let ((start (point))
    8.56+        word)
    8.57+    (forward-word -1)
    8.58+    (setq word (point))
    8.59+    (goto-char start)
    8.60+    (forward-char -1)
    8.61+    (while (and
    8.62+            (not (eq ?| (char-after)))
    8.63+            (> (point) word))
    8.64+      (forward-char -1))))
    8.65+
    8.66+(defun uml-swap-left ()
    8.67+  "Swap the timeline at the point with the timeline to its left."
    8.68+  (interactive)
    8.69+  (uml--redraw-sequence-diagram (list 'name :swapleft 'col (current-column))))
    8.70+
    8.71+(defun uml-swap-right ()
    8.72+  "Swap the timeline at the point with the timeline to its right."
    8.73+  (interactive)
    8.74+  (uml--redraw-sequence-diagram (list 'name :swapright 'col (current-column))))
    8.75+
    8.76+(defun uml-delete-timeline ()
    8.77+  "Delete the timeline at point."
    8.78+  (interactive)
    8.79+  (uml--redraw-sequence-diagram (list 'name :delete 'col (current-column))))
    8.80+
    8.81+(defun uml-insert-timeline ()
    8.82+  "Insert a timeline to the right of the point."
    8.83+  (interactive)
    8.84+  (uml--redraw-sequence-diagram (list 'name :insert 'col (current-column))))
    8.85+
    8.86+(defun uml-sequence-diagram ()
    8.87+  "Formats a sequence diagram."
    8.88+  (interactive)
    8.89+  (uml--redraw-sequence-diagram nil))
    8.90+
    8.91+(defun uml--write-text-centered-on (text target)
    8.92+  "Write TEXT centered on the TARGET column."
    8.93+  (let* ((halfname (floor (/ (length text) 2)))
    8.94+         (col (- target halfname))) ; target-pos-len/2
    8.95+    (move-to-column col t)
    8.96+    (insert (format "%s" text))))
    8.97+
    8.98+(defun uml--write-vertical-space (timelines prefix)
    8.99+  "Write a row of empty timeline bars for TIMELINES after writing PREFIX."
   8.100+  (if prefix
   8.101+      (insert prefix))
   8.102+  (dolist (elt timelines)
   8.103+    (let* ((col (plist-get elt 'center)))
   8.104+      (move-to-column col t)
   8.105+      (insert (format "|")))))
   8.106+
   8.107+(defun uml--find-nearest-timeline (timelines col)
   8.108+  "Return the index of the nearest of TIMELINES to the column COL."
   8.109+  (let ((ii 0)
   8.110+        olddelta
   8.111+        ret
   8.112+        delta)
   8.113+    (dolist (elt timelines)
   8.114+      (setq delta (abs (- col (plist-get elt 'origcenter))))
   8.115+      (when (or (not ret) (< delta olddelta))
   8.116+        (setq ret ii)
   8.117+        (setq olddelta delta))
   8.118+      (setq ii (1+ ii)))
   8.119+    ret))
   8.120+
   8.121+(defun uml--write-arrow (from to dashed)
   8.122+  "Write an arrow from FROM timeline to TO timeline, possibly with a DASHED line."
   8.123+  (let ((delta (abs (- to from)))
   8.124+        (ii 0)
   8.125+        on)                             ; bool to toggle between dash or space
   8.126+    (move-to-column (1+ (min to from)))
   8.127+    (if (> from to)                     ; <---
   8.128+        (insert ?<))
   8.129+    (while (< ii (- delta 2))
   8.130+      (insert (if (or (not dashed) on) ?- ? ))
   8.131+      (if on (setq on nil) (setq on t)) ; toggle dash
   8.132+      (setq ii (1+ ii)))
   8.133+    (if (< from to)                     ; --->
   8.134+        (insert ?>))
   8.135+    (delete-char (- delta 1))))
   8.136+
   8.137+(defun uml--write-label-and-arrow (timelines prefix fromcol tocol text dashed)
   8.138+  "Write TIMELINES with PREFIX then label and arrow for a message from column FROMCOL to column TOCOL with label TEXT which may be DASHED."
   8.139+  ;; write label
   8.140+  (if text
   8.141+      (let (center)
   8.142+        (dotimes (ii (length text))
   8.143+          (uml--write-vertical-space timelines prefix)
   8.144+          (newline)
   8.145+          (forward-line -1)
   8.146+          (setq center (floor (/ (+ fromcol tocol) 2)))
   8.147+          (uml--write-text-centered-on (nth ii text) center)
   8.148+          (delete-char (length (nth ii text)))
   8.149+          (forward-line))))
   8.150+
   8.151+  ;; write arrow
   8.152+  (uml--write-vertical-space timelines prefix)
   8.153+  (newline)
   8.154+  (forward-line -1)
   8.155+  (uml--write-arrow fromcol tocol dashed)
   8.156+  (forward-line))
   8.157+
   8.158+(defun uml--write-self-arrow (timelines prefix col text)
   8.159+  "Write TIMELINES with PREFIX and an arrow from and to column COL, labeled with TEXT."
   8.160+  (let ((numrows (max 2 (length text)))
   8.161+        arrow part-index text-part)
   8.162+    (dotimes (ii numrows)
   8.163+      (setq arrow (cond
   8.164+                   ((= (- numrows ii) 2)  " --.")
   8.165+                   ((= (- numrows ii) 1)  "<--'")
   8.166+                   (t "    ")))
   8.167+      (if (not text)
   8.168+          (setq text-part "")
   8.169+        (setq part-index (+ (- ii numrows) (length text)))
   8.170+        (setq text-part (if (< part-index 0) "" (nth part-index text))))
   8.171+      (uml--write-vertical-space timelines prefix)
   8.172+      (newline)
   8.173+      (forward-line -1)
   8.174+      (move-to-column (1+ col))
   8.175+      (insert (format "%s %s" arrow text-part))
   8.176+      (delete-char (min (+ 5 (length text-part)) (- (line-end-position) (point))))
   8.177+      (forward-line))))
   8.178+
   8.179+(defun uml--fit-label-between (timelines left right width)
   8.180+  "Spread out TIMELINES so that LEFT and RIGHT have WIDTH space between them."
   8.181+  (let (leftcol
   8.182+        rightcol
   8.183+        needed)
   8.184+    (setq leftcol (plist-get (nth left timelines) 'center))
   8.185+    (setq rightcol (plist-get (nth right timelines) 'center))
   8.186+    (setq needed (- (+ leftcol  width) rightcol))
   8.187+    (if (> needed 0)
   8.188+      (uml--shift-to-the-right timelines right needed))))
   8.189+
   8.190+(defun uml--shift-to-the-right (timelines right needed)
   8.191+  "Shift all TIMELINES greater than or equal to RIGHT to the right by NEEDED."
   8.192+  (let ((ii right)
   8.193+        elt)
   8.194+    (while (< ii (length timelines))
   8.195+      (setq elt (nth ii timelines))
   8.196+      (plist-put elt 'center (+ (plist-get elt 'center) needed))
   8.197+      (setq ii (1+ ii)))))
   8.198+
   8.199+(defun uml--swap-timelines (timelines messages col1 col2)
   8.200+  "Given all TIMELINES and MESSAGES, swap COL1 and COL2."
   8.201+  (let (tmp)
   8.202+    (setq tmp (nth col1 timelines))
   8.203+    (setcar (nthcdr col1 timelines) (nth col2 timelines))
   8.204+    (setcar (nthcdr col2 timelines) tmp))
   8.205+  (dolist (elt messages)
   8.206+    (if (= (plist-get elt 'from) col1) (plist-put elt 'from col2)
   8.207+      (if (= (plist-get elt 'from) col2) (plist-put elt 'from col1)))
   8.208+    (if (= (plist-get elt 'to) col1) (plist-put elt 'to col2)
   8.209+      (if (= (plist-get elt 'to) col2) (plist-put elt 'to col1)))))
   8.210+
   8.211+(defun uml--find-top-or-bottom (direction)
   8.212+  "Return the position at the top or bottom of the diagram depending on DIRECTION (:top or :bottom)."
   8.213+  (let ((end-of-buffer (if (eq direction :top) (point-min) (point-max)))
   8.214+        (step (if (eq direction :top) -1 1)))
   8.215+    (while (and
   8.216+            (not (= (point) end-of-buffer))
   8.217+            (not (looking-at "^[^[:word:]|]*$")))
   8.218+      (forward-line step))
   8.219+    (cond
   8.220+     ((eq direction :top)
   8.221+      (if (looking-at "^[^[:word:]|]*$")
   8.222+          (forward-line))
   8.223+      (point))
   8.224+     ((eq direction :bottom)
   8.225+      (if (not (= (point) (point-max)))
   8.226+          (forward-line -1))
   8.227+      (line-end-position)))))
   8.228+
   8.229+(defun uml--calc-middle (start end)
   8.230+  "This just computes the integer mean of START and END."
   8.231+  (floor (/ (+ start end) 2)))
   8.232+
   8.233+(defun uml--determine-prefix ()
   8.234+  "Determine the prefix (if there is one).
   8.235+
   8.236+The prefix is made up of any characters on the left margin that
   8.237+aren't part of the diagram, such as comment characters.  Prefixes
   8.238+can be any length but must be made up of only special
   8.239+characters.  Prefixes can have leading spaces but cannot contain
   8.240+spaces in the middle or at the end."
   8.241+  (if (looking-at "\\([[:blank:]]*[^[:word:][:blank:]]+\\) ")
   8.242+      (match-string 1)
   8.243+    nil))
   8.244+
   8.245+(defun uml--parse-timelines (prefix bottom)
   8.246+  "Parse the timeline names.
   8.247+
   8.248+Parse timeline names after the PREFIX of each line until we hit
   8.249+BOTTOM or see a pipe indicating we're past the timeline names and
   8.250+into the messages.  For each timeline, determine the name and
   8.251+center column.  The return structure looks like:
   8.252+
   8.253+    [ (name \"timeline1\" origcenter 5) ... ]
   8.254+
   8.255+Names can contain any characters except whitespace or pipes."
   8.256+  (let (timelines eob)
   8.257+    (while (and (looking-at (concat prefix "[^|]+$"))
   8.258+                (< (point) bottom))
   8.259+      (forward-char (length prefix))
   8.260+      ;; the first "[:blank:]" allows whitespace leading to the name,
   8.261+      ;; but doesn't let the while loop go to the next line.
   8.262+      (while (looking-at "[[:blank:]]*\\([^[:blank:]|\n]+\\)")
   8.263+        (let* ((name (match-string 1))
   8.264+               (beg (- (match-beginning 1) (line-beginning-position)))
   8.265+               (end (- (match-end 1) (line-beginning-position)))
   8.266+               (center (uml--calc-middle beg end))
   8.267+               (index (uml--find-nearest-timeline timelines center))
   8.268+               (halflen (and index (/ (uml--max-length-multipart-name (plist-get (nth index timelines) 'name) 2) 2))))
   8.269+          ;; if this is the first timeline or center is outside of the
   8.270+          ;; nearest existing timeline, then this is a new timeline
   8.271+          ;; and we should create a new timeline, else append to an
   8.272+          ;; existing one
   8.273+          (if (or (not timelines)
   8.274+                  (or (> beg (+ (plist-get (nth index timelines) 'origcenter) halflen))
   8.275+                      (< end (- (plist-get (nth index timelines) 'origcenter) halflen))))
   8.276+              (setq timelines (append timelines (list (list 'name (list name)
   8.277+                                                                   'origcenter center))))
   8.278+            (nconc (plist-get (nth index timelines) 'name) (list name))))
   8.279+        (goto-char (match-end 1)))
   8.280+      (setq eob (= 1 (forward-line 1))))
   8.281+    (if (not eob)            ; if we didn't hit the end of the buffer,
   8.282+        (forward-line -1))   ; back up so message parsing can pick up from the last header line
   8.283+
   8.284+    (sort timelines (lambda (a b) (< (plist-get a 'origcenter)
   8.285+                                     (plist-get b 'origcenter))))))
   8.286+
   8.287+(defun uml--parse-messages (timelines prefix bottom)
   8.288+  "Parse the messages from the diagram.
   8.289+
   8.290+Parse messages from the diagram given the TIMELINES and PREFIX
   8.291+until we reach the BOTTOM.  Messages is a mixed list of plists of
   8.292+arrows and separators.
   8.293+
   8.294+Arrows look like:
   8.295+    (from 0 to 2 label (\"doIt()\") dashed nil)
   8.296+
   8.297+Labels must start with a number or letter and cannot contain
   8.298+spaces, angle brackets or dashes.
   8.299+
   8.300+Separators look like:
   8.301+    (text \"title for next part\")"
   8.302+  (let (messages label dashed found)
   8.303+    (while (and (< (line-end-position) (- bottom (length prefix)))
   8.304+                (< (line-end-position) (buffer-end 1)))
   8.305+      (forward-line 1)
   8.306+      (forward-char (length prefix))
   8.307+
   8.308+      ;; the label may be above the message or on the same line
   8.309+      (when (re-search-forward "[[:word:]][^\n|<>\-]*" (line-end-position) t)
   8.310+        (if (not label)
   8.311+            (setq label (list (string-trim-right (match-string 0)))) ; single part
   8.312+          (nconc label (list (string-trim-right (match-string 0))))) ; multi part
   8.313+        (beginning-of-line))
   8.314+
   8.315+      ;; FOUND is (from . to) where FROM and TO are timeline indices
   8.316+      (setq found (uml--find-message-bounds-maybe timelines))
   8.317+
   8.318+      (when found
   8.319+        (beginning-of-line)
   8.320+        (setq dashed (re-search-forward "\- \-" (line-end-position) t))
   8.321+        (setq messages (append messages (list (list 'label  label
   8.322+                                                    'from   (car found)
   8.323+                                                    'to     (cdr found)
   8.324+                                                    'dashed dashed))))
   8.325+        (setq label nil)))
   8.326+    messages))
   8.327+
   8.328+(defun uml--find-message-bounds-maybe (timelines)
   8.329+  "Find which timelines a message connects.
   8.330+
   8.331+Return the indices in TIMELINES between which the message passes
   8.332+as (from . to), else nil if there is no message on the current
   8.333+line"
   8.334+  (let (from to found)
   8.335+    (cond
   8.336+     ((re-search-forward "\-.*>" (line-end-position) t) ; ->
   8.337+      (setq from (uml--find-nearest-timeline timelines (- (match-beginning 0) (line-beginning-position))))
   8.338+      (setq to (uml--find-nearest-timeline timelines (- (match-end 0) (line-beginning-position))))
   8.339+      (setq found t))
   8.340+
   8.341+     ((re-search-forward "<.*\-" (line-end-position) t) ; <-
   8.342+      (setq from (uml--find-nearest-timeline timelines (- (match-end 0) (line-beginning-position))))
   8.343+      (setq to (uml--find-nearest-timeline timelines (- (match-beginning 0) (line-beginning-position))))
   8.344+      (setq found t))
   8.345+
   8.346+     ((re-search-forward "<" (line-end-position) t)     ; <
   8.347+      (setq from (uml--find-nearest-timeline timelines (- (match-end 0) (line-beginning-position))))
   8.348+      (setq to (uml--find-nearest-timeline timelines (- (match-beginning 0) (line-beginning-position))))
   8.349+      (setq found t))
   8.350+
   8.351+     ((re-search-forward "|\-" (line-end-position) t)   ; |-
   8.352+      (setq from (uml--find-nearest-timeline timelines (- (match-beginning 0) (line-beginning-position))))
   8.353+      (setq to (1+ from))
   8.354+      (if (< to (length timelines))
   8.355+          (setq found t)
   8.356+        (message "Ignoring out of bounds message.")))
   8.357+
   8.358+     ((re-search-forward "\-|" (line-end-position) t)   ; -|
   8.359+      (setq from (uml--find-nearest-timeline timelines (- (match-beginning 0) (line-beginning-position))))
   8.360+      (setq to (- from 1))
   8.361+      (if (>= to 0)
   8.362+          (setq found t)
   8.363+        (message "Ignoring out of bounds message."))))
   8.364+    (if found (cons from to) nil)))
   8.365+
   8.366+(defun uml--apply-adjustments (adjust timelines messages)
   8.367+  "Apply ADJUST to TIMELINES and MESSAGES.
   8.368+
   8.369+Return (TIMELINES . MESSAGES) since we mucked with both of them."
   8.370+  (cond
   8.371+   ((eq :swapleft (plist-get adjust 'name))
   8.372+    (let (current swapwith)
   8.373+      (setq current (uml--find-nearest-timeline timelines (plist-get adjust 'col)))
   8.374+      (setq swapwith (- current 1))
   8.375+      (if (or (< swapwith 0) (>= swapwith (length timelines)))
   8.376+          (plist-put adjust 'movetocol current)
   8.377+        (plist-put adjust 'movetocol swapwith)
   8.378+        (uml--swap-timelines timelines messages current swapwith))))
   8.379+
   8.380+   ((eq :swapright (plist-get adjust 'name))
   8.381+    (let (current swapwith)
   8.382+      (setq current (uml--find-nearest-timeline timelines (plist-get adjust 'col)))
   8.383+      (setq swapwith (1+ current))
   8.384+      (if (or (< swapwith 0) (>= swapwith (length timelines)))
   8.385+          (plist-put adjust 'movetocol current)
   8.386+        (plist-put adjust 'movetocol swapwith)
   8.387+        (uml--swap-timelines timelines messages current swapwith))))
   8.388+
   8.389+   ((eq :delete (plist-get adjust 'name))
   8.390+    (let (current col)
   8.391+      (setq current (uml--find-nearest-timeline timelines (plist-get adjust 'col))
   8.392+            col current)
   8.393+      (plist-put adjust 'movetocol (max 0 (1- col)))
   8.394+      (when (>= col 0)
   8.395+        (setq timelines (delete (nth col timelines) timelines))
   8.396+        (dolist (elt messages)
   8.397+          (let ((from (plist-get elt 'from))
   8.398+                (to   (plist-get elt 'to)))
   8.399+            (if (or (= from col) (= to col))
   8.400+                (setq messages (delete elt messages))
   8.401+              (if (> from col) (plist-put elt 'from (- from 1)))
   8.402+              (if (> to col) (plist-put elt 'to (- to 1)))))))))
   8.403+
   8.404+   ((eq :insert (plist-get adjust 'name))
   8.405+    (let (current new rest)
   8.406+      (setq current (uml--find-nearest-timeline timelines (plist-get adjust 'col)))
   8.407+      (plist-put adjust 'movetocol current)
   8.408+      (setq current (1+ current))
   8.409+      (setq new (list (list 'name (list "new")
   8.410+                            'origcenter nil)))
   8.411+      (setq rest (nthcdr current timelines))
   8.412+      (setcdr (nthcdr (- current 1) timelines) new)
   8.413+      (setcdr new rest)
   8.414+      (dolist (elt messages)
   8.415+        (let ((from (plist-get elt 'from))
   8.416+              (to   (plist-get elt 'to)))
   8.417+          (if (>= from current) (plist-put elt 'from (1+ from)))
   8.418+          (if (>= to current) (plist-put elt 'to (1+ to))))))))
   8.419+  (cons timelines messages))
   8.420+
   8.421+(defun uml--max-length-multipart-name (multipart-name min)
   8.422+  "Convenience function to compute the longest string.
   8.423+
   8.424+Return the longest string in MULTIPART-NAME, which is a list of
   8.425+strings, or MIN if it is longer."
   8.426+  (seq-reduce (lambda (namelength namepart) (max namelength (length namepart)))
   8.427+              multipart-name
   8.428+              min))
   8.429+
   8.430+(defun uml--space-out-timelines (timelines messages prefix)
   8.431+  "Space out TIMELINES to fit MESSAGES' labels and PREFIX."
   8.432+    (dotimes (ii (length timelines))
   8.433+      (plist-put (nth ii timelines) 'center (+ (* 12 ii) 6 (length prefix))))
   8.434+    (let (elt needed namelen)
   8.435+      (dotimes (ii (length timelines))
   8.436+        (setq elt (nth ii timelines))
   8.437+        (setq namelen (uml--max-length-multipart-name (plist-get elt 'name) 8))
   8.438+        (setq needed (floor (/ (- namelen 8) 2)))
   8.439+        (when (> needed 0)
   8.440+            (uml--shift-to-the-right timelines ii      needed)
   8.441+            (uml--shift-to-the-right timelines (1+ ii) needed))))
   8.442+
   8.443+    (dolist (elt messages)
   8.444+      (let* ((to    (plist-get elt 'to))
   8.445+             (from  (plist-get elt 'from))
   8.446+             (left  (min to from))
   8.447+             (right (max to from)))
   8.448+        (if (= left right)
   8.449+            (if (< (1+ left) (length timelines))
   8.450+                (uml--fit-label-between timelines ; self arrow
   8.451+                                        left
   8.452+                                        (1+ left)
   8.453+                                        (+ (uml--max-length-multipart-name (plist-get elt 'label) 0) 8)))
   8.454+          (uml--fit-label-between timelines
   8.455+                                  left
   8.456+                                  right
   8.457+                                  (+ (uml--max-length-multipart-name (plist-get elt 'label) 0) 4))))))
   8.458+
   8.459+(defun uml--count-timeline-name-rows (timelines)
   8.460+  "Count the rows of the TIMELINES' names."
   8.461+  (seq-reduce (lambda (val elt) (max val (length (plist-get elt 'name))))
   8.462+                              timelines 0))
   8.463+
   8.464+(defun uml--write-diagram (timelines messages prefix)
   8.465+  "Write the TIMELINES and MESSAGES using PREFIX to the buffer.
   8.466+
   8.467+This is done in two steps:
   8.468+1. write timeline names
   8.469+2. write messages"
   8.470+
   8.471+  ;; 1. write timeline names
   8.472+  (let (numrows)
   8.473+    ;; determine the number of rows needed for the timeline names
   8.474+    (setq numrows (uml--count-timeline-name-rows timelines))
   8.475+    ;; then write them out to the buffer
   8.476+    (dotimes (ii numrows)
   8.477+      (if prefix
   8.478+          (insert prefix))
   8.479+      (dolist (elt timelines)
   8.480+        (let* ((parts (plist-get elt 'name))
   8.481+               (index (+ (- (length parts) numrows) ii))
   8.482+               (part (and (>= index 0) (nth index parts))))
   8.483+          (if part
   8.484+              (uml--write-text-centered-on part
   8.485+                                           (plist-get elt 'center)))))
   8.486+      (newline)))
   8.487+
   8.488+  ;; 2. write messages
   8.489+  (dolist (elt messages)
   8.490+    (uml--write-vertical-space timelines prefix)
   8.491+    (newline)
   8.492+
   8.493+    (let* ((text       (plist-get elt 'label))
   8.494+           (from       (plist-get elt 'from))
   8.495+           (to         (plist-get elt 'to))
   8.496+           (fromcenter (plist-get (nth from timelines) 'center))
   8.497+           (tocenter   (plist-get (nth to timelines) 'center))
   8.498+           (dashed     (plist-get elt 'dashed))
   8.499+           selfmessage)
   8.500+      (setq selfmessage (= (plist-get elt 'from) (plist-get elt 'to)))
   8.501+
   8.502+      (if selfmessage
   8.503+          (uml--write-self-arrow timelines prefix fromcenter text)
   8.504+        (uml--write-label-and-arrow timelines prefix fromcenter tocenter text dashed))))
   8.505+
   8.506+  (uml--write-vertical-space timelines prefix))
   8.507+
   8.508+(defun uml--redraw-sequence-diagram (adjust)
   8.509+  "Redraws a sequence diagram after applying ADJUST.  This is the main routine."
   8.510+  (let (top         ; first line in buffer of diagram
   8.511+        bottom      ; last line in buffer of diagram
   8.512+        prefix      ; comment character or nil
   8.513+        timelines   ; list of timeline data
   8.514+        messages)   ; list of arrow data
   8.515+
   8.516+    (beginning-of-line)
   8.517+
   8.518+    ;; find the top and bottom of the diagram
   8.519+    (setq top (uml--find-top-or-bottom :top))
   8.520+    (setq bottom (uml--find-top-or-bottom :bottom))
   8.521+    ;; (message "top: %d bottom: %d" top bottom)
   8.522+
   8.523+    (goto-char top)
   8.524+    (setq prefix (uml--determine-prefix))
   8.525+
   8.526+    ;; parse timeline names from old diagram
   8.527+    (setq timelines (uml--parse-timelines prefix bottom))
   8.528+    ;; (message "timelines %s" timelines)
   8.529+
   8.530+    ;; parse messages from old diagram
   8.531+    (setq messages (uml--parse-messages timelines prefix bottom))
   8.532+    ;; (message "messages %s" messages)
   8.533+
   8.534+    ;; clear the old diagram content from the buffer
   8.535+    (goto-char top)
   8.536+    (delete-char (- bottom top))
   8.537+
   8.538+    ;; apply adjustments such as shifts or swaps
   8.539+    (let (ret)
   8.540+      (setq ret (uml--apply-adjustments adjust timelines messages))
   8.541+      (setq timelines (car ret)
   8.542+            messages (cdr ret)))
   8.543+
   8.544+    ;; calculate timeline center columns
   8.545+    (uml--space-out-timelines timelines messages prefix)
   8.546+
   8.547+    ;; render the diagram into the buffer
   8.548+    (uml--write-diagram timelines messages prefix)
   8.549+
   8.550+    ;; move the cursor back to the column where it was before we did anything
   8.551+    (goto-char top)
   8.552+    (when (plist-get adjust 'movetocol)
   8.553+      (forward-line (1- (uml--count-timeline-name-rows timelines)))
   8.554+      (move-to-column (plist-get (nth (plist-get adjust 'movetocol) timelines) 'center)))))
   8.555+
   8.556+;;;###autoload
   8.557+(define-minor-mode uml-mode
   8.558+  "Toggle uml mode.
   8.559+Interactively with no argument, this command toggles the mode.
   8.560+A positive prefix argument enables the mode, any other prefix
   8.561+argument disables it.  From Lisp, argument omitted or nil enables
   8.562+the mode, `toggle' toggles the state.
   8.563+
   8.564+When uml mode is enabled, C-c while the point is in a
   8.565+sequence diagram cleans up the formatting of the diagram.
   8.566+See the command \\[uml-seqence-diagram]."
   8.567+ ;; The initial value.
   8.568+ :init-value nil
   8.569+ ;; The indicator for the mode line.
   8.570+ :lighter " uml"
   8.571+ ;; The minor mode bindings.
   8.572+ :keymap
   8.573+ `((,(kbd "C-c C-c") . uml-sequence-diagram)
   8.574+   (,(kbd "<M-left>") . uml-swap-left)
   8.575+   (,(kbd "<M-right>") . uml-swap-right)
   8.576+   (,(kbd "<M-S-left>") . uml-delete-timeline)
   8.577+   (,(kbd "<M-S-right>") . uml-insert-timeline)
   8.578+   (,(kbd "M-f") . uml-forward-timeline)
   8.579+   (,(kbd "M-b") . uml-back-timeline))
   8.580+ :group 'uml)
   8.581+
   8.582+(provide 'uml-mode)
   8.583+
   8.584+;;; uml-mode.el ends here
     9.1--- a/lisp/lib/dat/dat.asd	Fri Aug 16 21:27:00 2024 -0400
     9.2+++ b/lisp/lib/dat/dat.asd	Sat Aug 17 23:42:08 2024 -0400
     9.3@@ -1,5 +1,5 @@
     9.4 (defsystem :dat
     9.5-  :description "Data formats"
     9.6+  :description "Data Systems"
     9.7   :depends-on (:cl-ppcre :std :obj #+png :png :flexi-streams :io :log)
     9.8   :version "0.1.0"
     9.9   :serial t
    9.10@@ -29,6 +29,7 @@
    9.11                 ((:file "const")
    9.12                  (:file "entity")
    9.13                  (:file "html")))
    9.14+               (:file "handlebars")
    9.15                (:file "mime")
    9.16                (:file "toml")
    9.17                (:file "arff")
    10.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2+++ b/lisp/lib/dat/handlebars.lisp	Sat Aug 17 23:42:08 2024 -0400
    10.3@@ -0,0 +1,18 @@
    10.4+;;; handlebars.lisp --- Handlebars Template Format
    10.5+
    10.6+;; Minimal templating on steroids in Lisp
    10.7+
    10.8+;;; Commentary
    10.9+
   10.10+;; handlebars is a popular templating system initially derived from mustache
   10.11+;; which happened to have some CL bindings:
   10.12+;; https://github.com/kanru/cl-mustache
   10.13+
   10.14+;; This package aims to integrate directly with the DAT/HTML package but
   10.15+;; should be able to be dropped-in to other serde-enabled formats like DAT/XML
   10.16+;; or DAT/JSON.
   10.17+
   10.18+;; ref: https://handlebarsjs.com
   10.19+
   10.20+;;; Code:
   10.21+(in-package :dat/handlebars)
    11.1--- a/lisp/lib/dat/pkg.lisp	Fri Aug 16 21:27:00 2024 -0400
    11.2+++ b/lisp/lib/dat/pkg.lisp	Sat Aug 17 23:42:08 2024 -0400
    11.3@@ -175,6 +175,10 @@
    11.4    :extract-path-list
    11.5    :extract-path))
    11.6 
    11.7+(defpackage :dat/handlebars
    11.8+  (:use :cl :std :dat/proto :dat/html)
    11.9+  (:export))
   11.10+  
   11.11 (defpackage :dat/mime
   11.12   (:use :cl :std :dat/proto :dat/xml)
   11.13   (:export :*mime-database*
    12.1--- a/readme.org	Fri Aug 16 21:27:00 2024 -0400
    12.2+++ b/readme.org	Sat Aug 17 23:42:08 2024 -0400
    12.3@@ -3,16 +3,14 @@
    12.4 #+author: Richard Westhaver
    12.5 #+email: richard.westhaver@gmail.com
    12.6 #+setupfile: https://cdn.compiler.company/org/clean.theme
    12.7+#+property: header-args :eval no-export
    12.8 - [[https://compiler.company/docs/core][Docs]]
    12.9   - [[https://compiler.company/docs/core/install.html][Install]] :: Install Guide
   12.10   - [[https://compiler.company/docs/core/tests.html][Tests]] :: Testing Guide
   12.11   - [[https://compiler.company/docs/core/stats.html][Stats]] :: Project Statistics
   12.12 
   12.13 * Overview
   12.14- is a small software research laboratory
   12.15-concerned with the future of Mechanical Freedom.
   12.16-
   12.17-This repository contains the monolothic core of the Compiler Company.
   12.18+This repository contains the monolothic core of [[comp:][The Compiler Company]].
   12.19 
   12.20 To bootstrap the core you will need recent versions of [[https://www.rust-lang.org/][Rust]], [[http://www.sbcl.org/][SBCL]], and
   12.21 a C compiler (clang or gcc). Only Unix systems are explicitly
   12.22@@ -27,25 +25,25 @@
   12.23 platform-specific [[https://packy.compiler.company/dist][binary distributions]].
   12.24 
   12.25 #+NAME: Optional Dependencies
   12.26-| dependency  | dependents             | src                                           | 
   12.27-|-------------+------------------------+-----------------------------------------------+
   12.28-| Blake3      | ffi/blake3             | https://vc.compiler.company/packy/blake3      | 
   12.29-| Tree-sitter | ffi/tree-sitter        | https://vc.compiler.company/packy/tree-sitter | 
   12.30-| Uring       | ffi/uring              | https://vc.compiler.company/packy/uring       | 
   12.31-| Btrfs       | ffi/btrfs              | https://vc.compiler.company/packy/btrfs       | 
   12.32-| Ublksrv     | ffi/ublk               | https://vc.compiler.company/packy/ublksrv     | 
   12.33-| OpenSSL     | lib/net                |                                               | 
   12.34-| RocksDB     | ffi/rocksdb            | https://vc.compiler.company/packy/rocksdb     | 
   12.35-| Git         | lib/vc/git             | https://vc.compiler.company/packy/git         | 
   12.36-| Hg          | lib/vc/hg              | https://vc.compiler.company/packy/hg          | 
   12.37-| Zstd        | ffi/zstd               | https://vc.compiler.company/packy/zstd        | 
   12.38-| Qemu        | lib/box                | https://vc.compiler.company/packy/qemu        | 
   12.39-| Podman      | lib/pod                | https://vc.compiler.company/packy/podman      | 
   12.40-| Emacs       | emacs                  | https://vc.compiler.company/packy/emacs       | 
   12.41-| StumpWM     | lib/gui/wm/x11/stumpwm | https://vc.compiler.company/packy/stumpwm     | 
   12.42-| Readline    | ffi/readline           |                                               | 
   12.43-| Keyutils    | ffi/keyutils           |                                               | 
   12.44-| Mpd         | lib/aud/mpd            | https://vc.compiler.company/packy/mpd         | 
   12.45+| dependency  | dependents             | src                                           |
   12.46+|-------------+------------------------+-----------------------------------------------|
   12.47+| Blake3      | ffi/blake3             | https://vc.compiler.company/packy/blake3      |
   12.48+| Tree-sitter | ffi/tree-sitter        | https://vc.compiler.company/packy/tree-sitter |
   12.49+| Uring       | ffi/uring              | https://vc.compiler.company/packy/uring       |
   12.50+| Btrfs       | ffi/btrfs              | https://vc.compiler.company/packy/btrfs       |
   12.51+| Ublksrv     | ffi/ublk               | https://vc.compiler.company/packy/ublksrv     |
   12.52+| OpenSSL     | lib/net                | [[https://vc.compiler.company/packy/openssl]]     |
   12.53+| RocksDB     | ffi/rocksdb            | https://vc.compiler.company/packy/rocksdb     |
   12.54+| Git         | lib/vc/git             | https://vc.compiler.company/packy/git         |
   12.55+| Hg          | lib/vc/hg              | https://vc.compiler.company/packy/hg          |
   12.56+| Zstd        | ffi/zstd               | https://vc.compiler.company/packy/zstd        |
   12.57+| Qemu        | lib/box                | https://vc.compiler.company/packy/qemu        |
   12.58+| Podman      | lib/pod                | https://vc.compiler.company/packy/podman      |
   12.59+| Emacs       | emacs                  | https://vc.compiler.company/packy/emacs       |
   12.60+| StumpWM     | lib/gui/wm/x11/stumpwm | https://vc.compiler.company/packy/stumpwm     |
   12.61+| Readline    | ffi/readline           | [[https://vc.compiler.company/packy/readline]]    |
   12.62+| Keyutils    | ffi/keyutils           | [[https://vc.compiler.company/packy/libkeyutils]] |
   12.63+| Mpd         | lib/aud/mpd            | https://vc.compiler.company/packy/mpd         |
   12.64 
   12.65 * Build
   12.66 The Core consists of two major system: the *lisp* system and the
   12.67@@ -81,7 +79,7 @@
   12.68 
   12.69 #+RESULTS: x-help
   12.70 #+begin_example
   12.71-This is SBCL 2.4.7:dc890089a, an implementation of ANSI Common Lisp.
   12.72+This is SBCL 2.4.7:76bbecb68, an implementation of ANSI Common Lisp.
   12.73 More information about SBCL is available at <http://www.sbcl.org/>.
   12.74 
   12.75 SBCL is free software, provided as is, with absolutely no warranty.