minad / consult

:mag: consult.el - Consulting completing-read
GNU General Public License v3.0
1.21k stars 102 forks source link

Feature Request: Allow for configuration of `consult-org−-narrow` heading operator #769

Closed jeremyf closed 1 year ago

jeremyf commented 1 year ago

5, I would only get level 5. As implemented, I get levels 1 through 5; which does make sense.

In an ideal situation, I would love to be able to specify the comparison function/operator (e.g. <, =, <=) for the level check. Perhaps through the UI, but also perhaps through a variable.

I think the following is what I'm envisioning:

(defvar consult-org−-narrow-level-comparision-function
  #'<=)

(defun consult-org--narrow ()
  "Narrowing configuration for `consult-org' commands."
  (let ((todo-kws
         (seq-filter
          (lambda (x) (<= ?a (car x) ?z))
          (mapcar (lambda (s)
                    (pcase-let ((`(,a ,b) (split-string s "(")))
                      (cons (downcase (string-to-char (or b a))) a)))
                  (apply #'append (mapcar #'cdr org-todo-keywords))))))
    (list :predicate
          (lambda (cand)
            (pcase-let ((`(,level ,todo . ,prio)
                         (get-text-property 0 'consult-org--heading cand)))
              (cond
               ((<= ?1 consult--narrow ?9) (funcall consult-org−-narrow-level-comparision-function level (- consult--narrow ?0)))
               ((<= ?A consult--narrow ?Z) (eq prio consult--narrow))
               (t (equal todo (alist-get consult--narrow todo-kws))))))
          :keys
          (nconc (mapcar (lambda (c) (cons c (format "Level %c" c)))
                         (number-sequence ?1 ?9))
                 (mapcar (lambda (c) (cons c (format "Priority %c" c)))
                         (number-sequence (max ?A org-highest-priority)
                                          (min ?Z org-lowest-priority)))
                 todo-kws))))
minad commented 1 year ago

Hi Jeremy, thanks for the proposal. I see your point, that it would be nice to have more flexible filtering mechanism. But I am not convinced we should go this route, in particular if we would need an additional UI to make this feature useful. We could just add the single configuration variable, but I am not too fond of adding variables for every small detail, given that you can use advices or consult-customize. Could you please explain why you would want to configure this?

The current rule is reasonable since it behaves like unfolding the tree to the given level. Narrowing to a precise level seems overly restrictive, e.g., are you sure that the heading is on level 5 or 6? The narrowing mechanism was never intended as a flexible filtering mechanism where we can modify operators or even combine predicates. The main purpose of the mechanism is to quickly narrow down to a group as in consult-buffer.

The mechanism is intentionally restricted because of its downsides. The problem of narrowing is that it is "out of band" and not part of the minibuffer input string ("in band"). This means any complex query (or filtering setting) would be lost immediately since it is not stored as part of the minibuffer input history. As an example, the Prescient package provides multiple such out-of-band toggles, which are lost as soon as you exit the minibuffer as far as I know. You mentioned that we would need UI support to toggle the comparison function, which adds a similar problem. We already have a UI with history - the minibuffer itself, so why not use that?

I am considering a query language which can be used to filter completion candidates based on arbitrary auxiliary data, e.g., candidates by their annotation string, buffers based on their mode or content, modes based on their status, etc. We could also define predicates for Org headings. In the end it would be a bit like org-ql, but for arbitrary completion contexts. You can find a prototype of such a filter at https://github.com/oantolin/orderless/issues/30#issuecomment-898196817. So far this has not materialized as a package. Besides that I have only limited time to sink into my Emacs packages, there are also a few technical problems to solve. How should the syntax look like? How should the language interact harmonically with the completion style in use? Should it be a completion style itself?

This goes further than what you proposed here, but I think it is better to stick to the restricted narrowing model for now and instead investigate other more flexible approaches like the proposed query language. Please let me know what you think!

jeremyf commented 1 year ago

Thank you for the response. I agree with your assessment and reasoning. The one off solution certainly feels out of place (why this one and only one customization?) when there is in fact a layer of abstraction that this one off solution is masking. And whether that abstraction is "worthy" of the consideration.

I have resolved my particular "itch" by leveraging previously unused priorities to help filter.

minad commented 1 year ago

The one off solution certainly feels out of place (why this one and only one customization?) when there is in fact a layer of abstraction that this one off solution is masking.

Yes, exactly. In the worst case we would also have to add configuration variables for all the other predicates as well.