0

I was using Emacs 26 and put this line on top of my org-mode file:

#+PROPERTY: Effort_ALL 0:25 0:50 1:15 1:40 2:05 2:30 2:55 3:20 3:45 4:10 4:35 5:00 5:25 5:50 6:15 6:40 7:05 7:30 7:55 8:20

This means how much "pomodoros" (25 min each) I'd like to make as an effort prediction. When I pressed C-c C-x e (aka M-x org-set-effort) I could type like 1, 2, 3, 4... until 9 to say that I want like "1 pomodoro", "2 pomodoros", "3 pomodoros" and so on.

After upgrade my Emacs to version 28 now I cannot type the number as index. I should type the entire value, like 0:50, for example. I could not type 1, 2 etc. anymore. Why not? What have changed and what can I do to back the previous behavior?

Thank you.

1

The signature of org-set-effort changed with this commit and does not allow using an index into the allowed values any longer. There is no option to restore the previous behavior. This change went in to Org mode with version 9.2. See also ORG-NEWS and the description of org-set-effort in the manual.

The new version allows completion so you can select by typing a prefix or by selecting from a completion buffer.

That said however, it seems to me that what you should be doing is to use a function that uses the units that you prefer in this case: pomodoros, not minutes. So instead of specifying that number indirectly (by pre-calculating a list of values and then using a number as an index into that list, something which is error-prone for an arbitrary list, since you have to count to figure out which value you want), specify instead the value of a pomodoro in conventional units and then use the number of pomodoros as a multiplier. For example, let's say you want 1 pomodoro = 25 minutes. Here is an implementation for that:

* effort
  :PROPERTIES:
  :Effort:   1:15
  :END:

* Code                                                          :noexport:
  #+begin_src emacs-lisp
    (defun ndk/org-set-effort-in-pomodoros (n)
      (interactive "nHow many pomodoros: ")
      (let ((mins-per-pomodoro 25))
        (org-set-effort nil (org-duration-from-minutes (* n mins-per-pomodoro)))))

    ;; redefine the key binding `C-c C-x e' which was originally bound to `org-set-effort'
    ;; to the pomodoro function
    (define-key org-mode-map (kbd "C-c C-x e") #'ndk/org-set-effort-in-pomodoros)
  #+end_src

Note that I rebind the C-c C-x e key, which was originally bound to org-set-effort, to the new function, assuming that that's going to be your main interface to set the effort (you can always call org-set-effort with M-x org-set-effort RET anyway, but you might want to bind the new function to some other key and leave the existing binding alone. If so, change the key(s) you specify in the define-key above to suit your taste, but in the following I've assumed that the original key combo has been redefined.)

If you say C-c C-x e now, you will call the ndk/org-set-effort-in-pomodoros function, which will ask you how many pomodoros this effort is worth. I answered 3 and it calculated the duration as 3*25 = 75 minutes or as a duration 1:15. There is no need to pre-calculate the list or use an index.

You might want to specify the pomodoro duration to be something other than 25 minutes. Let's imagine that you do that by defining a property MINUTES_PER_POMODORO: if that's defined, we use the property value, otherwise we use 25. The implementation is now a bit more complicated, but not by much:

#+PROPERTY: MINUTES_PER_POMODORO 9

* effort
  :PROPERTIES:
  :Effort:   0:27
  :END:
  
* Code                                                          :noexport:
  #+begin_src emacs-lisp
    (defun ndk/org-set-effort-in-pomodoros (n)
      (interactive "nHow many pomodoros: ")
      (let* ((mins-per-pomodoro-prop (org-entry-get (point) "MINUTES_PER_POMODORO" t))
             (mins-per-pomodoro (if mins-per-pomodoro-prop
                                   (string-to-number mins-per-pomodoro-prop)
                                  25)))
        (org-set-effort nil (org-duration-from-minutes (* n mins-per-pomodoro)))))

    ;; redefine the key binding `C-c C-x e' which was originally bound to `org-set-effort'
    ;;  to the pomodoro function
    (define-key org-mode-map (kbd "C-c C-x e") #'ndk/org-set-effort-in-pomodoros)
  #+end_src

You might also want to be able to specify the number of pomodoros as a prefix argument, so you can say M-3 C-c C-x e to specify 3 pomodoros. The question is what to do when you call the function without a prefix argument; the following implements a default value of 1 pomodoro in that case:

(defun ndk/org-set-effort-in-pomodoros (&optional n)
  (interactive "p")
  (setq n (or n 1)) ; if prefix arg is nil, then use the default setting of 1
  (let* ((mins-per-pomodoro-prop (org-entry-get (point) "MINUTES_PER_POMODORO" t))
         (mins-per-pomodoro (if mins-per-pomodoro-prop
                               (string-to-number mins-per-pomodoro-prop)
                              25)))
    (org-set-effort nil (org-duration-from-minutes (* n mins-per-pomodoro)))))

Or you might want to use the prefix argument if specified, but instead of using the default of 1 if not, force the user to enter a value in the minibuffer (as was done in the first two implementations):

    (defun ndk/org-set-effort-in-pomodoros (&optional n)
      (interactive "P")
      (setq n (or n (string-to-number (read-from-minibuffer "How many pomodoros: " nil nil nil nil "1" nil))))
      (let* ((mins-per-pomodoro-prop (org-entry-get (point) "MINUTES_PER_POMODORO" t))
         (mins-per-pomodoro (if mins-per-pomodoro-prop
                    (string-to-number mins-per-pomodoro-prop)
                  25)))
    (org-set-effort nil (org-duration-from-minutes (* n mins-per-pomodoro)))))

    ;; redefine the key binding `C-c C-x e' which was originally bound to `org-set-effort'
    ;;  to the pomodoro function
    (define-key org-mode-map (kbd "C-c C-x e") #'ndk/org-set-effort-in-pomodoros)

Hope this helps.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.