Add facilities to ease work in the buffer ; Add delete command that make use of these facilities.

This commit is contained in:
Renaud Casenave-Péré 2011-08-31 16:04:44 +09:00
parent 218566d924
commit a00104236e

130
empc.el
View file

@ -147,7 +147,8 @@ If there is no command left to send, put the client in idle state."
(defun empc-status-get (object attr) (plist-get (empc-status object) attr))
(defun empc-playlist-set (object playlist) (setcar (cddr object) playlist))
(defun empc-playlist-songs-set (object playlist-songs) (setcdr (cddr object) playlist-songs))
(defun empc-song (object id) (gethash id (empc-playlist-songs object)))
(defun empc-song-by-id (object id) (gethash id (empc-playlist-songs object)))
(defun empc-song-by-pos (object pos) (empc-song-by-id object (elt (empc-playlist object) pos)))
(defun empc-current-song (object) (gethash (empc-status-get object :songid) (empc-playlist-songs object)))
(defun empc-create (name buffer host service)
@ -224,7 +225,7 @@ For status:
(defun empc-mode-line-to-string ()
"Return a string to write to the mode-line."
(if (eq (empc-status-get empc-object :state) 'play)
(concat " [" (number-to-string (empc-status-get empc-object :song)) "/" (number-to-string (empc-status-get empc-object :playlistlength)) "] "
(concat " [" (number-to-string (1+ (empc-status-get empc-object :song))) "/" (number-to-string (empc-status-get empc-object :playlistlength)) "] "
(if (and (plist-member (empc-current-song empc-object) :artist)
(plist-member (empc-current-song empc-object) :title))
(concat (plist-get (empc-current-song empc-object) :artist)
@ -254,8 +255,8 @@ For status:
(define-key empc-playlist-map "z" 'empc-toggle-random)
(define-key empc-playlist-map "x" 'empc-toggle-crossfade)
(define-key empc-playlist-map "o" 'empc-playlist-goto-current-song)
(define-key empc-playlist-map [return] 'empc-send-play)
(define-key empc-playlist-map "d" 'empc-send-delete)
(define-key empc-playlist-map [return] (lambda () (interactive) (empc-with-song-at-point song (empc-send-playid (plist-get song :id)))))
(define-key empc-playlist-map "d" 'empc-playlist-delete-song)
(define-key empc-playlist-map "c" 'empc-send-clear)
(defun empc-process-sentinel (proc event)
@ -459,6 +460,15 @@ According to what is in the diff, several actions can be performed:
(unless (equal (empc-status-get empc-object attr) value)
(setq status-diff (plist-put status-diff attr value)))))))
(defun empc-playlist-get-songs (begin &optional end)
"Retreive a list of songs' id between BEGIN and END."
(when (eq major-mode 'empc-playlist-mode)
(let* ((bpos (1- (line-number-at-pos begin)))
(epos (if end (count-lines begin end) 1))
ids)
(dotimes (i epos ids)
(setq ids (cons (empc-song-by-pos empc-object (+ bpos i)) ids))))))
(defun empc-playlist-goto-current-song ()
"Put point at currently playing song."
(interactive)
@ -490,14 +500,26 @@ According to what is in the diff, several actions can be performed:
(erase-buffer)
(when (empc-playlist-songs empc-object)
(mapcar (lambda (id)
(empc-playlist-insert-song (empc-song empc-object id)))
(empc-playlist-insert-song (empc-song-by-id empc-object id)))
(empc-playlist empc-object))))))
(defmacro empc-with-current-playlist (&rest body)
"Set the playlist buffer as the current one."
`(save-window-excursion
(empc-switch-to-playlist)
(save-excursion
(let ((buffer-read-only nil))
,@body))))
(defmacro empc-with-song-at-point (song &rest body)
"Retreive the song at point if applicable."
`(when (memq major-mode '(empc-playlist-mode))
(let ((,song (empc-song-by-pos empc-object (1- (line-number-at-pos (point))))))
,@body)))
(defun empc-playlist-insert-song (song)
"Insert SONG into the playlist buffer."
(save-window-excursion
(empc-switch-to-playlist)
(let ((buffer-read-only nil))
(empc-with-current-playlist
(goto-char (point-min))
(let ((needed-lines (forward-line (plist-get song :pos))))
(when (or (> needed-lines 0) (eq (point) (point-max)))
@ -505,7 +527,45 @@ According to what is in the diff, several actions can be performed:
(insert "\n"))
(forward-line -1)))
(kill-region (line-beginning-position) (line-end-position))
(insert (empc-song-to-string song)))))
(insert (empc-song-to-string song))))
(defun empc-playlist-kill-song (song)
"Kill SONG from the playlist."
(empc-with-current-playlist
(let ((songs (if (listp (car song)) song
(list song)))
ids)
(mapcar (lambda (song)
(goto-char (point-min))
(forward-line (plist-get song :pos))
(kill-line 1)
(setq ids (cons (plist-get song :id) ids)))
songs)
(empc-playlist-set empc-object (delete-if (lambda (id) (memq id ids))
(empc-playlist empc-object)))
(let ((i 0))
(mapcar (lambda (id)
(puthash id (plist-put (empc-song-by-id empc-object id) :pos i) (empc-playlist-songs empc-object))
(incf i))
(empc-playlist empc-object))))))
(defun empc-playlist-delete-song ()
"Delete song at point or a range of song if the mark is
active."
(interactive)
(if (and mark-active (use-region-p))
(call-interactively 'empc-playlist-delete-region)
(let ((songs (empc-playlist-get-songs (point))))
(when songs
(empc-send-deleteid (plist-get (car songs) :id))
(empc-playlist-kill-song (car songs))))))
(defun empc-playlist-delete-region (&optional begin end)
"Delete a range of song."
(interactive "r")
(let ((songs (empc-playlist-get-songs begin end)))
(mapcar (lambda (song) (empc-send-deleteid (plist-get song :id))) songs)
(mapcar 'empc-playlist-kill-song songs)))
(defun empc-response-get-playlist (data)
"Parse information regarding songs in current playlist and arrange it into a
@ -563,8 +623,11 @@ songs order is kept into an avector `empc-current-playlist'."
(let ((id (string-to-number (cdar data)))
(cpos (string-to-number (cdadr data))))
(setq data (cddr data))
(unless (gethash id (empc-playlist-songs empc-object))
(empc-send-playlistid id))
(let ((song (gethash id (empc-playlist-songs empc-object))))
(if (not song)
(empc-send-playlistid id)
(puthash id (setq song (plist-put song :pos cpos)) (empc-playlist-songs empc-object))
(empc-playlist-insert-song song)))
(aset (empc-playlist empc-object) cpos id)))))
(defun empc-response-idle (data)
@ -710,7 +773,7 @@ If the stream process is killed for whatever the reason, pause mpd if possible."
(switch-to-buffer "*empc*")))
(empc-playlist-mode))
(defmacro with-updated-status (&rest body)
(defmacro empc-with-updated-status (&rest body)
"Update the status and execute the forms in BODY."
`(if (empc-status empc-object)
,@body
@ -728,13 +791,15 @@ If the stream process is killed for whatever the reason, pause mpd if possible."
(defmacro empc-define-toggle-command (command &optional state-name attr &rest body)
"Define a command that toggle a state."
`(defun ,(intern (concat "empc-toggle-" command)) (&optional state)
`(progn
(empc-define-simple-command ,command)
(defun ,(intern (concat "empc-toggle-" command)) (&optional state)
,(concat "Toggle " command ".")
(interactive)
(let ((debug-on-error t))
(if state
(empc-send (concat ,(concat command " ") (int-to-string state)))
(with-updated-status
(,(intern (concat "empc-send-" command)) (int-to-string state))
(empc-with-updated-status
(let ((,(if attr attr
(intern command))
(empc-status-get empc-object (quote ,(intern (concat ":" (if state-name
@ -742,32 +807,8 @@ If the stream process is killed for whatever the reason, pause mpd if possible."
command)))))))
,(if body
`(progn ,@body)
`(empc-send (concat ,command (if (= ,(if attr attr
(intern command)) 1) " 0" " 1"))))))))))
(defmacro empc-define-command-with-pos (command &optional closure)
"Define a command that need a position either as a parameter or
computed using point in buffer."
`(defun ,(intern (concat "empc-send-" command)) (&optional pos)
,(concat "Send " command " to the server together with an ID
parameter computed using pos or cursor position.")
(interactive)
(let ((debug-on-error t))
(unless pos
(setq pos (count-lines (point-min) (point))))
(let ((id (elt (empc-playlist empc-object) pos)))
(empc-send (concat ,(concat command "id ") (number-to-string id)) ,closure)))))
(defmacro empc-define-command-with-current-id (command &optional closure)
"Define a command that uses the current song as a parameter."
`(defun ,(intern (concat "empc-send-" command)) (&optional arg)
,(concat "Send " command " to the server with the ID of the currently playing song.")
(interactive)
(let ((debug-on-error t))
(empc-send (concat ,(concat command "id ")
(number-to-string (empc-status-get empc-object :songid))
(when arg (concat " " (if (stringp arg) arg (number-to-string arg)))))
,closure))))
`(,(intern (concat "empc-send-" command)) ,(if attr attr
(intern command)))))))))))
;; Querying MPD's status
(empc-define-simple-command "clearerror")
@ -777,7 +818,6 @@ computed using point in buffer."
;; Playback options
(empc-define-toggle-command "consume")
(empc-define-simple-command "crossfade")
(empc-define-toggle-command "crossfade" "xfade" xfade
(if (= xfade 0)
(empc-send-crossfade (if empc-last-crossfade
@ -793,7 +833,6 @@ computed using point in buffer."
;; Controlling playback
(empc-define-simple-command "next")
(empc-define-simple-command "pause")
(empc-define-toggle-command "pause" "state" state
(cond
((eq state 'play)
@ -801,14 +840,13 @@ computed using point in buffer."
((eq state 'pause)
(empc-send-pause 0))
(t (empc-send-play))))
(empc-define-command-with-pos "play")
(empc-define-simple-command "playid")
(empc-define-simple-command "previous")
(empc-define-command-with-current-id "seek")
(empc-define-simple-command "stop")
;; The current playlist
(empc-define-simple-command "clear")
(empc-define-command-with-pos "delete")
(empc-define-simple-command "deleteid")
(empc-define-simple-command "playlistid" 'empc-response-get-playlistid)
(empc-define-simple-command "playlistinfo" 'empc-response-get-playlist)
(empc-define-simple-command "plchangesposid" 'empc-response-get-plchangesposid)