Further refining my org-pomodoro workflow

In a previous article, I showcased a script I made to display org-pomodoro’s current status to polybar. Since then my workflow has evolved and I have added some functionalities to this script.

First, org-pomodoro itself has evolved. There’s a new option to keep the pomodoro going when it ends, going into overtime and requiring a manual break. I like it because 25 minutes can sometimes be too short and not wanting to break my focus on the current task I would end up ignoring it for a few minutes making the break timer incorrect. You can turn on this option with :

(setq org-pomodoro-manual-break t)

I also modified my script to allow me to control org-pomodoro through clicks on the polybar modules. The new actions are :

Altogether it provides me with a convenient interface to org-pomodoro even when outside Emacs.

You’ll find all the code necessary here but I’ll go over the installation process.

To set this up you’ll need to copy the org-pomodoro.sh script. I use doom-emacs and rely on one of it’s functions to create a pop-up window with the wanted capture template. I have however provided in the script an alternative for non doom users that relies on org-protocol so you’ll need to set that up properly if needed. To use simply change to false DOOM_EMACS variable at the top of the script.

Then you’ll need to configure polybar to use the pomicons font and you’ll need to create a module to call the script. The argument of the call determines the action so you can bind them to any type of click or wheel-scroll. Available arguments are :

You can find my module configuration in the gist.

For new to work properly you’ll need two org-capture templates. Your usual to-do template and another template marked with the :pomodoro t property. So for example :

(setq org-capture-templates
      '(("t" "Todo" entry (file+headline "~/org/todo.org" "Inbox")
         "* TODO %?\n  %i\n  %a")
        ("z" "Pomodoro" entry (file+headline "~/org/todo.org" "Inbox")
         "* TODO %?\n  %i\n  %a" :pomodoro t)))

;; or for doom users you can use the built in t template and add another like so :
(after! org
  (appendq! org-capture-templates
            '(("z" "Pomodoro" entry ; Used for polybar integration
               (file+headline +org-capture-todo-file "Inbox")
               "* [ ] %?\n%i\n%a" :prepend t :kill-buffer t :pomodoro t))))

And now we have to make this :pomodoro t property actually do something. To do so I wrote a function that will be run at the end of a capture through a hook :

(defun +org-pomodoro/start-pomodoro-on-capture ()
  "Starts org-pomodoro upon capture if the pomodoro capture template was used"

  (when (and (not org-note-abort)
             (equal (org-capture-get :pomodoro) t))
    (when (and org-pomodoro-last-clock-in
               org-pomodoro-expiry-time
               (org-pomodoro-expires-p))
      (setq org-pomodoro-count 0))
    (set-buffer (org-capture-get :buffer))
      (goto-char (org-capture-get :insertion-point))
      (org-clock-in)
      (org-pomodoro-start :pomodoro)))

(add-hook 'org-capture-after-finalize-hook #'+org-pomodoro/start-pomodoro-on-capture)

What this does is check that the capture was not aborted and that the templates has the :pomodoro t property and if it does it will reset the pomodoro count if necessary, go to the new headline, start the clock and finally start the pomodoro.

For the end-or-restart command I wrote a function that basically behaves the same as org-pomodoro except there is no prompt, to avoid the command getting stuck on it and it uses org-clock-in-last meaning it automatically uses the last clocked item instead of requiring to be on a headline or having another prompt. So we also need to add it to our config :

(defun +org-pomodoro/restart-last-pomodoro ()
  "Starts a new pomodoro on the last clocked-in task. Resets the pomodoro count without prompt when necessary.

  This is useful for external scripts as the org-pomodoro function has y-or-n prompts"
  (when (and org-pomodoro-last-clock-in
             org-pomodoro-expiry-time
             (org-pomodoro-expires-p))
    (setq org-pomodoro-count 0))
  (setq org-pomodoro-last-clock-in (current-time))

  (call-interactively 'org-clock-in-last)
  (org-pomodoro-start :pomodoro))

This is called from the script so if you change it’s name don’t forget to modify it in the script.

That’s it for the emacs side of things. All that’s left is to configure the script org-pomodoro.sh through the variables at the top of the file.

Finally if you use a tiling window manager capable of this, it’s probably a good idea to set the windows created by the script as floating. They are named “org-capture”. Here’s how to do it in i3 :

for_window [title="org-capture"] floating enable