emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Proposal: 'executable' org-capture-templaes


From: Arthur Miller
Subject: Re: Proposal: 'executable' org-capture-templaes
Date: Fri, 01 Jul 2022 01:30:16 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux)

Max Nikulin <manikulin@gmail.com> writes:

> On 26/06/2022 11:50, Arthur Miller wrote:
>> Max Nikulin writes:
>>>
>>> By state I mean some structure opaque to menu package. It just receives it 
>>> from
>>> caller as an optional argument and (when given) later passes it to
>>> handler. Maybe it is alien approach in LISP, but in C (where closures are
>>> impossible) it is a widely used technique. Functions having callback 
>>> argument
>>> usually have another void* one that is later passed as an argument of the
>>> callback function in addition to other data.
>> I understand, I have done my share of C, and know what you mean. Say
>> posix thread will take a void* to user data, and pass it on. This is
>> exactly what this is about. It is just that we don't need an extra
>> structure to pass around. We have a menu entry, which *is* the user
>> data.
>
> You a right, it is not strictly necessary that menu should be aware of state. 
> I
> expect some helper though, e.g.
>
> ;;; -*- lexical-binding: t; -*-
> (defun org-menu-multiinstance-stateful (menus &rest args)
>   (let* ((buffer-name (or (plist-get args :buffer-name)
>                         "*Select menu*"))
>        (state (plist-get args :state))
>        (handler (plist-get args :handler))
>        (handler-closure
>         (and handler
>              state
>              (lambda (entry &rest _)
>                (funcall handler entry state)))))
>     (when state (org-plist-delete args :state))
>     (when handler (org-plist-delete args :handler))
>     (plist-put args
>              :buffer-name
>              (generate-new-buffer-name buffer-name))
>     (apply #'org-select
>          (if handler-closure
>              (mapcar
>               (lambda (menu)
>                 (append menu (list :org-select-handler
>                                    handler-closure)))
>               menus)
>            menus)
>          args)))
> (provide 'org-multimenu)
>
> To be able to call menu as
>
> (load (expand-file-name "org-multimenu"))
> (org-menu-multiinstance-stateful
>  `((("1" "one" 1)
>     ("2" "two" 2)))
>  :state (format-time-string "%T")
>  :text "Some heading"
>  :buffer-name "*Test menu*"
>  :handler (lambda (entry state)
>           (org-select-quit) ; it does not kill the buffer
>           (message "handler %S %S" entry state)))

I might be missunderstanding you now, but from this example it seems to me that
you see menu entries as something that aims for the menu itself, while state is
some user data?

The "menu data" here is just: "1" and "one" in the first entry, and simillary
"2" and "two" in the second entry. After those, client code can put whatever it
desires: ("1" "one" ..... lots of client state .....). So for example client
could have something like ("1" "one" 1 (format-time-string "%T")).

However that might be tedious to type for each entry, so maybe we could pass an
optional "menu-global" state (or entry) that is passed to the user. So it would
be:

(lambda (entry &optional state)
            (org-select-quit) ; it does not kill the buffer
            (message "handler %S %S" entry state)))

Reason for optional: I prefer simplicity. I think the idea to lump together
menu selection with user state as it is done in org-capture-templates is really
good. Kudos to whomever came up with that one!

I like it because it puts everything into one place, we don't need to update
display and logic separately but everything is in an entry. I think it is good,
and I would like to keep that simplicity. I wouldnt like to suddenly separate
user state and menu state, because it would negate that fundamental idea.

> I do not like how to closures are represented in elisp stack traces
> (e.g. JavaScript engines in browsers tries hard to give some debug names for
> anonymous functions). It should not be a problem when passed handler is a 
> named
> function (not a lambda) despite closure in the adjacent frame.
>
> However I would expect that menu handler is not aware if menu is implemented
> using buffers. From my point of view handler should not have buffer argument.

I understand, and I agree it is not very beautiful design :). The problem is 
when
client code provides its own handler. In the beginning I used a flag,
org-select-transient, to signal the menu should go away, but that wasn't very
clean either. The solution is maybe to never give total control to user handler,
but to always wrap/call user handler and finnish in menu's own handler. That way
we can use a flag to signal some state. I don't know, just thinking loud at the
moment; will have to test.

> What I missed in your current implementation is ability to change text of menu
> entries in response to action. E.g. "C-c C-e" ox dispatcher has some options 
> and
> user should see current values.

The buffer text is just dead text; menu entries are a graphical display for the
user, not really interactive in the sense that it will update itself upon some
user action, unless user picks submenu or returns from one. 

>                                E.g. "C-c C-e" ox dispatcher has some options 
> and
> user should see current values.

Can that be implemented as a submenu (group node as in org-capture-templates)?

> P.S. I am sorry for long delay.

Don't worry. It was midsummer celebration here and lots of sun so I was out on
the beach with the family, not much behind the computer last weekend and entire
week. We are expecting rain next week, so I will probably work on it more over
the weekend and early next week. I have rebuild it for now, I have removed
tripple nesting and incorporated some (most) of Ihors remarks, and removed some
ugly iteration for recursion but I am not completely done with everything I wish
to do yet.

best regards
/a



reply via email to

[Prev in Thread] Current Thread [Next in Thread]