aboutsummaryrefslogtreecommitdiff
path: root/modules/calendar-sync.el
Commit message (Collapse)AuthorAgeFilesLines
* Make calendar sync startup safe without configCraig Jennings2026-05-041-14/+26
|
* fix: sanitize calendar event headings and property valuesCraig Jennings2026-05-031-5/+28
| | | | | | | | | | `calendar-sync--event-to-org` already cleaned the description body via `calendar-sync--sanitize-org-body`, but the event summary went into the heading line and the location, organizer, status, and URL went into the property drawer without sanitization. Any of those fields containing newlines could create extra Org headings, close the property drawer early with a stray `:END:`, or inject property-looking lines that the agenda would then parse as real properties. I added two helpers. `calendar-sync--sanitize-org-property-value` trims the input and collapses any run of whitespace or newlines into a single space. `calendar-sync--sanitize-org-heading` composes that over the existing body sanitizer so `*` sequences also become `-`. The event-to-org function now routes the summary through the heading sanitizer and each property value through the property sanitizer. I added regression tests across two files. `test-calendar-sync--sanitize-org-body.el` gets 4 new tests for the two helpers, covering newline flattening, leading-star replacement, structural-character flattening, and whitespace collapse. `test-calendar-sync--event-to-org.el` gets 2 new integration tests. A summary containing `\n** Hidden task` produces a single `* ` heading with the body inlined. A location containing `\n:END:\n* Not a real heading` collapses to a single property line with no extra `:END:` or heading injected. 515 calendar-sync tests pass together.
* refactor(calendar-sync): extract require-calendars guard from 4 locationsCraig Jennings2026-04-051-8/+10
| | | | | Replaced 4 copies of "if null calendars, warn" with a shared calendar-sync--require-calendars predicate.
* refactor(calendar-sync): extract RFC 5545 continuation line unfoldingCraig Jennings2026-04-051-17/+17
| | | | | Deduplicated the folded-line handling loop from get-property and get-all-property-lines into calendar-sync--unfold-continuation.
* refactor(calendar-sync): extract CN and email parsing from duplicated codeCraig Jennings2026-04-051-25/+19
| | | | | Extracted calendar-sync--extract-cn and calendar-sync--extract-email from identical logic in parse-attendee-line and parse-organizer.
* refactor(calendar-sync): remove dead function calendar-sync--timezone-nameCraig Jennings2026-04-051-5/+0
| | | | Function was defined but never called anywhere in the codebase.
* feat(music): add random-aware next/previous; refactor music + calendar-syncCraig Jennings2026-04-031-159/+69
| | | | | | | | | | Music: random mode now respected by next/previous keys. Previous navigates a 50-track play history ring buffer. Fixed playlist replacement bug. 24 new tests. Calendar-sync: consolidated duplicate parse functions, extracted timezone localization helper, unified expand-daily/monthly/yearly into parameterized function, removed dead code. 33 new characterization tests. -90 lines.
* fix(calendar-sync): handle variable-length date lists in RRULE UNTILCraig Jennings2026-03-091-3/+7
| | | | | | | date-to-time used (reverse date) which broke when RRULE UNTIL values were parsed as 5-element lists (year month day hour minute) from UTC timestamps. This caused recurring events with UTC UNTIL dates to expand to 0 occurrences, producing stale calendar entries.
* chore: rename chime.el references to ChimeCraig Jennings2026-02-231-1/+1
| | | | | Update load-path, GitHub URL, and all project/package name references to reflect the chime.el → Chime rename.
* perf(calendar-sync): replace shell-out timezone conversion with pure ElispCraig Jennings2026-02-141-32/+60
| | | | | | | | | convert-tz-to-local was spawning a `date` subprocess per timestamp, causing ~15s parse freezes on Proton (223 events) and ~10s on Google (1543 events). Replaced with encode-time/decode-time ZONE argument — same TZ database, no subprocess overhead. Parse times now 0.2-2.8s. Also adds phase timing instrumentation behind cj/debug-modules flag.
* fix(calendar-sync): increase fetch timeout for large calendarsCraig Jennings2026-02-061-1/+11
| | | | | | Google calendar (7k+ events, 4.5MB) was hitting the 30s hard-coded curl timeout. Use configurable calendar-sync-fetch-timeout (default 120s) with a separate 10s connect-timeout for fast failure on unreachable hosts.
* fix(calendar-sync): sanitize description text to prevent org heading corruptionCraig Jennings2026-02-061-2/+13
| | | | | | Event descriptions containing lines starting with * were being interpreted as org headings, breaking file structure. Replace leading asterisks with dashes before writing descriptions.
* fix(calendar-sync): fix heading order, continuation lines, and exception ↵Craig Jennings2026-02-051-6/+10
| | | | | | | | | | | | | | | | | | text cleaning Three bugs found during manual verification of calendar sync output: 1. Heading/timestamp order reversed in event-to-org — nreverse pattern put timestamp before the org heading. Swap initial list order. 2. get-property continuation line regex broken — the ^ anchor in "^\n[ \t]" prevented matching at the correct position, truncating long DESCRIPTION values at the first ICS line fold. Remove anchor, add explicit position check (matching get-all-property-lines pattern). 3. collect-recurrence-exceptions didn't clean text — exception instances got raw ICS text (literal \n, HTML tags) replacing the cleaned base event text. Wrap summary/description/location in clean-text.
* feat(calendar-sync): add event details — attendees, organizer, status, URLCraig Jennings2026-02-051-16/+208
| | | | | | | | Add ICS text unescaping (RFC 5545), HTML stripping, and new fields (attendees/status, organizer, meeting URL) to calendar-sync.el. event-to-org now outputs org property drawers. 88 new tests across 10 test files, 146/146 pass. Also fix pre-existing test require order and keymap guard issues.
* refactor(calendar): move calendar URLs into calendar-sync.elCraig Jennings2026-02-041-0/+12
| | | | | Consolidate calendar configuration within the module itself rather than requiring setup in init.el. Improves module encapsulation.
* feat(calendar-sync): add EXDATE support for excluded recurring event datesCraig Jennings2026-02-031-10/+135
| | | | | | | | | | | | | | | | | | | | | When someone deletes a single instance of a recurring meeting in Google Calendar, the calendar exports an EXDATE property marking that date as excluded. Previously, calendar-sync expanded the RRULE without filtering out these excluded dates, causing deleted instances to appear in org output. New functions: - calendar-sync--get-exdates: Extract all EXDATE values from event - calendar-sync--get-exdate-line: Get full EXDATE line with parameters - calendar-sync--parse-exdate: Parse EXDATE into datetime list - calendar-sync--collect-exdates: Collect excluded dates with TZ conversion - calendar-sync--exdate-matches-p: Check if occurrence matches an EXDATE - calendar-sync--filter-exdates: Filter out excluded dates from occurrences Modified calendar-sync--expand-recurring-event to collect and filter EXDATEs after RRULE expansion. Includes 47 new tests covering extraction, parsing, collection, filtering, and integration with RECURRENCE-ID exceptions.
* feat(calendar-sync): add RECURRENCE-ID exception handling for recurring eventsCraig Jennings2026-02-031-10/+178
| | | | | | | | | | | | | | | | | | | | | Handle rescheduled instances of recurring calendar events by processing RECURRENCE-ID properties from ICS files. When someone reschedules a single instance of a recurring meeting in Google Calendar, the calendar-sync module now shows the rescheduled time instead of the original RRULE time. New functions: - calendar-sync--get-recurrence-id: Extract RECURRENCE-ID from event - calendar-sync--get-recurrence-id-line: Get full line with TZID params - calendar-sync--parse-recurrence-id: Parse into (year month day hour minute) - calendar-sync--collect-recurrence-exceptions: Collect all exceptions by UID - calendar-sync--occurrence-matches-exception-p: Match occurrences to exceptions - calendar-sync--apply-single-exception: Apply exception data to occurrence - calendar-sync--apply-recurrence-exceptions: Apply all exceptions to occurrences Also adds DeepSat calendar configuration (dcal-file) to user-constants, init.el, and org-agenda-config. 48 unit and integration tests added covering normal, boundary, and error cases.
* feat(calendar-sync): add timezone conversion for TZID-qualified eventsCraig Jennings2026-02-011-12/+79
| | | | | | | | | | | | Events with TZID parameters (e.g., DTSTART;TZID=Europe/Lisbon) were displaying in the source timezone instead of local time. Added: - calendar-sync--extract-tzid: extracts TZID from property lines - calendar-sync--convert-tz-to-local: converts using date command - Modified parse-timestamp to accept optional TZID parameter - Modified parse-event to extract and pass TZID through pipeline Includes 40 new tests covering extraction, conversion, and integration.
* feat(calendar-sync): re-enable auto-sync on startupCraig Jennings2026-01-271-1/+1
| | | | Freeze bugs are fixed; safe to auto-sync again.
* fix(calendar-sync): resolve freeze on DST transitions and large ICS filesCraig Jennings2026-01-271-7/+13
| | | | | | | | | | | Two bugs caused Emacs to freeze during calendar sync: 1. split-events used catastrophic regex (\(.\|\n\)*?) on multi-MB ICS data. Replaced with buffer-based search-forward (0.011s for 4.5MB). 2. add-days used midnight for date arithmetic. On DST fall-back days, adding 86400s to midnight CDT yields 11pm CST (same date), creating an infinite loop. Fixed by using noon so ±1h DST shift stays correct.
* fix(calendar-sync): disable auto-start to prevent freezeCraig Jennings2026-01-141-1/+1
| | | | | | Proton calendar download causes Emacs to freeze. Disabled auto-sync by default until root cause is investigated. Manual sync still available via C-; g s keybinding.
* feat(calendar-sync): multi-calendar support with property testsCraig Jennings2025-12-021-73/+179
| | | | | | Added multi-URL calendar sync supporting Google and Proton calendars. Each calendar syncs to separate file with per-calendar state tracking. Added 13 property-based tests for RRULE expansion. Total: 150 tests passing.
* fix: add quick-sdcv quit binding and fix calendar-sync sentinelCraig Jennings2025-11-211-11/+13
| | | | | | | - Add 'q' keybinding in quick-sdcv-mode to quit-window for easier dictionary dismissal while reading epubs - Fix "Selecting deleted buffer" error in calendar-sync by checking buffer-live-p before accessing process buffer in sentinel
* feat(calendar-sync): Add RRULE support and refactor expansion functionsCraig Jennings2025-11-181-35/+385
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Implements complete recurring event (RRULE) expansion for Google Calendar with rolling window approach and comprehensive test coverage. Features: - RRULE expansion for DAILY, WEEKLY, MONTHLY, YEARLY frequencies - Support for INTERVAL, BYDAY, UNTIL, and COUNT parameters - Rolling window: -3 months to +12 months from current date - Fixed COUNT parameter bug (events no longer appear beyond their limit) - Fixed TZID parameter parsing (supports timezone-specific timestamps) - Replaced debug messages with cj/log-silently Refactoring: - Extracted helper functions to eliminate code duplication: - calendar-sync--date-to-time: Date to time conversion - calendar-sync--before-date-p: Date comparison - calendar-sync--create-occurrence: Event occurrence creation - Refactored all expansion functions to use helper functions - Reduced code duplication across daily/weekly/monthly/yearly expansion Testing: - 68 tests total across 5 test files - Unit tests for RRULE parsing, property extraction, weekly expansion - Integration tests for complete RRULE workflow - Tests for helper functions validating refactored code - All tests passing
* fix: increase calendar-sync curl timeout from 10 to 30 secondsCraig Jennings2025-11-171-1/+1
| | | | | | 10-second timeout was too aggressive for slower networks or delayed Google servers. Increased to 30 seconds to prevent timeout errors while still preventing indefinite hangs.
* feat(calendar-sync): Make ICS fetching asynchronousCraig Jennings2025-11-171-29/+42
| | | | | | | | | | | | | | Changed calendar-sync--fetch-ics from synchronous call-process to asynchronous make-process with callback pattern. This prevents Emacs from freezing during calendar syncs. Changes: - calendar-sync--fetch-ics now takes a callback parameter - Uses make-process with sentinel for async completion - calendar-sync-now updated to use callback pattern - Fetch completes in background without blocking Emacs All 56 tests pass. User confirmed improved responsiveness.
* chore(calendar-sync): Change default interval to 60 minutesCraig Jennings2025-11-171-3/+3
| | | | | | | | | | Changed default sync interval from 15 minutes to 60 minutes (1 hour). Rationale: - Calendar events typically don't change that frequently - Reduces network requests and potential blocking events - Users can still manually sync or adjust interval as needed - More conservative default that balances freshness with performance
* refactor(calendar-sync): Make interval configurable in minutesCraig Jennings2025-11-171-11/+12
| | | | | | | | | | | | | | | | | | | Changed calendar-sync-interval (seconds) to calendar-sync-interval-minutes for more user-friendly configuration. Changes: - Renamed: calendar-sync-interval → calendar-sync-interval-minutes - Units: seconds → minutes (default: 15) - Internal conversion to seconds happens in calendar-sync-start - Updated docstrings and messages to reference minutes Benefits: - More intuitive configuration (users think in minutes, not seconds) - Clearer variable name indicates units - No functional change, just better UX Example usage: (setq calendar-sync-interval-minutes 30) ; Sync every 30 minutes
* feat(calendar-sync): Add auto-start toggle and convert to defvarCraig Jennings2025-11-171-20/+17
| | | | | | | | | | | | | | | | | | | | | | | | Changes: 1. Converted defcustom → defvar (3 variables): - calendar-sync-ics-url - calendar-sync-interval - calendar-sync-file - Removed defgroup (not using customize system) 2. Added calendar-sync-auto-start variable (default: t) - Controls whether auto-sync starts when module loads - Set to nil to disable automatic startup 3. Updated initialization to check auto-start flag - Auto-sync only starts if both auto-start and URL are non-nil - Provides user control over automatic behavior Rationale: - Auto-sync is convenient but should be toggleable - defvar is simpler and more direct than defcustom - Consistent with project style (no customize interface) Default behavior: Auto-sync enabled (backwards compatible with user expectation)
* fix(calendar-sync): Remove carriage return characters from synced eventsCraig Jennings2025-11-171-2/+16
| | | | | | | | | | | | | | | | | | | | | | | Problem: Google Calendar .ics files use CRLF line endings (RFC 5545 spec), which resulted in 11,685 ^M (CR) characters appearing in gcal.org, particularly at the end of org header lines. Solution: - Created calendar-sync--normalize-line-endings function to strip all \r characters from .ics content - Integrated into calendar-sync--fetch-ics immediately after curl download - Ensures clean Unix LF-only line endings throughout parsing pipeline Testing: - Added comprehensive test suite: test-calendar-sync--normalize-line-endings.el - 16 tests covering Normal, Boundary, and Error cases - All 56 existing calendar-sync tests still pass (no regressions) - Verified: gcal.org now has 0 CR characters (was 11,685) Files modified: - modules/calendar-sync.el: Added normalize function, updated fetch function - tests/test-calendar-sync--normalize-line-endings.el: New comprehensive test suite
* feat(calendar-sync): Add automatic timezone detection and chronological sortingCraig Jennings2025-11-161-0/+438
Implemented calendar-sync.el as a complete replacement for org-gcal, featuring: **Core Functionality:** - One-way sync from Google Calendar to Org (via .ics URL) - UTC to local timezone conversion for all event timestamps - Chronological event sorting (past → present → future) - Non-blocking sync using curl (works reliably in daemon mode) **Automatic Timezone Detection:** - Detects timezone changes when traveling between timezones - Tracks timezone offset in seconds (-21600 for CST, -28800 for PST, etc.) - Triggers automatic re-sync when timezone changes detected - Shows informative messages: "Timezone change detected (UTC-6 → UTC-8)" **State Persistence:** - Saves sync state to ~/.emacs.d/data/calendar-sync-state.el - Persists timezone and last sync time across Emacs sessions - Enables detection even after closing Emacs before traveling **User Features:** - Interactive commands: calendar-sync-now, calendar-sync-start/stop - Keybindings: C-; g s (sync), C-; g a (start auto-sync), C-; g x (stop) - Optional auto-sync every 15 minutes (disabled by default) - Clear status messages for all operations **Code Quality:** - Comprehensive test coverage: 51 ERT tests (100% passing) - Refactored UTC conversion into separate function - Clean separation of concerns (parsing, conversion, formatting, sorting) - Well-documented with timezone behavior guide and changelog **Migration:** - Removed org-gcal-config.el (archived in modules/archived/) - Updated init.el to use calendar-sync - Moved gcal.org to .emacs.d/data/ for machine-independent syncing - Removed org-gcal appointment capture template Files modified: modules/calendar-sync.el:442, tests/test-calendar-sync.el:577 Files created: data/calendar-sync-state.el, tests/testutil-calendar-sync.el Documentation: docs/calendar-sync-timezones.md, docs/calendar-sync-changelog.md