summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/calendar-sync.el92
-rw-r--r--modules/dashboard-config.el6
-rw-r--r--modules/music-config.el72
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)