* Summary
When using ~%^{CATEGORY}p~ in an org-capture template, the minibuffer
completion list is empty even though ~CATEGORY_ALL~ is defined in
~org-global-properties~. This works for ~EFFORT~ (~%^{EFFORT}p~) but
not for ~CATEGORY~.
* Reproducible Example
Minimal Emacs configuration:
#+begin_src emacs-lisp
(setq org-global-properties
'(("EFFORT_ALL" . "0:30 1:00 2:00")
("CATEGORY_ALL" . "Work Personal Meeting")))
(setq org-capture-templates
'(("t" "Test Task" entry (file+headline "/tmp/test.org" "Tasks")
"* TODO %?\n:PROPERTIES:\n:EFFORT: %^{EFFORT}p\n:CATEGORY:
%^{CATEGORY}p\n:END:\n")))
#+end_src
1. Open Emacs with the above configuration.
2. Call ~org-capture~ and select the "Test Task" template.
3. When prompted for EFFORT, a completion list appears with "0:30", "1:00",
"2:00".
4. When prompted for CATEGORY, a plain text prompt appears with *no* completion
list.
* Expected Behaviour
~%^{CATEGORY}p~ should present the values defined in ~CATEGORY_ALL~ as
completion candidates, just like ~%^{EFFORT}p~ does for ~EFFORT_ALL~.
* Root Cause
In ~org-property-get-allowed-values~ (org.el, ~13688~), there is an
explicit ~cond~ clause for the CATEGORY property:
#+begin_src emacs-lisp
(cond
((equal property "TODO") ...)
((equal property "PRIORITY") ...)
((equal property "CATEGORY")) ;; <-- short-circuits here
((member property org-special-properties))
((setq vals (run-hook-with-args-until-success
'org-property-allowed-value-functions property)))
(t
(setq vals (org-entry-get epom (concat property "_ALL") 'inherit))
...))
#+end_src
The clause ~((equal property "CATEGORY"))~ matches and returns
immediately with no body. The ~vals~ variable is never set, so the
fallthrough that looks up ~CATEGORY_ALL~ via ~org-entry-get~ with
~'inherit~ is never reached.
This means ~org-global-properties~ containing ~CATEGORY_ALL~ is
effectively ignored for property completion.
* Suggested Fix
Remove the CATEGORY short-circuit or add a body that resolves
~CATEGORY_ALL~ the same way the default clause does:
#+begin_src emacs-lisp
((equal property "CATEGORY")
(setq vals (org-entry-get epom (concat property "_ALL") 'inherit))
(when (and vals (string-match "\\S-" vals))
(setq vals (car (read-from-string (concat "(" vals ")"))))
(setq vals (mapcar (lambda (x)
(cond ((stringp x) x)
((numberp x) (number-tostring x))
((symbolp x) (symbol-name x))
(t "???")))
vals))))
#+end_src
Alternatively, moving the CATEGORY clause below the
~org-property-allowed-value-functions~ hook check would allow users to
extend CATEGORY via that hook without patching org.el.
* Workaround
Advise ~org-property-get-allowed-values~ to bypass the short-circuit:
#+begin_src emacs-lisp
(defun mk/allow-category-all (orig-fn epom property &optional table)
"Allow CATEGORY to resolve CATEGORY_ALL from org-global-properties."
(if (equal property "CATEGORY")
(let ((vals (org-entry-get epom "CATEGORY_ALL" 'inherit)))
(when (and vals (string-match "\\S-" vals))
(setq vals (car (read-from-string (concat "(" vals ")"))))
(setq vals (mapcar (lambda (x)
(cond ((stringp x) x)
((numberp x) (number-tostring x))
((symbolp x) (symbol-name x))
(t "???")))
vals)))
(if table (mapcar 'list vals) vals))
(funcall orig-fn epom property table)))
(advice-add 'org-property-get-allowed-values :around #'mk/allow-category-all)
#+end_src
* Environment
- Emacs version: 30.1
- Org mode version: 9.8
- OS: GNU/Linux (Fedora)