* 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)

Reply via email to