mirror of
https://github.com/vale981/pomm.el
synced 2025-03-05 09:41:42 -05:00
docs: improve docs
This commit is contained in:
parent
abbf649d63
commit
78cb9cd5f9
2 changed files with 172 additions and 30 deletions
83
README.org
83
README.org
|
@ -1,7 +1,37 @@
|
|||
#+TITLE: pomm.el
|
||||
|
||||
Yet another implementation of a [[https://en.wikipedia.org/wiki/Pomodoro_Technique][pomodoro timer]] for Emacs.
|
||||
|
||||
This particular package features:
|
||||
- Managing the timer with the excellent [[https://github.com/magit/transient/blob/master/lisp/transient.el][transient.el]].
|
||||
- A persistent state between Emacs sessions.
|
||||
The timer state isn't reset if you close Emacs. Also, the state file can be syncronized between machines.
|
||||
|
||||
None of the available [[*Alternatives][alternatives]] were doing quite what I wanted, and the idea of the timer is quite simple, so I figured I'd implement one myself.
|
||||
|
||||
* Setup
|
||||
While the package is available only this this repository, one way to install is to clone the repository, add the package to the =load-path= and load it with =require=:
|
||||
#+begin_src emacs-lisp
|
||||
(require 'pomm)
|
||||
#+end_src
|
||||
|
||||
My preferred way is =use-package= with =straight.el=:
|
||||
#+begin_src emacs-lisp
|
||||
(use-package pomm
|
||||
:straight (:host github :repo "SqrtMinusOne/pomm.el")
|
||||
:commands (pomm))
|
||||
#+end_src
|
||||
|
||||
The package requires Emacs 27.1, because the time API of the previous versions is kinda crazy and 27.1 has =time-convert=.
|
||||
|
||||
** Alerts
|
||||
The package sends alerts via =alert.el=. The default style of alert is a plain =message=, but if you want an actual notification, set =alert-default-style= accordingly:
|
||||
#+begin_src emacs-lisp
|
||||
(setq alert-default-style 'libnotify)
|
||||
#+end_src
|
||||
|
||||
** Modeline
|
||||
If you want the timer to display in the modeline, add the following code to your config:
|
||||
#+begin_src emacs-lisp
|
||||
(add-to-list 'mode-line-misc-info '(:eval pomm-current-mode-line-string))
|
||||
(add-hook 'pomm-on-tick-hook 'pomm-update-mode-line-string)
|
||||
|
@ -9,3 +39,56 @@
|
|||
(add-hook 'pomm-on-status-changed-hook 'pomm-update-mode-line-string)
|
||||
(add-hook 'pomm-on-status-changed-hook 'force-mode-line-update)
|
||||
#+end_src
|
||||
|
||||
This is quite verbose, but as I don't use this feature, I want to avoid adding an unnecesary load to my Emacs.
|
||||
|
||||
** Polybar module
|
||||
If you want to display the pomodoro status in something like polybar, you can add the following lines to your config:
|
||||
#+begin_src emacs-lisp
|
||||
(add-hook 'pomm-on-tick-hook 'pomm-update-mode-line-string)
|
||||
(add-hook 'pomm-on-status-changed-hook 'pomm-update-mode-line-string)
|
||||
#+end_src
|
||||
|
||||
Create a script like this:
|
||||
#+begin_src bash
|
||||
if ps -e | grep emacs >> /dev/null; then
|
||||
emacsclient --eval "pomm-current-mode-line-string" | xargs echo -e
|
||||
fi
|
||||
#+end_src
|
||||
|
||||
And add a polybar module definition to your polybar config:
|
||||
#+begin_src conf-windows
|
||||
[module/pomm]
|
||||
type = custom/script
|
||||
exec = /home/pavel/bin/polybar/pomm.sh
|
||||
interval = 1
|
||||
#+end_src
|
||||
|
||||
* Usage
|
||||
Run =M-x pomm= to open the transient buffer.
|
||||
|
||||
The listed commands are rather self-descriptive and match the pomodoro ideology.
|
||||
|
||||
The timer can have 3 states:
|
||||
- *Stopped*.
|
||||
Can be started with "s" or =M-x pomm-start=. A new iteration of the timer will be started.
|
||||
- *Paused*. Can be continuted with "s" / =M-x pomm-start= or stopped competely with "S" / =M-x pomm-stop=.
|
||||
- *Running*. Can be paused with "p" / =M-x pomm-pause= or stopped with "S" / =M-x pomm-stop=.
|
||||
|
||||
The state of the timer can be reset with "R" or =M-x pomm-reset=.
|
||||
|
||||
"U" updates the transient buffer. The update is manual because I didn't figure out how to automate this, and I think this is not /really/ necessary.
|
||||
|
||||
Some settings are available in the transient buffer, but you can customize the relevant variables to make them permanent. Check =M-x customize-group= =pomm= for more information.
|
||||
|
||||
* Alternatives
|
||||
There is a number of packages with a similar purpose, here are some:
|
||||
- [[https://github.com/marcinkoziej/org-pomodoro/tree/master][org-pomodoro]]
|
||||
- [[https://github.com/TatriX/pomidor/][pomidor]]
|
||||
- [[https://github.com/baudtack/pomodoro.el/][pomodoro.el]]
|
||||
- [[https://github.com/konr/tomatinho/][tomatinho]]
|
||||
- [[https://github.com/ferfebles/redtick][redtick]]
|
||||
Be sure to check those out if this one doesn't quite fit your workflow!
|
||||
|
||||
* P.S.
|
||||
The package name is not an abbreviation. I just hope it doesn't mean something horrible in some language I don't know.
|
||||
|
|
119
pomm.el
119
pomm.el
|
@ -91,7 +91,7 @@ The format is the same as in `format-seconds'"
|
|||
:type 'string)
|
||||
|
||||
(defcustom pomm-on-tick-hook nil
|
||||
"A hook to run on a tick when the timer is running."
|
||||
"A hook to run on every tick when the timer is running."
|
||||
:group 'pomm
|
||||
:type 'hook)
|
||||
|
||||
|
@ -113,19 +113,29 @@ Current period is also an alist with the following keys:
|
|||
- kind: either 'short-break, 'long-break or 'work
|
||||
- start-time: start timestamp
|
||||
- effective-start-time: start timestamp, corrected for pauses
|
||||
- iteration: number the current pomodoro iteration")
|
||||
- iteration: number the current pomodoro iteration
|
||||
|
||||
History is a list of alists with the following keys:
|
||||
- kind: same as in current
|
||||
- iteration
|
||||
- start-time: start timestamp
|
||||
- end-time: end timestamp
|
||||
- paused-time: time spent it a paused state")
|
||||
|
||||
(defvar pomm--timer nil
|
||||
"A variable for the pomm timer.")
|
||||
|
||||
(defvar pomm-current-mode-line-string nil
|
||||
"Current mode-line of the pomodoro timer.")
|
||||
"Current mode-line string of the pomodoro timer.
|
||||
|
||||
Updated by `pomm-update-mode-line-string'.")
|
||||
|
||||
(defun pomm--do-reset ()
|
||||
"Reset the pomodoro timer state."
|
||||
(when pomm--timer
|
||||
(cancel-timer pomm--timer)
|
||||
(setq pomm--timer nil))
|
||||
;; This is necessary to make the reset work with setf on the variable
|
||||
(setq pomm--state
|
||||
`((status . ,'stopped)
|
||||
(current . ,nil)
|
||||
|
@ -156,7 +166,7 @@ KIND is the same as in `pomm--state'"
|
|||
:title "Pomodoro"))
|
||||
|
||||
(defun pomm--new-iteration ()
|
||||
"Initialize state as a new iteration of pomodoro."
|
||||
"Start a new iteration of the pomodoro timer."
|
||||
(setf (alist-get 'current pomm--state)
|
||||
`((kind . work)
|
||||
(start-time . ,(time-convert nil 'integer))
|
||||
|
@ -181,7 +191,9 @@ KIND is the same as in `pomm--state'"
|
|||
(_ 0))))
|
||||
|
||||
(defun pomm--need-switch-p ()
|
||||
"Check if it is necessary to switch a period."
|
||||
"Check if it is necessary to switch a period.
|
||||
|
||||
The condition is: (effective-start-time + length) < now."
|
||||
(< (+ (alist-get 'effective-start-time (alist-get 'current pomm--state))
|
||||
(pomm--get-kind-length
|
||||
(alist-get 'kind (alist-get 'current pomm--state))))
|
||||
|
@ -207,6 +219,7 @@ KIND is the same as in `pomm--state'"
|
|||
"Switch to the next period."
|
||||
(let* ((current-kind (alist-get 'kind (alist-get 'current pomm--state)))
|
||||
(current-iteration (alist-get 'iteration (alist-get 'current pomm--state)))
|
||||
;; Number of work periods in the current iteration
|
||||
(work-periods (+ (seq-count
|
||||
(lambda (item)
|
||||
(and (= (alist-get 'iteration item) current-iteration)
|
||||
|
@ -240,7 +253,6 @@ KIND is the same as in `pomm--state'"
|
|||
|
||||
(defun pomm--on-tick ()
|
||||
"A function to be ran on a timer tick."
|
||||
|
||||
(pcase (alist-get 'status pomm--state)
|
||||
('stopped (when pomm--timer
|
||||
(cancel-timer pomm--timer)
|
||||
|
@ -253,7 +265,12 @@ KIND is the same as in `pomm--state'"
|
|||
(run-hooks 'pomm-on-tick-hook)))))
|
||||
|
||||
(defun pomm--get-time-remaning ()
|
||||
"Get time remaining in the current pomodoro period."
|
||||
"Get time remaining in the current pomodoro period.
|
||||
|
||||
The formula is:
|
||||
\(effective-start-time + length\) - now + paused-time,
|
||||
where paused-time is 0 if status is not 'paused, otherwise:
|
||||
paused-time := now - last-changed-time"
|
||||
(+
|
||||
(+ (or (alist-get 'effective-start-time (alist-get 'current pomm--state)) 0)
|
||||
(pomm--get-kind-length
|
||||
|
@ -281,12 +298,27 @@ KIND is the same as in `pomm--state'"
|
|||
(format-seconds pomm-remaining-time-format time-remaining))))))
|
||||
|
||||
(defun pomm-update-mode-line-string ()
|
||||
"Update the modeline string."
|
||||
"Update the modeline string for the pomodoro timer.
|
||||
|
||||
This sets the variable `pomm-current-mode-line-string' with a value
|
||||
from `pomm-format-mode-line'. This is made so to minimize the load on
|
||||
the modeline, because otherwise updates may be quite frequent.
|
||||
|
||||
To add this to the modeline, add the following code to your config:
|
||||
\(add-to-list 'mode-line-misc-info '\(:eval pomm-current-mode-line-string\)')
|
||||
\(add-hook 'pomm-on-tick-hook 'pomm-update-mode-line-string\)
|
||||
\(add-hook 'pomm-on-tick-hook 'force-mode-line-update\)
|
||||
\(add-hook 'pomm-on-status-changed-hook 'pomm-update-mode-line-string\)
|
||||
\(add-hook 'pomm-on-status-changed-hook 'force-mode-line-update)"
|
||||
(setq pomm-current-mode-line-string (pomm-format-mode-line)))
|
||||
|
||||
;;;###autoload
|
||||
(defun pomm-start ()
|
||||
"Start or continue the pomodoro timer."
|
||||
"Start or continue the pomodoro timer.
|
||||
|
||||
- If the timer is not initialized, initialize the state.
|
||||
- If the timer is stopped, start a new iteration.
|
||||
- If the timer is paused, unpause the timer."
|
||||
(interactive)
|
||||
(unless pomm--state
|
||||
(pomm--init-state))
|
||||
|
@ -297,7 +329,8 @@ KIND is the same as in `pomm--state'"
|
|||
(alist-get 'effective-start-time (alist-get 'current pomm--state))
|
||||
(+ (alist-get 'effective-start-time (alist-get 'current pomm--state))
|
||||
(- (time-convert nil 'integer) (alist-get 'last-changed-time pomm--state)))
|
||||
(alist-get 'last-changed-time pomm--state) (time-convert nil 'integer))))
|
||||
(alist-get 'last-changed-time pomm--state) (time-convert nil 'integer)))
|
||||
((eq (alist-get 'status pomm--state) 'running) (message "The timer is running!")))
|
||||
(run-hooks 'pomm-on-status-changed-hook)
|
||||
(unless pomm--timer
|
||||
(setq pomm--timer (run-with-timer 0 1 'pomm--on-tick))))
|
||||
|
@ -305,21 +338,25 @@ KIND is the same as in `pomm--state'"
|
|||
(defun pomm-stop ()
|
||||
"Stop the current iteration of the pomodoro timer."
|
||||
(interactive)
|
||||
(pomm--store-current-to-history)
|
||||
(setf (alist-get 'status pomm--state) 'stopped
|
||||
(alist-get 'current pomm--state) nil
|
||||
(alist-get 'last-changed-time pomm--state) (time-convert nil 'integer))
|
||||
(run-hooks 'pomm-on-status-changed-hook))
|
||||
(if (eq (alist-get 'status pomm--state) 'stopped)
|
||||
(message "The timer is already stopped!")
|
||||
(pomm--store-current-to-history)
|
||||
(setf (alist-get 'status pomm--state) 'stopped
|
||||
(alist-get 'current pomm--state) nil
|
||||
(alist-get 'last-changed-time pomm--state) (time-convert nil 'integer))
|
||||
(run-hooks 'pomm-on-status-changed-hook)))
|
||||
|
||||
(defun pomm-pause ()
|
||||
"Pause the pomodoro timer."
|
||||
(interactive)
|
||||
(setf (alist-get 'status pomm--state) 'paused
|
||||
(alist-get 'last-changed-time pomm--state) (time-convert nil 'integer))
|
||||
(run-hooks 'pomm-on-status-changed-hook))
|
||||
(if (eq (alist-get 'status pomm--state) 'running)
|
||||
(progn
|
||||
(setf (alist-get 'status pomm--state) 'paused
|
||||
(alist-get 'last-changed-time pomm--state) (time-convert nil 'integer))
|
||||
(run-hooks 'pomm-on-status-changed-hook))
|
||||
(message "The timer is not running!")))
|
||||
|
||||
;;;; Transient
|
||||
|
||||
(transient-define-infix pomm--set-short-break-period ()
|
||||
:class 'transient-lisp-variable
|
||||
:variable 'pomm-short-break-period
|
||||
|
@ -353,23 +390,30 @@ KIND is the same as in `pomm--state'"
|
|||
:key "-p"
|
||||
:description "Number of work periods before long break: "
|
||||
:reader (lambda (&rest _)
|
||||
(read-number "Number of work periods before long break:"
|
||||
(read-number "Number of work periods before a long break:"
|
||||
pomm-number-of-periods)))
|
||||
|
||||
(defclass pomm--transient-current (transient-suffix)
|
||||
(transient :initform t))
|
||||
(transient :initform t)
|
||||
"A transient class to display the current state of the timer.")
|
||||
|
||||
(cl-defmethod transient-init-value ((obj pomm--transient-current))
|
||||
(cl-defmethod transient-init-value ((_ pomm--transient-current))
|
||||
"A dummy method for `pomm--transient-current'.
|
||||
|
||||
The class doesn't actually have any value, but this is necessary for transient."
|
||||
nil)
|
||||
|
||||
(defun pomm--get-kind-face (kind)
|
||||
"Get a face for a KIND of period.
|
||||
|
||||
KIND is the same as in `pomm--state'"
|
||||
(pcase kind
|
||||
('work 'success)
|
||||
('short-break 'warning)
|
||||
('long-break 'error)))
|
||||
|
||||
(cl-defmethod transient-format ((obj pomm--transient-current))
|
||||
"hello!"
|
||||
(cl-defmethod transient-format ((_ pomm--transient-current))
|
||||
"Format the state of the pomodoro timer."
|
||||
(let ((status (alist-get 'status pomm--state)))
|
||||
(if (or (eq 'stopped status) (not (alist-get 'current pomm--state)))
|
||||
"The timer is not running"
|
||||
|
@ -399,13 +443,17 @@ KIND is the same as in `pomm--state'"
|
|||
'face 'success))))))
|
||||
|
||||
(defclass pomm--transient-history (transient-suffix)
|
||||
(transient :initform t))
|
||||
(transient :initform t)
|
||||
"A transient class to display the history of the pomodoro timer.")
|
||||
|
||||
(cl-defmethod transient-init-value ((obj pomm--transient-history))
|
||||
(cl-defmethod transient-init-value ((_ pomm--transient-history))
|
||||
"A dummy method for `pomm--transient-history'.
|
||||
|
||||
The class doesn't actually have any value, but this is necessary for transient."
|
||||
nil)
|
||||
|
||||
(cl-defmethod transient-format ((obj pomm--transient-history))
|
||||
"hello!"
|
||||
(cl-defmethod transient-format ((_ pomm--transient-history))
|
||||
"Format the history list for the transient buffer."
|
||||
(if (not (alist-get 'history pomm--state))
|
||||
"No history yet"
|
||||
(let ((previous-iteration -1))
|
||||
|
@ -431,14 +479,15 @@ KIND is the same as in `pomm--state'"
|
|||
|
||||
(transient-define-infix pomm--transient-history-suffix ()
|
||||
:class 'pomm--transient-history
|
||||
;; A dummy key. Seems to be necessary for transient.
|
||||
;; Just don't press ~ while in buffer.
|
||||
:key "~~1")
|
||||
|
||||
(transient-define-infix pomm--transient-current-suffix ()
|
||||
:class 'pomm--transient-current
|
||||
:key "~~2")
|
||||
|
||||
;;;###autoload (autoload 'pomm "Pomodoro timer" nil t)
|
||||
(transient-define-prefix pomm ()
|
||||
(transient-define-prefix pomm-transient ()
|
||||
["Settings"
|
||||
(pomm--set-short-break-period)
|
||||
(pomm--set-long-break-period)
|
||||
|
@ -457,5 +506,15 @@ KIND is the same as in `pomm--state'"
|
|||
["History"
|
||||
(pomm--transient-history-suffix)])
|
||||
|
||||
;;;###autoload
|
||||
(defun pomm ()
|
||||
"A Pomodoro timer.
|
||||
|
||||
This command initialized the state of timer and triggers the transient buffer."
|
||||
(interactive)
|
||||
(unless pomm--state
|
||||
(pomm--init-state))
|
||||
(call-interactively #'pomm-transient))
|
||||
|
||||
(provide 'pomm)
|
||||
;;; pomm.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue