aboutsummaryrefslogtreecommitdiff
path: root/chime.el
Commit message (Collapse)AuthorAgeFilesLines
* refactor!: collapse three time-left format defcustoms into one alistCraig Jennings2026-05-051-33/+30
| | | | | | | | | | | | | | | | | | | | | | | | 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.
* refactor!: demote chime-validation-max-retries to private defvarCraig Jennings2026-05-051-19/+10
| | | | | | | | | | | | | | | | | | | | | This is an internal startup-timing parameter, not a knob real users have reason to tune through `M-x customize'. I demoted it from defcustom to defvar and renamed it to `chime--validation-max-retries' to make the private status explicit. Anyone who was overriding it can keep doing so with `setq' (the variable still exists, just under the new name). The three customize-time validation tests went away with the defcustom — nothing left to validate at customize-time once it stops being a customize-target. The setter helper still applies to the other five numeric defcustoms. Test files that referenced the variable (`test-chime-validation-retry.el', `test-integration-chime-mode.el') were renamed mechanically along with the source. Breaking change: `(setq chime-validation-max-retries N)' becomes `(setq chime--validation-max-retries N)' if you actually had it. Most users won't have touched it.
* refactor!: drop chime-play-sound, use chime-sound-file = nil to disableCraig Jennings2026-05-051-9/+2
| | | | | | | | | | | | | | | | | `chime-play-sound' was redundant with `chime-sound-file' — both had to be truthy for sound to play, and `chime-sound-file' already documented "set to nil to disable sound completely". Two knobs for one decision is a trap. I removed the defcustom and the `(when chime-play-sound ...)' guard in `chime--notify' (it now just checks `chime-sound-file'). Tests, README quick-start, and the troubleshooting checklist all reference the new single-knob form. One redundant test (sound-disabled-via-the-toggle) got dropped because it now duplicates the sound-file-nil case. Breaking change: users with `(setq chime-play-sound nil)' in their config will get a void-variable warning at customize time and lose their disable. Migration: `(setq chime-sound-file nil)'.
* fix: validate numeric defcustoms at customize-timeCraig Jennings2026-05-051-6/+42
| | | | | | | | | | | | | | | | | | | | | | | | | | Six numeric settings are declared as integers but were read straight into arithmetic and timer math. A bad value (string, negative number, nil where nil isn't supported) used to slip past the defcustom and surface as a timer error or `arith-error' deep in a callback, instead of as a configuration problem at the moment the user set it. I added `chime--validate-integer-setting' as a small shared helper and wired a `:set' on each of the affected defcustoms: - `chime-modeline-lookahead-minutes' — integer >= 0 (0 disables) - `chime-tooltip-lookahead-hours' — integer >= 1 - `chime-modeline-tooltip-max-events' — integer >= 1 or nil (show all) - `chime-day-wide-advance-notice' — integer >= 0 or nil (same-day only) - `chime-max-consecutive-failures' — integer >= 0 (0 disables warnings) - `chime-validation-max-retries' — integer >= 0 (0 = fail immediately) The constraints follow each docstring's stated intent. The helper signals `user-error', so `customize-set-variable' surfaces it as a config problem rather than a generic error trace. Tests: 22 cases in `tests/test-chime-numeric-defcustom-setters.el' — five direct on the helper plus each defcustom's accept/reject paths through `customize-set-variable'.
* fix: stop tooltip dedup from hiding distinct events sharing a titleCraig Jennings2026-05-051-14/+23
| | | | | | | | | | | | | | | | | `chime--deduplicate-events-by-title' was keying off the user-facing title alone, so two real "1:1" entries on different days collapsed to one and the second one disappeared from the tooltip. I changed the key to `(marker-file . marker-pos)' — the source heading identity — so distinct headings keep both copies and recurring expansions of one heading still fold to the soonest. When marker info is missing (synthesized test events, edge cases) the key falls back to title, which preserves the older fixtures the function was originally tested against. Three new tests cover the regression paths: two distinct headings sharing a title both survive, one heading expanded into multiple instances still collapses to the soonest, and a mix of sourced and unsourced events behaves correctly under both code paths.
* refactor: clear the rest of the lint backlog and make `make lint' actually workCraig Jennings2026-05-051-18/+18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Took the package-lint commit's foundation and finished the job. Three groups of changes: 1. **checkdoc** is now clean across all three package files. I worked through every warning: 13 missing-double-space-after-period fixes, 4 message-capitalization fixes, 1 trailing-period-on-error fix, 6 Lisp-symbol-should-be-quoted fixes, and one rephrase from "starts" to the imperative "Log the start of an async check". One `chime-debug.el' docstring also exceeded 80 columns and got split into two lines. 2. **`make lint'** ran elisp-lint but had been silently broken. The shell glob `test-*.el' wasn't expanding (CWD ended up at project root, not tests/), and the noisy validators flagged everything. I rewrote the target to lint package files explicitly via absolute paths, preload chime.el so the byte-compiler sees cross-file symbols when checking chime-debug.el, and disable the validators that conflict with project style or duplicate other lint steps: - `--no-checkdoc' (covered by `eask lint checkdoc') - `--no-package-lint' (covered by `eask lint package'; running it on auxiliary files re-flags them as standalone packages) - `--no-indent-character' (project uses spaces per `.claude/rules/elisp.md'; the validator defaults to tabs) - `--no-fill-column' (project allows up to 80; validator defaults to 70) - `--no-indent' (false-positives on dash threading macros `->'/`->>') The validators-disabled list is documented in the recipe header. 3. **Generated autoload files** (`chime-autoloads.el', `tests/tests-autoloads.el') are now gitignored. `tests/tests-autoloads.el' was tracked from an earlier commit; I removed it from the index. Eask regenerates these on every `eask compile' / `eask install-deps' run, so they don't belong in the tree. Verified: `make compile' clean (with byte-compile-error-on-warn t), `make test-all' green at 677 tests, `eask lint package' clean, `eask lint checkdoc' clean, `make lint' clean. Full lint backlog is now zero across all three checks.
* refactor: clear package-lint warnings for MELPA prepCraig Jennings2026-05-051-13/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I worked through the package-lint backlog and got it to zero. Five changes: 1. Renamed six interactive debug commands from the private `chime--debug-*' prefix to the public `chime-debug-*' form. They were always M-x targets, so the private prefix was just wrong. The autoload cookies stay because public commands SHOULD be autoloaded. README, docstring references in chime.el, and the matching tests follow the rename. 2. Dropped `Version', `Package-Requires', and `Keywords' headers from chime-org-contacts.el. Auxiliary files in a multi-file package shouldn't carry their own metadata — package-lint flags it as an error because the headers have no effect outside the main file. The main file (chime.el) already declares the chime package's metadata. 3. Dropped `Keywords' from chime-debug.el for the same reason. 4. Dropped the auto-loader for the optional chime-org-contacts integration from chime.el. The old code used `with-eval-after-load 'org-capture' to pull the file in, which package-lint flags as a configuration pattern that doesn't belong in a package. Users who want the integration now require it themselves; the README shows both the plain `with-eval-after-load' pattern and the `use-package :after' form. chime-org-contacts.el's internal `with-eval-after-load' went away too — by the time the user has required the file, they've already gated it on org-capture being loaded, so the inner check is redundant. 5. Dropped the redundant `with-eval-after-load' from chime-org-contacts.el's activation block. The setup function still guards on `(boundp 'org-capture-templates)' so it's safe to require either order. Behavioral note: this is a small breaking change for anyone whose config relied on the auto-load. The README change spells out the migration path.
* chore: bump version to 0.7.0v0.7.0Craig Jennings2026-05-051-1/+1
| | | | | Header version in chime.el and the Eask manifest both move from 0.6.0 to 0.7.0 for the v0.7.0 release.
* fix: skip declined events in tooltip, modeline, and notificationsCraig Jennings2026-05-051-1/+10
| | | | | | | | | | | | | | | | | | org-gcal writes `:STATUS: declined' on calendar entries the user has declined, and chime was happily showing them in the tooltip, modeline, and notification stream — which defeats the whole point of declining a meeting. I added `chime-declined-events-predicate', mirroring the shape of `chime-done-keywords-predicate', and put it on the default `chime-predicate-blacklist'. The match is on the literal lowercase value because that's what real org-gcal exports use; uppercase or other values pass through. Users who want declined events back can pop the predicate off the blacklist. Tests cover all four STATUS values seen in real org-gcal data (accepted, declined, needs-action, tentative), plus the no-property, empty-property, todo-keyword-still-attached, gibberish-value, case-sensitivity, and default-blacklist-membership cases.
* chore: drop non-standard keywords from chime.el headerCraig Jennings2026-05-051-1/+1
| | | | | | Eask flagged `agenda`, `chime`, and `sound` as unknown finder keywords. The Eask manifest already lists the canonical set (notification, alert, org, org-agenda, calendar), so I trimmed the in-file header to match.
* fix: remove unused `title` lexical in chime--process-notificationsCraig Jennings2026-05-051-2/+1
| | | | | | | `make compile' flagged it. The variable was bound but never referenced in the bundled-notification branch, so I dropped the `let' binding. Whether the bundled notification should actually carry that title is a separate concern, not this fix.
* fix: trim chime--find-soonest-modeline-event docstring to <=80 colsCraig Jennings2026-05-051-2/+3
| | | | | | `make compile' flagged the summary line at 86 chars. I split it across two lines, with the lookahead clause and the return-value sentence on the lines after.
* refactor: rewrite chime--time-left without threading-pcaseCraig Jennings2026-05-051-7/+9
| | | | | | | | Edebug's defun parser rejects the `(-> seconds (pcase ...) ...)' form, so undercover can't instrument chime.el and `make coverage' produces nothing. The let-bound rewrite is equivalent and parses cleanly. I left an inline note so the form doesn't get folded back later.
* refactor: move 12 internal helpers from chime- to chime-- prefixCraig Jennings2026-04-221-26/+26
| | | | | | | | | | Before MELPA submission, tighten the public API surface. Single-dash chime-foo is the convention for user-facing commands and predicates that external code can bind. Anything that is not meant to be bound externally should use the double-dash chime--foo prefix so byte-compile warnings, docstrings, and MELPA package inspection all point in the same direction. Twelve helpers in chime.el had single-dash names but no user-facing role. They are not mentioned in README, they carry no interactive declaration, and their docstrings do not promise a behavior contract. Rename them: chime-get-minutes-into-day, chime-get-hours-minutes-from-time, chime-set-hours-minutes-for-time, chime-current-time-matches-time-of-day-string, chime-current-time-is-day-wide-time, chime-day-wide-notifications, chime-display-as-day-wide-event, chime-event-has-any-day-wide-timestamp, chime-event-within-advance-notice-window, chime-event-has-any-passed-time, chime-event-is-today, chime-environment-regex. The public API surface is now intentional. It covers chime-mode, chime-validate-configuration, chime-refresh-modeline, chime-done-keywords-predicate, and the chime-debug-* interactive commands in chime-debug.el. Breaking change. No alias shims because there are no downstream users yet.
* refactor: extract chime--day-label-for-event-time from ↵Craig Jennings2026-04-221-29/+30
| | | | | | | | chime--group-events-by-day The date-string computation mixed the today/tomorrow/weekday label selection in with the grouping loop and carried its own pre-decoded copies of NOW and TOMORROW through five let-bindings before it could decide which branch to take. Lift the selection into chime--day-label-for-event-time (event-time now tomorrow). The outer function becomes pure grouping, the label logic gets its own test target, and future label tweaks (localized strings, a "Yesterday" branch for overdue items) edit one helper. The outer function drops from 36 to 15 lines. The redundant (when event-decoded) guard is replaced by (when event-time) because decode-time always returns a struct for valid time values.
* refactor: extract async result helpers from chime--fetch-and-processCraig Jennings2026-04-221-27/+26
| | | | | | chime--fetch-and-process inlined two near-identical 7-line failure-handling blocks inside the async callback. They differed only in the log prefix ("Async error" for errors surfaced by the async process, "Error processing events" for errors thrown by the callback). Lift the shared body into chime--record-async-failure (err prefix) so the sequence lives in one place, and lift the success path into chime--handle-async-success (callback events) so the callback lambda becomes pure dispatch. The outer function drops from 44 lines to 22.
* refactor: extract chime--render-modeline-string from chime--update-modelineCraig Jennings2026-04-211-25/+30
| | | | | | | | chime--update-modeline grew to mix orchestration with the modeline-string formation logic. Extract the formation block into its own helper so the orchestrator reads as pure orchestration: build upcoming, find soonest, render, force update. The helper handles both branches. When a soonest event is in the modeline window, format and propertize via chime--propertize-modeline-string. Otherwise, when chime-modeline-no-events-text is set, propertize that with the appropriate tooltip and click handlers (mouse-1 always, mouse-3 only when there are upcoming events to jump to). The orchestrator drops from 45 to 25 lines.
* Remove unused chime--today functionCraig Jennings2026-04-041-4/+0
| | | | Defined and tested but never called from any production code.
* Bundle multiple day-wide events into a single notificationCraig Jennings2026-04-041-2/+10
| | | | | | | | | | When all-day event alert times trigger, multiple events (overdue tasks, birthdays, holidays) previously fired individual notifications — one sound and one popup each. With 8-10 events this was overwhelming. Now multiple day-wide events are bundled into a single notification with all messages joined by newlines. Single events still notify normally without bundling.
* Show modeline icon immediately on chime-mode enableCraig Jennings2026-04-041-11/+47
| | | | | | | | | Previously chime-modeline-string was nil until the first async check completed (~10-15 seconds after startup). Now the icon appears instantly with a "waiting for first event check" tooltip. On validation or async failure, the icon stays visible and the tooltip updates to show the error state instead of going blank.
* Default modeline lighter to empty stringCraig Jennings2026-04-041-4/+5
| | | | | | The bell emoji lighter was redundant alongside the alarm clock event display in global-mode-string. One icon is enough. Users who want a separate mode indicator can set chime-modeline-lighter.
* Improve defaults: 2-hour modeline window, notify at event timeCraig Jennings2026-04-041-2/+2
| | | | | | | | | | Bump chime-modeline-lookahead-minutes from 60 to 120 so events within the next 2 hours show in the modeline. The old 60-minute default hid events most users would expect to see. Add (0 . high) to default chime-alert-intervals so users get a notification at event time in addition to 10 minutes before. The single-notification default meant a missed alert had no backup.
* Add comments for non-obvious code patternsCraig Jennings2026-04-041-7/+15
| | | | | | | | Clarify: dash cartesian product pipeline in chime--notifications, butlast 2 dropping USEC/PSEC from time values, Emacs time type guard, upcoming-events item structure, rx macroexpand for async env injection, backquoted lambda subprocess boundary, and org-end-of-meta-data nil argument meaning.
* Remove dead code and reduce default tooltip lookahead to 1 weekCraig Jennings2026-04-041-7/+6
| | | | | | | | | | | Remove chime--agenda-buffer-name (unused variable) and chime--extract-birthday-year (superseded by chime--parse-birthday). Reduce chime-tooltip-lookahead-hours from 8760 (1 year) to 168 (1 week). The 1-year default caused org-agenda-list to scan a 365-day span every check cycle, which is slow for large org collections. The tooltip only shows 5 events, so a week is sufficient for most users.
* Hoist loop-invariant computations and remove redundant sortCraig Jennings2026-04-041-68/+53
| | | | | | | | | Performance improvements with no behavioral change: - chime--group-events-by-day: hoist decode-time calls before loop - chime-event-has-any-passed-time: compute today-start once, not per timestamp - chime-event-is-today: same pattern - chime--build-upcoming-events-list: remove first sort (discarded by hash dedup) - chime--process-notifications: use -mapcat instead of -map + -flatten
* Nil chime--timer after cancellation in chime--stopCraig Jennings2026-04-041-0/+1
| | | | | The timer variable was left pointing at a cancelled timer object. Now set to nil so state accurately reflects no active timer.
* Extract days-until calculation from chime--day-wide-notification-textCraig Jennings2026-04-041-27/+22
| | | | | | Move the all-day timestamp date arithmetic into chime--days-until-event. The notification text function now reads clearly as a cond dispatch over today/advance-notice/fallback cases.
* Extract validation retry logic from chime-check into chime--maybe-validateCraig Jennings2026-04-041-26/+25
| | | | | | The validation/retry state machine was 30 lines embedded in chime-check. Now chime--maybe-validate returns t if OK to proceed, nil to skip. chime-check is reduced to 12 lines: validate, then fetch-and-process.
* Extract display logic from chime-validate-configurationCraig Jennings2026-04-041-14/+14
| | | | | | Move interactive display (message/display-warning calls) into chime--display-validation-results. The validation function stays focused on collecting issues; display is a separate concern.
* Extract three timestamp strategies from chime--extract-timeCraig Jennings2026-04-041-54/+59
| | | | | | | | | Decompose the 72-line function into focused helpers: - chime--extract-gcal-timestamps: org-gcal drawer extraction - chime--extract-property-timestamps: SCHEDULED/DEADLINE properties - chime--extract-plain-timestamps: plain body timestamps chime--extract-time is now a 12-line dispatcher.
* Add 94 tests across 10 new test files and fix 2 bugsCraig Jennings2026-04-041-7/+8
| | | | | | | | | | | | New test coverage for previously untested functions: - filter-day-wide-events, time utilities, day-wide time matching - make-tooltip, event-is-today, get-tags, done-keywords-predicate - extract-title, propertize-modeline-string, warn-persistent-failures - log-silently Bug fixes discovered by new tests: - Fix pluralization in no-events tooltip ("1 hours" -> "1 hour", etc.) - Fix chime--get-tags returning ("") instead of nil for untagged headings
* Add persistent async failure warnings after consecutive errorsCraig Jennings2026-02-231-2/+30
| | | | | | Tracks consecutive async check failures and displays a warning via display-warning when the threshold is reached (default 5). Resets on any successful check.
* Use locate-library fallback for sound file path resolutionCraig Jennings2026-02-231-1/+4
| | | | | Adds locate-library as a fallback between load-file-name and buffer-file-name for more robust path resolution in MELPA installs.
* Improve org-agenda-files validation to distinguish files from directoriesCraig Jennings2026-02-231-7/+10
|
* Include event title in timestamp parse error messagesCraig Jennings2026-02-231-8/+13
| | | | | | | Add optional context parameter to chime--timestamp-parse and pass the org heading title from chime--extract-time. Error messages now show which event failed, e.g. "Failed to parse timestamp '...' in 'Team Meeting': ..."
* Replace anonymous lambda in modeline keymap with named functionCraig Jennings2026-02-231-8/+4
| | | | | | Use chime--jump-to-first-event instead of a closure capturing soonest-event. Improves debuggability and removes unused parameter from chime--propertize-modeline-string.
* Fix docstring grammar and remove double blank lineCraig Jennings2026-02-231-2/+1
|
* Add missing declare-function for chime-debug functionsCraig Jennings2026-02-231-0/+3
| | | | | Declare chime-debug-enable-async-monitoring, chime--debug-log-async-error, and chime--debug-log-async-complete to suppress byte-compiler warnings.
* Fix defcustom :type specs for list and nullable variablesCraig Jennings2026-02-231-4/+5
| | | | | | | - chime-predicate-whitelist: (function) -> (repeat function) - chime-predicate-blacklist: (function) -> (repeat function) - chime-additional-environment-regexes: (string) -> (repeat string) - chime-notification-icon: string -> choice of nil or file path
* Bump minimum Emacs version to 27.1 and require subr-xCraig Jennings2026-02-231-3/+4
| | | | | | Package uses decoded-time-* accessors, time-equal-p (27.1+), and subr-x functions (when-let*, hash-table-values, string-empty-p). Remove stale "Emacs 26 compatibility" comments.
* Update README and fix repo references after rename to chimeCraig Jennings2026-02-221-3/+3
| | | | | | | | | Fix backronym to "CHIME Heralds Imminent Modeline Events" across all files. Update GitHub URLs from chime.el to chime. Add documentation for missing customization variables (notification icon, startup delay, modeline lighter, no-events text, tooltip header format, advance notice, tooltip all-day events). Fix test count (505) and default for chime-day-wide-alert-times.
* Add section headers to chime.el for imenu/outline navigationCraig Jennings2026-02-191-0/+28
| | | | | | | | | 14 ;;;; headers added for code navigation: Dependencies, Customization Variables, Internal State, Time/Date Utilities, All-Day Event Handling, Event Checking & Navigation, Modeline & Tooltip Display, Whitelist/Blacklist Filtering, Async Event Retrieval, Notification Dispatch, Timestamp Parsing, Event Info Extraction, Configuration Validation, Core Lifecycle. Comments only — zero runtime impact.
* Update copyright years to 2026 across all source filesCraig Jennings2026-02-171-1/+1
| | | | | Updated 41 files: chime.el, chime-debug.el, chime-org-contacts.el, convert-org-contacts-birthdays.el, and all test/utility files.
* Fix time comparison bugs in chime--time= and chime--timestamp-within-interval-pCraig Jennings2026-02-171-3/+3
| | | | | | | chime--time= was using %d:%H:%M (day-of-month only), causing false matches across different months/years. Now uses %Y-%m-%d %H:%M for full date comparison. chime--timestamp-within-interval-p now accepts numeric timestamps (integer/float) in addition to lists.
* changed repositoriesCraig Jennings2025-11-181-0/+1825