From 2acf51043c6fe0b67bdc74cc10ae98fcb9c69414 Mon Sep 17 00:00:00 2001 From: SqrtMinusOne Date: Wed, 10 Jul 2024 00:36:26 +0300 Subject: [PATCH] pomm: add org-clock integration --- README.org | 12 ++++++++++++ pomm-third-time.el | 17 +++++++++++++++++ pomm.el | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/README.org b/README.org index c66176b..bf02fd8 100644 --- a/README.org +++ b/README.org @@ -134,6 +134,18 @@ To customize timestamp, set the =pomm-csv-history-file-timestamp-format= variabl #+end_src The format is the same as in =format-time-string=. +** Usage with =org-clock= +The package can be used with [[https://orgmode.org/manual/Clocking-commands.html][org-clock]] in the following way. Set up these two hooks: + +#+begin_src emacs-lisp +(add-hook 'pomm-on-status-changed-hook #'pomm--sync-org-clock) +(add-hook 'pomm-third-time-on-status-changed-hook + #'pomm-third-time--sync-org-clock +#+end_src + +Then, start the timer (either =pomm= or =pomm-third-time=) and =org-clock-in=, in whichever order. The package will call =org-clock-out= when a break starts and =org-clock-in-last= when it ends. + +Setting =pomm-org-clock-in-immediately= to =nil= "defers" calling =org-clock-in-last= until after any command from the user (via =post-command-hook=). I've added this because I occasionally return to my PC a few minutes after the break ends, so I don't want these minutes to show up in =org-clock=. * Alternatives There is a number of packages with a similar purpose, here is a rough comparison of features: diff --git a/pomm-third-time.el b/pomm-third-time.el index 1d9ffbc..5051b4a 100644 --- a/pomm-third-time.el +++ b/pomm-third-time.el @@ -438,6 +438,23 @@ Take a look at the `pomm-third-time' function for more details." (setf (alist-get 'context pomm-third-time--state) (prin1-to-string (read-minibuffer "Context: " (current-word))))) +(defun pomm-third-time--sync-org-clock () + "Sync org-clock with the pomodoro timer." + (let* ((status (alist-get 'status pomm-third-time--state)) + (kind (alist-get 'kind (alist-get 'current pomm-third-time--state))) + (active-p (and (eq kind 'work) + (eq status 'running))) + (resume-next-time-p (not (eq status 'stopped)))) + (cond + ((and active-p (not org-clock-current-task) + pomm--sync-org-clock-was-stopped) + (if pomm-org-clock-in-immediately + (org-clock-in-last) + (pomm--org-clock-in-last-after-action))) + ((and (not active-p) org-clock-current-task) + (org-clock-out) + (setq pomm--sync-org-clock-was-stopped resume-next-time-p))))) + ;;;###autoload (defun pomm-third-time-start-with-context () "Prompt for context call call `pomm-third-time-start'." diff --git a/pomm.el b/pomm.el index c2b6470..7d7eee4 100644 --- a/pomm.el +++ b/pomm.el @@ -209,6 +209,13 @@ Each element of the list is a cons cell, where: :group 'pomm :type 'hook) +(defcustom pomm-org-clock-in-immediately t + "Run `org-clock-in-last' immediately after the break ends. + +If nil, the clock in happens after the first command." + :group 'pomm + :type 'boolean) + (defvar pomm--state nil "The current state of the Pomodoro timer. @@ -536,6 +543,37 @@ minor mode." (remove-hook 'pomm-on-status-changed-hook #'pomm-update-mode-line-string) (remove-hook 'pomm-on-status-changed-hook #'force-mode-line-update)))) +(defvar pomm--sync-org-clock-was-stopped nil + "If t, `pomm--sync-org-clock' had stopped `org-clock.") + +(defun pomm--org-clock-in-last-after-action () + "Run `org-clock-in-last' after some action by the user. + +This exists because I sometimes return to PC after a the break ends." + (add-hook 'post-command-hook #'pomm--org-clock-in-last-and-remove-from-hook)) + +(defun pomm--org-clock-in-last-and-remove-from-hook () + "Run `org-clock-in-last' and remove self from the `post-command-hook'." + (org-clock-in-last) + (remove-hook 'post-command-hook #'pomm--org-clock-in-last-and-remove-from-hook)) + +(defun pomm--sync-org-clock () + "Sync org-clock with the pomodoro timer." + (let* ((status (alist-get 'status pomm--state)) + (kind (alist-get 'kind (alist-get 'current pomm--state))) + (active-p (and (eq kind 'work) + (eq status 'running))) + (resume-next-time-p (not (eq status 'stopped)))) + (cond + ((and active-p (not org-clock-current-task) + pomm--sync-org-clock-was-stopped) + (if pomm-org-clock-in-immediately + (org-clock-in-last) + (pomm--org-clock-in-last-after-action))) + ((and (not active-p) org-clock-current-task) + (org-clock-out) + (setq pomm--sync-org-clock-was-stopped resume-next-time-p))))) + ;;;###autoload (defun pomm-start () "Start or continue the pomodoro timer.