Compare commits

...

11 Commits

@ -49,11 +49,16 @@
;;; Code: ;;; Code:
;; TODOS: ;; TODOS: Buffer kill hook not triggered on exit?
;; ERROR: timer shutdown seems to leave things in a wrong state and buffer doesn't die properly ;; Split buffer update into clock and graph sections
;; Check on the logic of push-current, needs predicates rather than lazy if conditions ;; - As it is, pausing the timer can't draw a full buffer and ends up
;; I'm not calling anything to destroy the buffer on desched ;; erasing the previous time graph
;; BUG: The buffer is immortal? ;; The buffer could render in smoother, just set points don't render empty data
;; Center the clock-countdown display
;;
;; ERROR: Check on the logic of push-current, needs predicates rather than lazy if conditions
;; history field is not populating
;; BUG: Break timer displays wrong time and sign
;; ;;
;; Optional ;; Optional
@ -154,6 +159,8 @@
(define-error 'tacc-error "Generic error from the tacc-timer module" 'error) (define-error 'tacc-error "Generic error from the tacc-timer module" 'error)
(define-error 'tacc-error-illegal-buffer-state (define-error 'tacc-error-illegal-buffer-state
"Precondition failed, the timer buffer is in an invalid state" 'tacc-error) "Precondition failed, the timer buffer is in an invalid state" 'tacc-error)
(define-error 'tacc-error-dirty-state
"Precondition failed, tacc state wasn't clean" 'tacc-error)
(define-error 'tacc-error-serialization-registered (define-error 'tacc-error-serialization-registered
"Failed to register a serialization, serializations must have unique names" "Failed to register a serialization, serializations must have unique names"
'tacc-error) 'tacc-error)
@ -272,13 +279,13 @@ registered" sym-name)
(defun tacc-info-initial () (defun tacc-info-initial ()
"Returns the initial state of tacc-info" "Returns the initial state of tacc-info"
`((history . ()) (copy-alist `((history . ())
(cycle . 0) (cycle . 0)
(work-ts . nil) (work-ts . nil)
(break-ts . nil) (break-ts . nil)
(end-ts . nil) (end-ts . nil)
(state . stop) (state . stop)
(tag . ""))) (tag . ""))))
(defvar tacc-info (tacc-info-initial) (defvar tacc-info (tacc-info-initial)
"State information related to the timer") "State information related to the timer")
@ -292,26 +299,61 @@ registered" sym-name)
"Gets the tacc-timer buffer if there is one" "Gets the tacc-timer buffer if there is one"
(get-buffer tacc-buffer-name)) (get-buffer tacc-buffer-name))
(defun tacc-running? () (defun tacc-live-state? ()
"Checks if tacc-info's state indicates a live timer"
(let ((state (alist-get 'state tacc-info)))
(cond ((eq state 'pause) nil)
((eq state 'stop) nil)
((eq state 'work) t)
((eq state 'break) t)
('t (signal 'tacc-error-illegal-buffer-state state)))))
(defun tacc-live? ()
"Checks if there is a live timer running" "Checks if there is a live timer running"
(timerp tacc-timer)) (timerp tacc-timer))
(defun tacc-running-state? (state) (defun tacc-session-state? (state)
"Checks if the state supplied indicates a live timer" "Checks if the state supplied indicates there should be session information"
(cond ((eq state 'pause) nil) (cond ((eq state 'pause) t)
((eq state 'stop) nil) ((eq state 'stop) nil)
((eq state 'work) t) ((eq state 'work) t)
((eq state 'break) t) ((eq state 'break) t)
('t (signal 'tacc-error-illegal-buffer-state state)))) ('t (signal 'tacc-error-illegal-buffer-state state))))
(defun tacc-session? ()
"Checks if tacc session info indicates there should be session information"
(tacc-session-state? (alist-get 'state tacc-info)))
(defun tacc-history? ()
"Checks if tacc session has history information"
(if (> 0 (length (alist-get 'history tacc-info)))
t
nil))
(defun tacc-invariant () (defun tacc-invariant ()
"Returns t if tacc is in a legal state for mutating tacc-info "Returns t when state indicates tacc-info can be mutated. Signals error if not"
Note, if there is no tacc-buffer then you shouldn't be touching tacc-info" (if (bufferp (tacc-buffer))
(if (tacc-live?)
(if (tacc-live-state?)
t
;; Timer running without reason in logic
(signal 'tacc-error-illegal-buffer-state tacc-timer))
(if (tacc-session-state? (alist-get 'state tacc-info))
t
;; Buffer is live without reason in logic
(signal 'tacc-error-illegal-buffer-state (tacc-buffer))))
;; We shouldn't be calling this when the buffer is dead
(signal 'tacc-error-illegal-buffer-state "No buffer")))
(defun tacc-cleanup-invariant ()
"Returns t if tacc is in a cleaned up state, signals an error otherwise"
(if (bufferp (tacc-buffer)) (if (bufferp (tacc-buffer))
(if (and (timerp tacc-timer) (signal 'tacc-error-dirty-state (tacc-buffer))
(tacc-running-state? tacc-info)) (if (tacc-live?)
t) (signal 'tacc-error-dirty-state tacc-timer)
(signal 'tacc-error-illegal-buffer-state (tacc-buffer)))) (if (tacc-session?)
(signal 'tacc-error-dirty-state tacc-info)
t))))
;; 05.3 Predicates and helpers ;; 05.3 Predicates and helpers
(defun tacc-chime-p (countdown-ts) (defun tacc-chime-p (countdown-ts)
@ -340,25 +382,38 @@ Note, if there is no tacc-buffer then you shouldn't be touching tacc-info"
(time-add start-ts (time-add start-ts
(if (eq state 'work) (if (eq state 'work)
(tacc-work-period cycle) (tacc-work-period cycle)
(tacc-break-period cycle))))) (if (eq state 'break)
(tacc-break-period cycle))
0))))
;; 05.3 State manipulation ;; 05.3 State manipulation
(defun tacc-tick () (defun tacc-draw-buffer (now state)
"Time marches on. Called every second while a tacc timer is running" "Redraws the timer buffer and returns the calculated countdown seconds"
(let* ((now (current-time)) (let* ((cycle (alist-get 'cycle tacc-info))
(state (alist-get 'state tacc-info))
(cycle (alist-get 'cycle tacc-info))
(work-ts (alist-get 'work-ts tacc-info)) (work-ts (alist-get 'work-ts tacc-info))
(break-ts (alist-get 'break-ts tacc-info)) (break-ts (alist-get 'break-ts tacc-info))
(est (tacc-est-ts state cycle work-ts break-ts)) (est (tacc-est-ts state cycle work-ts break-ts))
(countdown (time-subtract est now))) (countdown (time-subtract est now)))
(tacc-timer-buffer-update state est countdown now) (tacc-timer-buffer-update state est countdown now)
countdown))
(defun tacc-tick ()
"Time marches on. Called every second while a tacc timer is running"
(let* ((now (current-time))
(state (alist-get 'state tacc-info))
(countdown (tacc-draw-buffer now state)))
(if (functionp tacc-play-sound-file) (if (functionp tacc-play-sound-file)
(cond ((tacc-chime-p countdown) (cond ((tacc-chime-p countdown)
(funcall tacc-play-sound-file tacc-sound-chime)) (funcall tacc-play-sound-file tacc-sound-chime))
(t (funcall tacc-play-sound-file tacc-sound-tick)))))) (t (funcall tacc-play-sound-file tacc-sound-tick))))))
(defun tacc-timer-buffer-redraw ()
"Force a redraw of the timer buffer"
(let ((now (current-time))
(state (alist-get 'state tacc-info)))
(tacc-draw-buffer now state)))
(defun tacc-schedule () (defun tacc-schedule ()
"Note the time and schedule the first tick" "Note the time and schedule the first tick"
(setcdr (assoc 'state tacc-info) 'work) (setcdr (assoc 'state tacc-info) 'work)
@ -403,14 +458,14 @@ Note, if there is no tacc-buffer then you shouldn't be touching tacc-info"
(defun tacc-kill-buffer-confirm () (defun tacc-kill-buffer-confirm ()
"Ask for confirmation if a timer is still running" "Ask for confirmation if a timer is still running"
(if (tacc-running?) (if (tacc-session?)
(yes-or-no-p "Are you sure you want to kill the timer buffer? This will\ (yes-or-no-p "Are you sure you want to kill the timer buffer? This will\
end the current timer session as well.") end the current timer session as well.")
t)) t))
(defun tacc-buffer-cleanup () (defun tacc-buffer-cleanup ()
"Save and cleanup the timer state if a timer is still live" "Save and cleanup the timer state if a timer is still live"
(if (tacc-running?) (if (tacc-session?)
(progn (tacc-deschedule) (progn (tacc-deschedule)
(tacc-push-current)))) (tacc-push-current))))
@ -488,13 +543,14 @@ Note, if there is no tacc-buffer then you shouldn't be touching tacc-info"
(set-marker .graph-end (point-max)) (set-marker .graph-end (point-max))
(tacc-insert-between (tacc-insert-between
.graph-start .graph-end .graph-start .graph-end
"Tag: \n" " Tag: \n"
(tacc-graph-bar-render width 0.0 0.0 1.0))))))) (tacc-graph-bar-render width 0.0 0.0 1.0)))))))
(defun tacc-clock-glyph (state) (defun tacc-clock-glyph (state)
"Returns the correct play or pause glyph for each state" "Returns the correct play or pause glyph for each state"
(string (cond ((eq state 'work) tacc-run-glyph) (string
(cond ((eq state 'work) tacc-run-glyph)
((eq state 'break) tacc-run-glyph) ((eq state 'break) tacc-run-glyph)
((eq state 'pause) tacc-pause-glyph) ((eq state 'pause) tacc-pause-glyph)
((eq state 'stop) tacc-pause-glyph)))) ((eq state 'stop) tacc-pause-glyph))))
@ -602,6 +658,18 @@ Returns the current tag if no prompt is made"
(alist-get 'tag tacc-info) (alist-get 'tag tacc-info)
(tacc-tag-prompt))) (tacc-tag-prompt)))
(defun tacc-end-session ()
"Ends the current timer session, runs serialization and kills the buffer"
(if (tacc-session?)
(progn
(if (tacc-live?)
(progn (tacc-deschedule)
(tacc-push-current)))
(tacc-save-record (assoc 'history tacc-info))
(setq tacc-info (tacc-info-initial))
(kill-buffer (tacc-buffer))
(tacc-cleanup-invariant))))
;; 08.2 Interactive ;; 08.2 Interactive
(defun tacc-start-timer () (defun tacc-start-timer ()
@ -614,7 +682,7 @@ Returns the current tag if no prompt is made"
(defun tacc-set-tag () (defun tacc-set-tag ()
"Sets a tag for live timer sessions going forward" "Sets a tag for live timer sessions going forward"
(interactive) (interactive)
(if (tacc-running?) (if (tacc-session?)
(setcdr (assoc 'tag tacc-info) (tacc-tag-prompt)) (setcdr (assoc 'tag tacc-info) (tacc-tag-prompt))
(message "There isn't a live timer session to tag"))) (message "There isn't a live timer session to tag")))
@ -628,7 +696,7 @@ Returns the current tag if no prompt is made"
(defun tacc-timer-state-forward () (defun tacc-timer-state-forward ()
"Move the tacc timer state forward" "Move the tacc timer state forward"
(interactive) (interactive)
(if (tacc-running?) (if (tacc-live?)
(let* ((state-place (assoc 'state tacc-info)) (let* ((state-place (assoc 'state tacc-info))
(state (cdr state-place)) (state (cdr state-place))
(work-ts (assoc 'work-ts tacc-info)) (work-ts (assoc 'work-ts tacc-info))
@ -649,7 +717,7 @@ Returns the current tag if no prompt is made"
(defun tacc-timer-state-skip () (defun tacc-timer-state-skip ()
"Move the tacc timer state to a new work cycle" "Move the tacc timer state to a new work cycle"
(interactive) (interactive)
(if (tacc-running?) (if (tacc-live?)
(let* ((state-place (assoc 'state tacc-info)) (let* ((state-place (assoc 'state tacc-info))
(work-ts (assoc 'work-ts tacc-info)) (work-ts (assoc 'work-ts tacc-info))
(end-ts (assoc 'end-ts tacc-info)) (end-ts (assoc 'end-ts tacc-info))
@ -675,26 +743,34 @@ Returns the current tag if no prompt is made"
(defun tacc-hold-timer () (defun tacc-hold-timer ()
"Pause/unpause the tacc timer session" "Pause/unpause the tacc timer session"
(interactive) (interactive)
(if (tacc-running?) (if (tacc-session?)
(let ((state (alist-get 'state tacc-info))) (let ((state (alist-get 'state tacc-info)))
(if (eq state 'pause) (if (eq state 'pause)
(tacc-unpause-timer) (tacc-unpause-timer)
(tacc-pause-timer)))) (tacc-pause-timer))
(tacc-timer-buffer-redraw)))
(message "There isn't a live timer session to pause/unpause")) (message "There isn't a live timer session to pause/unpause"))
(defun tacc-end-timer () (defun tacc-end-timer ()
"Ends the current timer session, runs serialization and kills the buffer" "Ends the current timer session, runs serialization and kills the buffer"
(interactive) (interactive)
(tacc-deschedule) (tacc-end-session))
(tacc-push-current)
(tacc-save-record (assoc 'history tacc-info)) ;; 09 Hooks setup
(setcdr (assoc 'state tacc-info) 'stop)
(kill-buffer (tacc-buffer))) (defun tacc-kill-emacs-confirm ()
"Ask for confirmation if a timer is still running"
(if (and (tacc-session?) ;; Redundant, but more re-use this way
(yes-or-no-p "You still have a work timer running, are you sure you want\
to quit emacs?"))
(tacc-end-session)
t))
;; 09 Hooks setup
(add-hook 'after-init-hook 'tacc-load-tag-history) (add-hook 'after-init-hook 'tacc-load-tag-history)
(add-hook 'kill-emacs-query-functions 'tacc-kill-emacs-confirm)
(add-hook 'kill-emacs-hook 'tacc-save-tag-history) (add-hook 'kill-emacs-hook 'tacc-save-tag-history)
;; 10 Provides ;; 10 Provides
(provide 'tacc) (provide 'tacc)

Loading…
Cancel
Save