Change Log of TMR (tmr.el)

This document contains the release notes for each tagged commit on the project's main git repository:

The newest release is at the top. For further details, please consult the manual:

Version 0.4.0 on 2022-07-07

The general theme of this release is that TMR became simpler, better, and more robust. Daniel Mendler provided lots of patches and is now recognised as co-author of the package together with Damien Cassou and me (Protesilaos). With the exception of documentation changes and other accompanying tweaks, all of the following are courtesy of Daniel Mendler. Consult the git log for the minutia.

  • Timers can also be set using an absolute time input. For example, 21:45 will set a timer from now until the specified time. The familiar ways of starting timers with relative values, work as they did before. This is part of a wider internal revision to make the parsing of input more strict.
  • TMR no longer maintains distinct feature sets between its minibuffer and tabulated interfaces. What works in one context, works equally in the other. All commands that were formerly available only in the tmr-tabulated-mode (accessed via tmr-tabulated-view) are now implemented anew to provide the requisite minibuffer capabilities. When called from inside the tmr-tabulated-mode, the commands operate on the timer at point. Otherwise they prompt for completion among the available timers (where relevant). This covers all operations for creating, cloning, [re-]describing, rescheduling, and removing timers. The tmr-tabulated-mode-map is updated thus:

      (defvar tmr-tabulated-mode-map
        (let ((map (make-sparse-keymap)))
          (define-key map "k" #'tmr-remove)
          (define-key map "r" #'tmr-remove)
          (define-key map "R" #'tmr-remove-finished)
          (define-key map "+" #'tmr)
          (define-key map "t" #'tmr)
          (define-key map "*" #'tmr-with-description)
          (define-key map "T" #'tmr-with-description)
          (define-key map "c" #'tmr-clone)
          (define-key map "e" #'tmr-edit-description)
          (define-key map "s" #'tmr-reschedule)
        "Keybindings for `tmr-tabulated-mode-map'.")

    Similarly, our sample key bindings are these:

      ;; OPTIONALLY set your own global key bindings:
      (let ((map global-map))
        (define-key map (kbd "C-c t t") #'tmr)
        (define-key map (kbd "C-c t T") #'tmr-with-description)
        (define-key map (kbd "C-c t l") #'tmr-tabulated-view) ; "list timers" mnemonic
        (define-key map (kbd "C-c t c") #'tmr-clone)
        (define-key map (kbd "C-c t k") #'tmr-cancel)
        (define-key map (kbd "C-c t s") #'tmr-reschedule)
        (define-key map (kbd "C-c t e") #'tmr-edit-description)
        (define-key map (kbd "C-c t r") #'tmr-remove)
        (define-key map (kbd "C-c t R") #'tmr-remove-finished))
  • The tabulated view now shows the remaining time for all timer objects. This is how the *tmr-tabulated-view* buffer is formatted:

      Start      End        Remaining  Description
      10:11:49   10:11:54   ✔
      10:11:36   10:31:36   19m 35s
      10:11:32   10:26:32   14m 31s    Yet another test
      10:11:16   10:21:16   9m 14s     Testing how it works
  • All timer objects are refactored to expose a properly formatted completion table. The completion category is tmr-timer. In practical terms, embark (and other standards-compliant packages) can operate on them. The manual provides sample glue code for Embark:

      (defvar tmr-action-map
        (let ((map (make-sparse-keymap)))
          (define-key map "k" #'tmr-remove)
          (define-key map "r" #'tmr-remove)
          (define-key map "R" #'tmr-remove-finished)
          (define-key map "c" #'tmr-clone)
          (define-key map "e" #'tmr-edit-description)
          (define-key map "s" #'tmr-reschedule)
      (with-eval-after-load 'embark
        (add-to-list 'embark-keymap-alist '(tmr-timer . tmr-action-map))
         for cmd the key-bindings of tmr-action-map
         if (commandp cmd) do
         (add-to-list 'embark-post-action-hooks (list cmd 'embark--restart))))

    The Embark Wiki is updated accordingly.

  • The new user option tmr-confirm-single-timer governs how TMR should behave while operating on the sole timer. If non-nil (the default), TMR will always use the minibuffer to select a timer object to operate on, even when there is only one candidate available. If set to nil, TMR will not ask for confirmation when there is one timer available: the operation will be carried out outright. The default value is optimal for use with Embark.
  • The existing user option tmr-description-list is revised to accept either a list of strings (the old approach) or a symbol of a variable that holds a list of strings. In the latter case, this can be the tmr-description-history, which is a variable that stores the user's input at the relevant minibuffer prompt. We have made this the new default value, as it grows naturally to reflect one's usage of TMR. Minibuffer histories can persist between sessions if the user enables the built-in savehist library. Sample configuration:

      (require 'savehist)
      (setq savehist-file (locate-user-emacs-file "savehist"))
      (setq history-length 10000)
      (setq history-delete-duplicates t)
      (setq savehist-save-minibuffer-history t)
      (add-hook 'after-init-hook #'savehist-mode)
  • Fixed an edge case where a when-let* form did not return the expected value. Thanks to Nathan R. DeGruchy for the patch. The patch is below the ~15 line threshold and thus does not require copyright assignment to the Free Software Foundation.
  • Named the mailing list address as the Maintainer: of Denote. Together with the other package headers, they help the user find our primary sources and/or communication channels. This change conforms with work being done upstream in package.el by Philip Kaludercic. I was informed about it here:
  • Updated the manual to reflect the aforementioned.

Version 0.3.0 on 2022-05-17

The gist of TMR's May Release is that TMR is Maintained Rigorously—but enough with The Mostly Recursive acronyms!

  • This is the first version for which we produce a change log. The short story of previous releases: I (Protesilaos) was using and developing TMR (pronounced as "timer" or "T-M-R") as part of my personal setup for more than a year until I eventually contributed it to GNU ELPA.
  • What was once tmr.el is now split up into purpose-specific files: tmr.el (core functionality), tmr-tabulated.el (grid view), tmr-sound.el (audible notifications), and tmr-notification.el (desktop notifications).
  • The tmr-with-description command creates a new timer while always asking for a description. Whereas the standard tmr command prompts for a description only when invoked with a prefix argument.
  • The tmr-clone command copies the duration and optional description of an existing timer object into a new one. The operation is performed without further questions, except if a prefix argument is supplied: in that case the command will prompt for a duration and, if the original timer had a description, for one as well. The default values of these prompts are those of the original timer.
  • The tmr-remove-finished deletes all elapsed timers. This means that they are removed from the list of available timers and, thus, cannot be cloned.
  • The tmr-timer-created-functions, tmr-timer-completed-functions, and tmr-timer-cancelled-functions are hooks which can be used to control what happens once a timer is (i) created, (ii) elapses, or (iii) is cancelled.
  • Elapsed and running timers are displayed in a grid view with the command tmr-tabulated-view. The buffer looks like this:

      Start      End        Finished?  Description
      09:22:43   09:32:43   ✔         Prepare tea
      09:17:14   09:37:14              Boil water
      09:07:03   09:57:03              Bake bread
  • In that grid view, it is possible to create a new timer, or operate on the one at point to cancel, clone, redescribe, and reschedule it.
  • Thanks to Christian Tietze for implementing changes to how desktop notifications are handled. The overall effect should still be the same for existing users, though the implementation has been redesigned.
  • Thanks to Damien Cassou who is now is my co-author due to multiple contributions for tmr.el, the addition of the grid view, and the splitting of TMR into numerous files. Please consult the Git commit log for the details. (I still am the maintainer.)
  • Christian and Damien have assigned copyright to the Free Software Foundation. It is required for all packages distributed via GNU ELPA.

The manual documents the technicalities and provides a sample configuration. Either evaluate the form (info "(tmr) Top") if you have the package installed or visit

Version 0.2.0 on 2022-04-21

This entry is retroactively introduced on 2022-07-07.

  • Changed the tmr and tmr-cancel commands to handle a list of timers instead of only interfacing with the last timer.
  • Improved the documentation.
  • Made various internal tweaks and refinements.
  • Added TMR to the official GNU ELPA archive as tmr.

Version 0.1.0 on 2021-10-02

This entry is retroactively introduced on 2022-07-07.

TMR was originally announced on my website. The code was developed as part of my dotemacs for several months before it was placed in its own Git repo. Even before the Elisp implementation, the core idea existed as a standalone shell script, which is still part of my dotfiles.