From 26fabb22edfea51e8a686c179ab91d00a2ff0bc3 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 5 May 2026 12:30:42 -0500 Subject: refactor!: collapse three time-left format defcustoms into one alist I merged `chime-time-left-format-at-event', `chime-time-left-format-short', and `chime-time-left-format-long' into a single alist `chime-time-left-formats' keyed by `at-event' / `short' / `long'. Three knobs for one feature (countdown display) was unnecessary surface area; one alist is the same flexibility with a third the namespace. `chime--time-left' switched from a pcase-on-variable to a pcase-on-regime that picks an alist key, then `alist-get's the format string. Behavior is identical for default settings. Test setup in the four affected files now builds the alist with `(list (cons 'KEY VAL) ...)' instead of `'(...)'. The literal-quote form returns the SAME cons-cell structure on every evaluation, so a previous test mutating it via `setf' on `alist-get' poisoned later tests. `list' + `cons' produces fresh structure per call, which is what the tests actually need. Migration: `(setq chime-time-left-format-short "in %mm")' becomes `(setf (alist-get 'short chime-time-left-formats) "in %mm")', or a full `(setq chime-time-left-formats '((at-event . ...) (short . ...) (long . ...)))' replacement. --- README.org | 42 ++++++++++++++-------- chime.el | 63 ++++++++++++++++----------------- tests/test-chime-make-tooltip.el | 7 ++-- tests/test-chime-notification-text.el | 9 ++--- tests/test-chime-propertize-modeline.el | 7 ++-- tests/test-chime-time-left.el | 21 +++++------ 6 files changed, 82 insertions(+), 67 deletions(-) diff --git a/README.org b/README.org index 258cabc..fd8081c 100644 --- a/README.org +++ b/README.org @@ -436,7 +436,7 @@ Customize which components are shown: Available placeholders: - =%t= - Event title - =%T= - Event time (formatted per =chime-display-time-format-string=) -- =%u= - Time until event (formatted per =chime-time-left-format-*=) +- =%u= - Time until event (formatted per =chime-time-left-formats=) ***** Event Time Format @@ -471,24 +471,35 @@ Available format codes: Customize how the countdown is displayed: +All three formats live in one alist, =chime-time-left-formats=, with three keys: +=at-event= (literal string when the event has arrived), =short= +(=format-seconds= template for under 1 hour), and =long= (template for 1 hour +or more). + #+BEGIN_SRC elisp ;; Default: verbose format -(setq chime-time-left-format-short "in %M") ; Under 1 hour -(setq chime-time-left-format-long "in %H %M") ; 1 hour or more +(setq chime-time-left-formats + '((at-event . "right now") + (short . "in %M") + (long . "in %H %M"))) ;; → "in 10 minutes" or "in 1 hour 30 minutes" ;; Compact format -(setq chime-time-left-format-short "in %mm") -(setq chime-time-left-format-long "in %hh %mm") +(setq chime-time-left-formats + '((at-event . "right now") + (short . "in %mm") + (long . "in %hh %mm"))) ;; → "in 10m" or "in 1h 30m" ;; Very compact (no prefix) -(setq chime-time-left-format-short "%mm") -(setq chime-time-left-format-long "%hh%mm") +(setq chime-time-left-formats + '((at-event . "right now") + (short . "%mm") + (long . "%hh%mm"))) ;; → "10m" or "1h30m" -;; Custom "at event time" message -(setq chime-time-left-format-at-event "NOW!") +;; Tweak just one key — `setf' on `alist-get' is fine for one-shots +(setf (alist-get 'at-event chime-time-left-formats) "NOW!") ;; → "NOW!" instead of "right now" #+END_SRC @@ -529,8 +540,10 @@ For maximum modeline space savings: (setq chime-modeline-lookahead-minutes 60) (setq chime-modeline-format " ⏰ %s") ; Minimal prefix (setq chime-notification-text-format "%t (%u)") ; No time shown -(setq chime-time-left-format-short "%mm") ; Compact short -(setq chime-time-left-format-long "%hh%mm") ; Compact long +(setq chime-time-left-formats ; Compact countdown + '((at-event . "right now") + (short . "%mm") + (long . "%hh%mm"))) (setq chime-max-title-length 20) ; Truncate long titles ;; Result: "⏰ Dentist (10m)" or "⏰ Retrospective o... (1h30m)" #+END_SRC @@ -835,9 +848,10 @@ If you have custom variables that need to be available in chime's async subproce (setq chime-modeline-format " ⏰%s") ; Minimal prefix (setq chime-notification-text-format "%t (%u)") ; Title + countdown only (setq chime-display-time-format-string "%H:%M") ; 24-hour time - (setq chime-time-left-format-short "in %mm") ; Compact: "in 5m" - (setq chime-time-left-format-long "%hh%mm") ; Compact: "1h30m" - (setq chime-time-left-format-at-event "NOW!") ; Custom at-event message + (setq chime-time-left-formats ; Countdown formats + '((at-event . "NOW!") + (short . "in %mm") + (long . "%hh%mm"))) ;; Notification settings (setq chime-notification-title "Reminder") diff --git a/chime.el b/chime.el index e843537..9de2e72 100644 --- a/chime.el +++ b/chime.el @@ -224,43 +224,40 @@ Note: Avoid using seconds (%S) as chime polls once per minute." (warn "chime-display-time-format-string: Using seconds (%%S) is not recommended as chime polls once per minute")) (set-default symbol value))) -(defcustom chime-time-left-format-at-event "right now" - "Format string for when event time has arrived (0 or negative seconds). -This is a literal string with no format codes." - :package-version '(chime . "0.6.0") - :group 'chime - :type 'string) - -(defcustom chime-time-left-format-short "in %M" - "Format string for times under 1 hour. -Uses `format-seconds' codes: +(defcustom chime-time-left-formats + '((at-event . "right now") + (short . "in %M") + (long . "in %H %M")) + "Format strings for time-until-event display, keyed by regime. +An alist with three keys: + + at-event - Literal string when event time has arrived (0 or negative + seconds). No format codes. + short - `format-seconds' template for times under 1 hour. + long - `format-seconds' template for times 1 hour or longer. + +Available `format-seconds' codes for short/long: %m - minutes as number only (e.g., \"37\") %M - minutes with unit name (e.g., \"37 minutes\") + %h - hours as number only (e.g., \"1\") + %H - hours with unit name (e.g., \"1 hour\") -Examples: +Examples for short: \"in %M\" -> \"in 37 minutes\" \"in %mm\" -> \"in 37m\" - \"%m min\" -> \"37 min\"" - :package-version '(chime . "0.6.0") - :group 'chime - :type 'string) + \"%m min\" -> \"37 min\" -(defcustom chime-time-left-format-long "in %H %M" - "Format string for times 1 hour or longer. -Uses `format-seconds' codes: - %h - hours as number only (e.g., \"1\") - %H - hours with unit name (e.g., \"1 hour\") - %m - minutes as number only (e.g., \"37\") - %M - minutes with unit name (e.g., \"37 minutes\") - -Examples: +Examples for long: \"in %H %M\" -> \"in 1 hour 37 minutes\" \"in %hh %mm\" -> \"in 1h 37m\" \"(%h hr %m min)\" -> \"(1 hr 37 min)\" \"%hh%mm\" -> \"1h37m\"" - :package-version '(chime . "0.6.0") + :package-version '(chime . "0.8.0") :group 'chime - :type 'string) + :type '(alist :key-type (choice (const at-event) + (const short) + (const long)) + :value-type string)) (defcustom chime-predicate-whitelist nil "Receive notifications for events matching these predicates only. @@ -699,16 +696,16 @@ Returns non-nil only if the timestamp includes HH:MM time information." (defun chime--time-left (seconds) "Human-friendly representation for SECONDS. -Format is controlled by `chime-time-left-format-at-event', -`chime-time-left-format-short', and `chime-time-left-format-long'." +Format is controlled by `chime-time-left-formats' (keys: at-event, +short, long)." ;; Don't fold this into a `(-> seconds (pcase ...) ...)' threading form — ;; edebug's defun parser rejects threaded pcase, which breaks coverage ;; instrumentation (undercover) on this file. - (let ((format-string - (pcase seconds - ((pred (>= 0)) chime-time-left-format-at-event) - ((pred (>= 3600)) chime-time-left-format-short) - (_ chime-time-left-format-long)))) + (let* ((regime (pcase seconds + ((pred (>= 0)) 'at-event) + ((pred (>= 3600)) 'short) + (_ 'long))) + (format-string (alist-get regime chime-time-left-formats))) (format-seconds format-string seconds))) (defun chime--get-hh-mm-from-org-time-string (time-string) diff --git a/tests/test-chime-make-tooltip.el b/tests/test-chime-make-tooltip.el index 2c9cf37..7ace684 100644 --- a/tests/test-chime-make-tooltip.el +++ b/tests/test-chime-make-tooltip.el @@ -40,9 +40,10 @@ (setq chime-modeline-tooltip-max-events 5) (setq chime-tooltip-header-format "Upcoming Events as of %a %b %d %Y @ %I:%M %p") (setq chime-display-time-format-string "%I:%M %p") - (setq chime-time-left-format-at-event "right now") - (setq chime-time-left-format-short "in %M") - (setq chime-time-left-format-long "in %H %M")) + (setq chime-time-left-formats + (list (cons 'at-event "right now") + (cons 'short "in %M") + (cons 'long "in %H %M")))) (defun test-chime-make-tooltip-teardown () "Teardown function run after each test." diff --git a/tests/test-chime-notification-text.el b/tests/test-chime-notification-text.el index 2446004..a707d29 100644 --- a/tests/test-chime-notification-text.el +++ b/tests/test-chime-notification-text.el @@ -40,9 +40,10 @@ ;; Reset notification text format to default (setq chime-notification-text-format "%t at %T (%u)") ;; Reset time-left formats to defaults - (setq chime-time-left-format-at-event "right now") - (setq chime-time-left-format-short "in %M") - (setq chime-time-left-format-long "in %H %M") + (setq chime-time-left-formats + (list (cons 'at-event "right now") + (cons 'short "in %M") + (cons 'long "in %H %M"))) ;; Reset title truncation to default (no truncation) (setq chime-max-title-length nil)) @@ -331,7 +332,7 @@ REFACTORED: Uses dynamic timestamps" (str-interval (cons (test-timestamp-string time) '(10 . medium))) (event '((title . "Meeting"))) (chime-notification-text-format "%t (%u)") - (chime-time-left-format-short "in %mm")) + (chime-time-left-formats '((at-event . "right now") (short . "in %mm") (long . "in %H %M")))) (let ((result (chime--notification-text str-interval event))) (should (stringp result)) (should (string-equal "Meeting (in 10m)" result)))) diff --git a/tests/test-chime-propertize-modeline.el b/tests/test-chime-propertize-modeline.el index 3856831..cc0dbb2 100644 --- a/tests/test-chime-propertize-modeline.el +++ b/tests/test-chime-propertize-modeline.el @@ -38,9 +38,10 @@ (chime-create-test-base-dir) (setq chime-tooltip-header-format "Upcoming Events as of %a %b %d %Y @ %I:%M %p") (setq chime-display-time-format-string "%I:%M %p") - (setq chime-time-left-format-at-event "right now") - (setq chime-time-left-format-short "in %M") - (setq chime-time-left-format-long "in %H %M")) + (setq chime-time-left-formats + (list (cons 'at-event "right now") + (cons 'short "in %M") + (cons 'long "in %H %M")))) (defun test-chime-propertize-teardown () "Teardown function." diff --git a/tests/test-chime-time-left.el b/tests/test-chime-time-left.el index 25b41d9..b9bd176 100644 --- a/tests/test-chime-time-left.el +++ b/tests/test-chime-time-left.el @@ -35,9 +35,10 @@ "Setup function run before each test." (chime-create-test-base-dir) ;; Reset format strings to defaults - (setq chime-time-left-format-at-event "right now") - (setq chime-time-left-format-short "in %M") - (setq chime-time-left-format-long "in %H %M")) + (setq chime-time-left-formats + (list (cons 'at-event "right now") + (cons 'short "in %M") + (cons 'long "in %H %M")))) (defun test-chime-time-left-teardown () "Teardown function run after each test." @@ -216,7 +217,7 @@ (test-chime-time-left-setup) (unwind-protect (progn - (setq chime-time-left-format-short "in %mm") + (setf (alist-get 'short chime-time-left-formats) "in %mm") (let ((result (chime--time-left 300))) ; 5 minutes (should (stringp result)) (should (string-equal "in 5m" result)))) @@ -227,7 +228,7 @@ (test-chime-time-left-setup) (unwind-protect (progn - (setq chime-time-left-format-long "in %hh %mm") + (setf (alist-get 'long chime-time-left-formats) "in %hh %mm") (let ((result (chime--time-left 5820))) ; 1 hour 37 minutes (should (stringp result)) (should (string-equal "in 1h 37m" result)))) @@ -238,7 +239,7 @@ (test-chime-time-left-setup) (unwind-protect (progn - (setq chime-time-left-format-long "(%h hr %m min)") + (setf (alist-get 'long chime-time-left-formats) "(%h hr %m min)") (let ((result (chime--time-left 5820))) ; 1 hour 37 minutes (should (stringp result)) (should (string-equal "(1 hr 37 min)" result)))) @@ -249,7 +250,7 @@ (test-chime-time-left-setup) (unwind-protect (progn - (setq chime-time-left-format-long "%hh%mm") + (setf (alist-get 'long chime-time-left-formats) "%hh%mm") (let ((result (chime--time-left 5820))) ; 1 hour 37 minutes (should (stringp result)) (should (string-equal "1h37m" result)))) @@ -260,7 +261,7 @@ (test-chime-time-left-setup) (unwind-protect (progn - (setq chime-time-left-format-at-event "NOW!") + (setf (alist-get 'at-event chime-time-left-formats) "NOW!") (let ((result (chime--time-left 0))) (should (stringp result)) (should (string-equal "NOW!" result)))) @@ -271,7 +272,7 @@ (test-chime-time-left-setup) (unwind-protect (progn - (setq chime-time-left-format-short "%m min") + (setf (alist-get 'short chime-time-left-formats) "%m min") (let ((result (chime--time-left 300))) ; 5 minutes (should (stringp result)) (should (string-equal "5 min" result)))) @@ -282,7 +283,7 @@ (test-chime-time-left-setup) (unwind-protect (progn - (setq chime-time-left-format-long "🕐 %hh%mm") + (setf (alist-get 'long chime-time-left-formats) "🕐 %hh%mm") (let ((result (chime--time-left 5820))) ; 1 hour 37 minutes (should (stringp result)) (should (string-equal "🕐 1h37m" result)))) -- cgit v1.2.3