I wonder if there is any easy way to configure effort selection dialogue to allow selecting effort value from the list using single key.

Currently I have configured Effort_ALL in the following way:

(add-to-list 'org-global-properties
         '("Effort_ALL". "0:05 0:15 0:30 1:00 2:00"))

and am using org-set-effort to pop up the dialogue. However the dialogue that it shows is just a regular narrow-list-of-candidates dialogue which takes multiple keystrokes (either arrows or partial string followed by return) to select the option I need.

What I would rather have instead is a org-fast-tag-selection-single-key-style dialogue that allows to select an entry from the list by just pressing a single button (e.g. 1, 2, 3, etc).

I've tried to look at the code for the org-set-effort and google for the ivy-like list-narrowing modules, but so far wasn't able to find much.


I'm just getting into using effort properties myself and stumbled on your question. Here is a function modified from org-fast-todo-selection which maps the index of the effort value to that number key (e.q. 0 -> 0:05 and 1 -> 0:15 per your provided EFFORT_ALL property).

(defun org-fast-effort-selection ()
  "Modification of `org-fast-todo-selection' for use with org-set-effert. Select an effort value with single keys.
Returns the new effort value, or nil if no state change should occur.

Motivated by https://emacs.stackexchange.com/questions/59424/org-set-effort-fast-effort-selection"
  ;; Format effort values into an alist keyed by index
  (let* ((fulltable (seq-map-indexed (lambda (e i) (cons (car e) (string-to-char (int-to-string i))))
                                     (org-property-get-allowed-values nil org-effort-property t)))
         (maxlen (apply 'max (mapcar
                              (lambda (x)
                                (if (stringp (car x)) (string-width (car x)) 0))
         (expert (equal org-use-fast-todo-selection 'expert))
         (prompt "")
         (fwidth (+ maxlen 3 1 3))
         (ncol (/ (- (window-width) 4) fwidth))
         tg cnt e c tbl subtable)
        (if expert
            (set-buffer (get-buffer-create " *Org effort"))
          (set-window-buffer (split-window-vertically) (get-buffer-create " *Org effort*"))
          (org-switch-to-buffer-other-window " *Org effort*"))
        (setq tbl fulltable cnt 0)
        (while (setq e (pop tbl))
          (setq tg (car e)
                c (cdr e))
          (print (char-to-string c))
          (when (and (= cnt 0))
            (insert "  "))
          (setq prompt (concat prompt "[" (char-to-string c) "] " tg " "))
          (insert "[" c "] " tg (make-string
                                 (- fwidth 4 (length tg)) ?\ ))
          (when (and (= (setq cnt (1+ cnt)) ncol)
                     ;; Avoid lines with just a closing delimiter.
                     (not (equal (car tbl) '(:endgroup))))
            (insert "\n")
            (setq cnt 0)))
        (insert "\n")
        (goto-char (point-min))
        (unless expert (org-fit-window-to-buffer))
        (message (concat "[1-9..]:Set [SPC]:clear"
                         (if expert (concat "\n" prompt) "")))
        (setq c (let ((inhibit-quit t)) (read-char-exclusive)))
        (setq subtable (nreverse subtable))
         ((or (= c ?\C-g)
              (and (= c ?q) (not (rassoc c fulltable))))
          (setq quit-flag t))
         ((= c ?\ ) nil)
         ((setq e (or (rassoc c subtable) (rassoc c fulltable))
                tg (car e))
         (t (setq quit-flag t)))))))

Using with org-set-effort and binding to a key:

(define-key org-mode-map
  (kbd "C-M-e") (lambda () (interactive) (org-set-effort nil (org-fast-set-effort))))

Update 07/27/2020

A possibly less intrusive solution is to add a case to org-set-effort where, if org-use-fast-todo-selection is set to 'expert, use org-fast-effort-selection as defined above.

The advantages are that you won't have to bind it to a specific key, and it will also work with org-agenda-set-effort out of the box.

I made a commit in my fork of org-mode showing how this can be done. After I use it a bit to make sure there's no edge cases that are being missed, I may submit it to the org-mode maintainers.

| improve this answer | |
  • Thanks much! Will need to ponder the code a bit more to figure out the specifics, but it definitely does the job – whoever Jul 6 at 21:56
  • Had to wrap it in some additional helpers to make it work from the agenda view (otherwise it was complaining that the agenda buffer is read-only) ``` (defun invoke-fast-effort-selection () (interactive) (save-excursion (save-window-excursion (org-agenda-switch-to) (org-set-effort nil (org-fast-set-effort)))) (org-agenda-redo)) ``` Maybe you can add it to the answer as well? – whoever Jul 8 at 11:52

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.