From 1c5a2ebab7c721d795ed9331afdb305fd683e172 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 16 May 2026 02:35:38 -0500 Subject: refactor(foundation): hygiene pass across early-init, user-constants, system-defaults, chrono-tools Six small fixes the 2026-05-15 module-by-module re-review surfaced: - Consolidate `user-home-dir` -- canonical defconst stays in early-init.el (package-archive bootstrap needs it before normal modules load); user-constants.el switches to a `defvar` with the identical `(getenv "HOME")` expression so the module still loads / byte-compiles standalone, but at runtime early-init's defconst wins. - Drop the redundant `(autoload 'env-bsd-p ...)` line in system-defaults.el. The `(eval-when-compile (require 'host-environment))` already exposes the symbol to the byte compiler, and at runtime host-environment is loaded earlier in init.el. Added a comment documenting the boundary. - Convert `cj/debug-modules` and `cj/use-online-repos` from `defvar` to `defcustom`, with `:type`, `:group 'cj`, and a top-level `(defgroup cj ...)` so both show up in M-x customize. - Name the package-archive priorities in early-init.el. Nine new defconsts replace the magic numbers (200 / 125 / 120 / 115 / 100 / 25 / 20 / 15 / 5) with one constant each, plus a header comment explaining the local-first ordering and the gnu > nongnu > melpa > melpa-stable trust ranking within each tier. - Delete the 19-line commented-out `use-package time` world-clock block in chrono-tools.el. `time-zones` immediately above is the active replacement; git history preserves the old config if anyone needs it. - Add coverage for `cj/tmr-select-sound-file`. Collapsed the prefix-arg branch into a delegation to `cj/tmr-reset-sound-to-default` (single reset source) and extracted `cj/tmr--available-sound-files` as a pure helper that tests directly. 9 ERT tests across Normal / Boundary / Error cover the available-sounds helper, the reset path, the prefix-arg delegation (no prompt), the normal selection path, and the empty-dir / missing-dir / cancel boundaries. --- modules/chrono-tools.el | 109 +++++++++++++++++++-------------------------- modules/system-defaults.el | 9 ++-- modules/user-constants.el | 24 +++++++--- 3 files changed, 69 insertions(+), 73 deletions(-) (limited to 'modules') diff --git a/modules/chrono-tools.el b/modules/chrono-tools.el index 37a065f2..8b8c2072 100644 --- a/modules/chrono-tools.el +++ b/modules/chrono-tools.el @@ -20,26 +20,6 @@ :commands time-zones :bind ("M-S-c" . time-zones)) ;; was M-C, overrides capitalize-word -;; Commented out old world-clock config while testing time-zone package above -;; (use-package time -;; :ensure nil ;; built-in -;; :defer 0.5 -;; :bind ("C-x c" . world-clock) -;; :config -;; (setq world-clock-list -;; '(("Pacific/Honolulu" " Honolulu") -;; ("America/Los_Angeles" " San Francisco, LA") -;; ("America/Chicago" " Chicago, New Orleans") -;; ("America/New_York" " New York, Boston") -;; ("Etc/UTC" " UTC =================") -;; ("Europe/London" " London, Lisbon") -;; ("Europe/Paris" " Paris, Berlin, Rome") -;; ("Europe/Athens" " Athens, Istanbul, Moscow") -;; ("Asia/Kolkata" " India") -;; ("Asia/Shanghai" " Shanghai, Singapore") -;; ("Asia/Tokyo" " Tokyo, Seoul"))) -;; (setq world-clock-time-format " %a, %d %b @ %I:%M %p %Z")) - (use-package calendar :ensure nil ;; built-in :defer 0.5 @@ -56,50 +36,16 @@ ;; ------------------------------------ TMR ------------------------------------ -(defun cj/tmr-select-sound-file () - "Select a sound file from `sounds-dir' to use for tmr timers. +(defconst cj/tmr--audio-extensions + '("mp3" "m4a" "ogg" "opus" "wav" "flac" "aac") + "Audio file extensions offered to `cj/tmr-select-sound-file'.") -Present all audio files in the sounds directory and set the chosen file as -`tmr-sound-file'. Use \\[universal-argument] to reset to the default sound." - (interactive) - (if current-prefix-arg - ;; With prefix arg, reset to default - (progn - (setq tmr-sound-file notification-sound) - (message "Timer sound reset to default: %s" - (file-name-nondirectory notification-sound))) - ;; Otherwise, select a new sound - (let* ((audio-extensions '("mp3" "m4a" "ogg" "opus" "wav" "flac" "aac")) - (extension-regex (concat "\\." (regexp-opt audio-extensions t) "$")) - (sound-files (when (file-directory-p sounds-dir) - (directory-files sounds-dir nil extension-regex))) - (current-file (when (and tmr-sound-file (file-exists-p tmr-sound-file)) - (file-name-nondirectory tmr-sound-file))) - (selected-file (when sound-files - (completing-read - (format "Select timer sound%s: " - (if current-file - (format " (current: %s)" current-file) - "")) - sound-files - nil - t - nil - nil - current-file)))) ; Default to current file - (cond - ((not (file-directory-p sounds-dir)) - (message "Sounds directory does not exist: %s" sounds-dir)) - ((null sound-files) - (message "No audio files found in %s" sounds-dir)) - (selected-file - (setq tmr-sound-file (expand-file-name selected-file sounds-dir)) - (when (equal tmr-sound-file notification-sound) - (message "Timer sound set to default: %s" selected-file)) - (unless (equal tmr-sound-file notification-sound) - (message "Timer sound set to: %s" selected-file))) - (t - (message "No file selected")))))) +(defun cj/tmr--available-sound-files () + "Return a list of audio filenames in `sounds-dir', or nil if none. +Returns nil if `sounds-dir' does not exist." + (when (and (boundp 'sounds-dir) (file-directory-p sounds-dir)) + (let ((regex (concat "\\." (regexp-opt cj/tmr--audio-extensions t) "$"))) + (directory-files sounds-dir nil regex)))) (defun cj/tmr-reset-sound-to-default () "Reset the tmr sound file to the default notification sound." @@ -108,6 +54,43 @@ Present all audio files in the sounds directory and set the chosen file as (message "Timer sound reset to default: %s" (file-name-nondirectory notification-sound))) +(defun cj/tmr-select-sound-file () + "Select a sound file from `sounds-dir' to use for tmr timers. + +Present all audio files in the sounds directory and set the chosen file as +`tmr-sound-file'. Use \\[universal-argument] to reset to the default sound." + (interactive) + (cond + (current-prefix-arg + (cj/tmr-reset-sound-to-default)) + ((not (and (boundp 'sounds-dir) (file-directory-p sounds-dir))) + (message "Sounds directory does not exist: %s" + (if (boundp 'sounds-dir) sounds-dir ""))) + (t + (let ((sound-files (cj/tmr--available-sound-files))) + (cond + ((null sound-files) + (message "No audio files found in %s" sounds-dir)) + (t + (let* ((current-file (when (and tmr-sound-file + (file-exists-p tmr-sound-file)) + (file-name-nondirectory tmr-sound-file))) + (selected-file + (completing-read + (format "Select timer sound%s: " + (if current-file + (format " (current: %s)" current-file) + "")) + sound-files nil t nil nil current-file))) + (cond + ((or (null selected-file) (string-empty-p selected-file)) + (message "No file selected")) + (t + (setq tmr-sound-file (expand-file-name selected-file sounds-dir)) + (if (equal tmr-sound-file notification-sound) + (message "Timer sound set to default: %s" selected-file) + (message "Timer sound set to: %s" selected-file))))))))))) + (use-package tmr :defer 0.5 :init diff --git a/modules/system-defaults.el b/modules/system-defaults.el index 6fbec003..d0b9f835 100644 --- a/modules/system-defaults.el +++ b/modules/system-defaults.el @@ -16,13 +16,14 @@ (require 'server) (require 'bookmark) -;; Loaded earlier in init.el +;; `host-environment' and `user-constants' are loaded earlier in init.el, +;; so they are available at runtime when this module loads. The +;; `eval-when-compile' forms here are byte-compile hints to silence +;; free-variable / free-function warnings when this module is compiled +;; in isolation; no runtime requires are needed. (eval-when-compile (require 'host-environment)) (eval-when-compile (require 'user-constants)) -;; Function in system-utils.el; autoload to avoid requiring it here. -(autoload 'env-bsd-p "host-environment" nil t) - ;; -------------------------- Native Comp Preferences -------------------------- (with-eval-after-load 'comp-run diff --git a/modules/user-constants.el b/modules/user-constants.el index 21e141a5..2cc4a50c 100644 --- a/modules/user-constants.el +++ b/modules/user-constants.el @@ -22,12 +22,15 @@ ;; -------------------------------- Debug Toggle ------------------------------- -(defvar cj/debug-modules nil +(defcustom cj/debug-modules nil "List of modules with debug functions enabled. -Possible values: org-agenda, mail, chime, etc. Set to t to enable all debug modules. -Example: (setq cj/debug-modules '(org-agenda mail)) - (setq cj/debug-modules t) ; Enable all") +Set to a list of module symbols (e.g. \\='(org-agenda mail)) to enable +debug output for those modules only. Possible values: org-agenda, +mail, chime, etc." + :type '(choice (const :tag "All modules" t) + (repeat :tag "Specific modules" symbol)) + :group 'cj) ;; -------------------------------- Contact Info ------------------------------- @@ -71,8 +74,17 @@ the regular transcription pipeline.") (defconst emacs-early-init-file (expand-file-name "early-init.el" user-emacs-directory) "The location of Emacs's early init file.") -(defconst user-home-dir (getenv "HOME") - "The user's home directory per the environment variable.") +;; Canonical definition of `user-home-dir' lives in `early-init.el' so +;; the package-archive paths there can reference it during package +;; bootstrap. The `defvar' below is a no-op at runtime (early-init's +;; defconst wins, defvar doesn't reassign a bound symbol) -- it exists +;; only so this module loads / byte-compiles standalone, when +;; early-init hasn't run. If you ever change the expression here, keep +;; it identical to early-init.el's. +(defvar user-home-dir (getenv "HOME") + "The user's home directory per the environment variable. +Canonical definition in early-init.el; this form is a standalone-load +fallback only.") (defconst books-dir (expand-file-name "sync/books/" user-home-dir) "The location of book files for CalibreDB.") -- cgit v1.2.3