diff options
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/calendar-sync.el | 92 | ||||
| -rw-r--r-- | modules/dashboard-config.el | 6 | ||||
| -rw-r--r-- | modules/music-config.el | 72 |
3 files changed, 124 insertions, 46 deletions
diff --git a/modules/calendar-sync.el b/modules/calendar-sync.el index 6753e5fe..fb1c0f22 100644 --- a/modules/calendar-sync.el +++ b/modules/calendar-sync.el @@ -818,27 +818,18 @@ Returns list (year month day hour minute) in local timezone." SOURCE-TZ is a timezone name like 'Europe/Lisbon' or 'Asia/Yerevan'. Returns list (year month day hour minute) in local timezone, or nil on error. -Uses the system `date` command for reliable timezone conversion." +Uses Emacs built-in timezone support (encode-time/decode-time with ZONE +argument) for fast, subprocess-free conversion. Uses the same system +TZ database as the `date' command." (when (and source-tz (not (string-empty-p source-tz))) (condition-case err - (let* ((date-input (format "%04d-%02d-%02d %02d:%02d" - year month day hour minute)) - ;; Use date command: convert from source-tz to local - ;; TZ= sets output timezone (local), TZ=\"...\" in -d sets input timezone - (cmd (format "date -d 'TZ=\"%s\" %s' '+%%Y %%m %%d %%H %%M' 2>/dev/null" - source-tz date-input)) - (result (string-trim (shell-command-to-string cmd))) - (parts (split-string result " "))) - (if (= 5 (length parts)) - (list (string-to-number (nth 0 parts)) - (string-to-number (nth 1 parts)) - (string-to-number (nth 2 parts)) - (string-to-number (nth 3 parts)) - (string-to-number (nth 4 parts))) - ;; date command failed (invalid timezone, etc.) - (cj/log-silently "calendar-sync: Failed to convert timezone %s: %s" - source-tz result) - nil)) + (let* ((abs-time (encode-time 0 minute hour day month year source-tz)) + (local (decode-time abs-time))) + (list (nth 5 local) ; year + (nth 4 local) ; month + (nth 3 local) ; day + (nth 2 local) ; hour + (nth 1 local))) ; minute (error (cj/log-silently "calendar-sync: Error converting timezone %s: %s" source-tz (error-message-string err)) @@ -1310,25 +1301,67 @@ Creates parent directories if needed." (with-temp-file file (insert content))) +;;; Debug Logging + +(defun calendar-sync--debug-p () + "Return non-nil if calendar-sync debug logging is enabled. +Checks `cj/debug-modules' for symbol `calendar-sync' or t (all)." + (and (boundp 'cj/debug-modules) + (or (eq cj/debug-modules t) + (memq 'calendar-sync cj/debug-modules)))) + ;;; Single Calendar Sync (defun calendar-sync--sync-calendar (calendar) "Sync a single CALENDAR asynchronously. CALENDAR is a plist with :name, :url, and :file keys. -Updates calendar state and saves to disk on completion." +Updates calendar state and saves to disk on completion. +Logs timing for each phase to *Messages* for performance diagnosis." (let ((name (plist-get calendar :name)) (url (plist-get calendar :url)) - (file (plist-get calendar :file))) + (file (plist-get calendar :file)) + (fetch-start (float-time))) ;; Mark as syncing (calendar-sync--set-calendar-state name '(:status syncing)) (cj/log-silently "calendar-sync: [%s] Syncing..." name) (calendar-sync--fetch-ics url (lambda (ics-content) - (let ((org-content (and ics-content (calendar-sync--parse-ics ics-content)))) - (if org-content + (let ((fetch-elapsed (- (float-time) fetch-start))) + (if (null ics-content) (progn - (calendar-sync--write-file org-content file) + (cj/log-silently "calendar-sync: [%s] Fetch failed" name) + (calendar-sync--set-calendar-state + name + (list :status 'error + :last-sync (plist-get (calendar-sync--get-calendar-state name) :last-sync) + :last-error "Fetch failed")) + (calendar-sync--save-state) + (message "calendar-sync: [%s] Sync failed (see *Messages*)" name)) + (when (calendar-sync--debug-p) + (cj/log-silently "calendar-sync: [%s] Fetched %dKB in %.1fs" + name (/ (length ics-content) 1024) fetch-elapsed)) + (let* ((parse-start (float-time)) + (org-content (calendar-sync--parse-ics ics-content)) + (parse-elapsed (- (float-time) parse-start))) + (if (null org-content) + (progn + (cj/log-silently "calendar-sync: [%s] Parse failed (%.1fs)" name parse-elapsed) + (calendar-sync--set-calendar-state + name + (list :status 'error + :last-sync (plist-get (calendar-sync--get-calendar-state name) :last-sync) + :last-error "Parse failed")) + (calendar-sync--save-state) + (message "calendar-sync: [%s] Sync failed (see *Messages*)" name)) + (when (calendar-sync--debug-p) + (cj/log-silently "calendar-sync: [%s] Parsed in %.1fs" name parse-elapsed)) + (let ((write-start (float-time))) + (calendar-sync--write-file org-content file) + (when (calendar-sync--debug-p) + (cj/log-silently "calendar-sync: [%s] Wrote %s in %.2fs" + name (file-name-nondirectory file) + (- (float-time) write-start)))) (calendar-sync--set-calendar-state name (list :status 'ok @@ -1337,14 +1370,9 @@ Updates calendar state and saves to disk on completion." (setq calendar-sync--last-timezone-offset (calendar-sync--current-timezone-offset)) (calendar-sync--save-state) - (message "calendar-sync: [%s] Sync complete → %s" name file)) - (calendar-sync--set-calendar-state - name - (list :status 'error - :last-sync (plist-get (calendar-sync--get-calendar-state name) :last-sync) - :last-error "Parse failed")) - (calendar-sync--save-state) - (message "calendar-sync: [%s] Sync failed (see *Messages*)" name))))))) + (let ((total-elapsed (- (float-time) fetch-start))) + (message "calendar-sync: [%s] Sync complete (%.1fs total) → %s" + name total-elapsed file)))))))))) (defun calendar-sync--sync-all-calendars () "Sync all configured calendars asynchronously. diff --git a/modules/dashboard-config.el b/modules/dashboard-config.el index 3333d96d..b8ee91d9 100644 --- a/modules/dashboard-config.el +++ b/modules/dashboard-config.el @@ -81,10 +81,12 @@ Adjust this if the title doesn't appear centered under the banner image.") (dashboard-item-generators '((projects . dashboard-insert-projects) - (bookmarks . dashboard-insert-bookmarks))) + (bookmarks . dashboard-insert-bookmarks) + (recents . dashboard-insert-recents))) (dashboard-items '((projects . 5) - (bookmarks . 10))) + (bookmarks . 10) + (recents . 5))) (dashboard-startupify-list '(dashboard-insert-banner diff --git a/modules/music-config.el b/modules/music-config.el index a3960440..a488bf4e 100644 --- a/modules/music-config.el +++ b/modules/music-config.el @@ -2,7 +2,7 @@ ;; ;;; Commentary: ;; -;; Comprehensive music management in Emacs via EMMS with MPV backend. +;; Music management in Emacs via EMMS with MPV backend. ;; Focus: simple, modular helpers; consistent error handling; streamlined UX. ;; ;; Highlights: @@ -331,6 +331,32 @@ Offers completion over existing names but allows new names." (user-error "Playlist file no longer exists: %s" (file-name-nondirectory path)))))) +;;; Commands: consume mode + +(defvar cj/music-consume-mode nil + "Non-nil means consume mode is active. +When enabled, tracks are removed from the playlist after they finish playing.") + +(defun cj/music--consume-track () + "Remove the just-finished track from the playlist. +Intended for use on `emms-player-finished-hook'." + (when cj/music-consume-mode + (with-current-buffer (cj/music--ensure-playlist-buffer) + (when (and emms-playlist-selected-marker + (marker-position emms-playlist-selected-marker)) + (save-excursion + (goto-char emms-playlist-selected-marker) + (emms-playlist-mode-kill-track)))))) + +(defun cj/music-toggle-consume () + "Toggle consume mode. When active, tracks are removed after playing." + (interactive) + (setq cj/music-consume-mode (not cj/music-consume-mode)) + (if cj/music-consume-mode + (add-hook 'emms-player-finished-hook #'cj/music--consume-track) + (remove-hook 'emms-player-finished-hook #'cj/music--consume-track)) + (message "Consume mode %s" (if cj/music-consume-mode "enabled" "disabled"))) + ;;; Commands: UI ;;; Minimal ensure-loaded setup for on-demand use @@ -419,13 +445,17 @@ Dirs added recursively." "m" #'cj/music-playlist-toggle "M" #'cj/music-playlist-show "a" #'cj/music-fuzzy-select-and-add - "r" #'cj/music-create-radio-station + "R" #'cj/music-create-radio-station "SPC" #'emms-pause "s" #'emms-stop "n" #'emms-next "p" #'emms-previous "g" #'emms-playlist-mode-go - "x" #'emms-shuffle) + "Z" #'emms-shuffle + "r" #'emms-toggle-repeat-playlist + "t" #'emms-toggle-repeat-track + "z" #'emms-toggle-random-playlist + "x" #'cj/music-toggle-consume) (keymap-set cj/custom-keymap "m" cj/music-map) (with-eval-after-load 'which-key @@ -434,13 +464,17 @@ Dirs added recursively." "C-; m m" "toggle playlist" "C-; m M" "show playlist" "C-; m a" "add music" - "C-; m r" "create radio" + "C-; m R" "create radio" "C-; m SPC" "pause" "C-; m s" "stop" "C-; m n" "next track" "C-; m p" "previous track" "C-; m g" "goto playlist" - "C-; m x" "shuffle")) + "C-; m Z" "shuffle" + "C-; m r" "repeat playlist" + "C-; m t" "repeat track" + "C-; m z" "random" + "C-; m x" "consume")) (use-package emms :defer t @@ -499,27 +533,41 @@ Dirs added recursively." ("SPC" . emms-pause) ("s" . emms-stop) ("n" . emms-next) + (">" . emms-next) ("P" . emms-previous) + ("<" . emms-previous) ("f" . emms-seek-forward) ("b" . emms-seek-backward) - ("x" . emms-shuffle) ("q" . emms-playlist-mode-bury-buffer) ("a" . cj/music-fuzzy-select-and-add) + ;; Toggles (aligned with ncmpcpp) + ("r" . emms-toggle-repeat-playlist) + ("t" . emms-toggle-repeat-track) + ("z" . emms-toggle-random-playlist) + ("x" . cj/music-toggle-consume) + ("Z" . emms-shuffle) + ;; Info + ("i" . emms-show) + ("o" . emms-playlist-mode-center-current) ;; Manipulation ("A" . cj/music-append-track-to-playlist) + ("c" . cj/music-playlist-clear) ("C" . cj/music-playlist-clear) ("L" . cj/music-playlist-load) ("E" . cj/music-playlist-edit) - ("R" . cj/music-playlist-reload) + ("g" . cj/music-playlist-reload) ("S" . cj/music-playlist-save) - ;; Track reordering (bind directly to EMMS commands; no wrappers) + ;; Track reordering + ("S-<up>" . emms-playlist-mode-shift-track-up) + ("S-<down>" . emms-playlist-mode-shift-track-down) ("C-<up>" . emms-playlist-mode-shift-track-up) ("C-<down>" . emms-playlist-mode-shift-track-down) ;; Radio - ("r" . cj/music-create-radio-station) - ;; Volume (MPV) - ("-" . emms-volume-lower) - ("=" . emms-volume-raise))) + ("R" . cj/music-create-radio-station) + ;; Volume + ("+" . emms-volume-raise) + ("=" . emms-volume-raise) + ("-" . emms-volume-lower))) ;; Quick toggle key - use autoload to avoid loading emms at startup (autoload 'cj/music-playlist-toggle "music-config" "Toggle EMMS playlist window." t) |
