diff options
| author | Craig Jennings <c@cjennings.net> | 2025-11-17 04:25:35 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-11-17 04:25:35 -0600 |
| commit | 4bdd4cfd4f00ef919cae5ee7de6cd7833448711d (patch) | |
| tree | f6f4bae789634594b1160c4361c5fbc5803eab86 /modules | |
| parent | 732bef47c133b2e3030a068afefdc1a1bcd31e54 (diff) | |
feat(calendar-sync): Make ICS fetching asynchronous
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.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/calendar-sync.el | 71 |
1 files changed, 42 insertions, 29 deletions
diff --git a/modules/calendar-sync.el b/modules/calendar-sync.el index 5af1977b..9fbfbeae 100644 --- a/modules/calendar-sync.el +++ b/modules/calendar-sync.el @@ -297,26 +297,37 @@ Events are sorted chronologically by start time." ;;; Sync functions -(defun calendar-sync--fetch-ics (url) - "Fetch .ics file from URL using curl. -Returns .ics content as string with normalized Unix line endings (LF only), or nil on error. -Uses curl instead of url-retrieve-synchronously to avoid daemon mode hanging." +(defun calendar-sync--fetch-ics (url callback) + "Fetch .ics file from URL asynchronously using curl. +Calls CALLBACK with the .ics content as string (normalized to Unix line endings) +or nil on error. CALLBACK signature: (lambda (content) ...). + +The fetch happens asynchronously and doesn't block Emacs. The callback is +invoked when the fetch completes, either successfully or with an error." (condition-case err - (with-temp-buffer - (let ((exit-code (call-process "curl" nil t nil - "-s" ; Silent - "-L" ; Follow redirects - "-m" "10" ; Max 10 seconds - url))) - (if (= exit-code 0) - (calendar-sync--normalize-line-endings (buffer-string)) - (setq calendar-sync--last-error (format "curl exited with code %d" exit-code)) - (message "calendar-sync: Fetch error: %s" calendar-sync--last-error) - nil))) + (let ((buffer (generate-new-buffer " *calendar-sync-curl*"))) + (make-process + :name "calendar-sync-curl" + :buffer buffer + :command (list "curl" "-s" "-L" "-m" "10" url) + :sentinel + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (with-current-buffer (process-buffer process) + (let ((content + (if (and (eq (process-status process) 'exit) + (= (process-exit-status process) 0)) + (calendar-sync--normalize-line-endings (buffer-string)) + (setq calendar-sync--last-error + (format "curl failed: %s" (string-trim event))) + (message "calendar-sync: Fetch error: %s" calendar-sync--last-error) + nil))) + (kill-buffer (process-buffer process)) + (funcall callback content))))))) (error (setq calendar-sync--last-error (error-message-string err)) (message "calendar-sync: Fetch error: %s" calendar-sync--last-error) - nil))) + (funcall callback nil)))) (defun calendar-sync--write-file (content) "Write CONTENT to `calendar-sync-file'. @@ -330,24 +341,26 @@ Creates parent directories if needed." ;;;###autoload (defun calendar-sync-now () - "Sync Google Calendar now. -Downloads .ics file and updates org file. + "Sync Google Calendar now asynchronously. +Downloads .ics file and updates org file without blocking Emacs. Tracks timezone for automatic re-sync on timezone changes." (interactive) (if (not calendar-sync-ics-url) (message "calendar-sync: Please set calendar-sync-ics-url") (message "calendar-sync: Syncing...") - (let* ((ics-content (calendar-sync--fetch-ics calendar-sync-ics-url)) - (org-content (and ics-content (calendar-sync--parse-ics ics-content)))) - (if org-content - (progn - (calendar-sync--write-file org-content) - (setq calendar-sync--last-sync-time (current-time)) - (setq calendar-sync--last-timezone-offset (calendar-sync--current-timezone-offset)) - (setq calendar-sync--last-error nil) - (calendar-sync--save-state) - (message "calendar-sync: Sync complete")) - (message "calendar-sync: Sync failed (see *Messages* for details)"))))) + (calendar-sync--fetch-ics + calendar-sync-ics-url + (lambda (ics-content) + (let ((org-content (and ics-content (calendar-sync--parse-ics ics-content)))) + (if org-content + (progn + (calendar-sync--write-file org-content) + (setq calendar-sync--last-sync-time (current-time)) + (setq calendar-sync--last-timezone-offset (calendar-sync--current-timezone-offset)) + (setq calendar-sync--last-error nil) + (calendar-sync--save-state) + (message "calendar-sync: Sync complete")) + (message "calendar-sync: Sync failed (see *Messages* for details)"))))))) ;;; Timer management |
