Version 0.1.2 of package Do-At-Point has just been released in GNU ELPA.
You can now find it in M-x list-packages RET.
Do-At-Point describes itself as:
============================================
Generic context-sensitive action dispatcher.
============================================
More at https://elpa.gnu.org/packages/do-at-point.html
## Summary:
The command `do-at-point' is a generalised `find-file-at-point',
both in the sense that it can understand more than just files, and
do more than just open a file. Depending on the "thing" at point,
different "actions" can be dispatched, e.g. opening a url using
`browse-url' or occurring a symbol at point.
The entry point of this package is `do-at-point'. Bind it to a
convenient key:
(global-set-key (kbd "C-'") #'do-at-point)
Most of the behaviour is controlled via the user option
`do-at-point-actions' and `do-at-point-user-actions'. A mode may
use `do-at-point-local-actions' to add additional things and/or
actions.
## Recent NEWS:
Version 0.1.2
- New general actions: "delete-region" and "yank-and-swap".
- New hook `do-at-point-mode-hook'.
- New minor mode `do-at-point-persist-mode', bound to M-<return> by
default. If enabled, it disables the disactivation of selections
after an action. To enable it by default, evaluate
(add-hook 'do-at-point-hook #'do-at-point-persist-mode)
- Allow customising the quick-confirm key (by Visuwesh).
Hi Philip,
This reminds me of (the core of) Hyperbole.
Am I confused or there's indeed some overlap? If I'm not confused,
could you add some brief comparison to it in the `Commentary:`?
Stefan
ELPA update [2024-06-26 05:04:10] wrote:
> Version 0.1.2 of package Do-At-Point has just been released in GNU ELPA.> You can now find it in M-x list-packages RET.>> Do-At-Point describes itself as:>> ============================================> Generic context-sensitive action dispatcher.> ============================================>> More at https://elpa.gnu.org/packages/do-at-point.html>> ## Summary:>> The command `do-at-point' is a generalised `find-file-at-point',> both in the sense that it can understand more than just files, and> do more than just open a file. Depending on the "thing" at point,> different "actions" can be dispatched, e.g. opening a url using> `browse-url' or occurring a symbol at point.>> The entry point of this package is `do-at-point'. Bind it to a> convenient key:>> (global-set-key (kbd "C-'") #'do-at-point)>> Most of the behaviour is controlled via the user option> `do-at-point-actions' and `do-at-point-user-actions'. A mode may> use `do-at-point-local-actions' to add additional things and/or> actions.>> ## Recent NEWS:>> Version 0.1.2>> - New general actions: "delete-region" and "yank-and-swap".>> - New hook `do-at-point-mode-hook'.>> - New minor mode `do-at-point-persist-mode', bound to M-<return> by> default. If enabled, it disables the disactivation of selections> after an action. To enable it by default, evaluate>> (add-hook 'do-at-point-hook #'do-at-point-persist-mode)>> - Allow customising the quick-confirm key (by Visuwesh).
Stefan Monnier <monnier@iro.umontreal.ca> writes:
> Hi Philip,
Hi,
> This reminds me of (the core of) Hyperbole.> Am I confused or there's indeed some overlap? If I'm not confused,> could you add some brief comparison to it in the `Commentary:`?
I am not familiar with Hyperbole, or at least I have never understood
what it does. I took a more serious look and found this summary:
Automatically match text in all buffers, to actions; example: if a URL
is seen, a special action can open a browser to visit it; for files,
open the file; for org-mode files, open them up and jump to the
referred section, etc. No need for special markup.
Would you say that it is a fair summary?
[0] https://old.reddit.com/r/emacs/comments/vhc8l1/gnu_hyperbole_many_have_heard_of_it_but_what_does/>> Stefan>>> ELPA update [2024-06-26 05:04:10] wrote:>>> Version 0.1.2 of package Do-At-Point has just been released in GNU ELPA.>> You can now find it in M-x list-packages RET.>>>> Do-At-Point describes itself as:>>>> ============================================>> Generic context-sensitive action dispatcher.>> ============================================>>>> More at https://elpa.gnu.org/packages/do-at-point.html>>>> ## Summary:>>>> The command `do-at-point' is a generalised `find-file-at-point',>> both in the sense that it can understand more than just files, and>> do more than just open a file. Depending on the "thing" at point,>> different "actions" can be dispatched, e.g. opening a url using>> `browse-url' or occurring a symbol at point.>>>> The entry point of this package is `do-at-point'. Bind it to a>> convenient key:>>>> (global-set-key (kbd "C-'") #'do-at-point)>>>> Most of the behaviour is controlled via the user option>> `do-at-point-actions' and `do-at-point-user-actions'. A mode may>> use `do-at-point-local-actions' to add additional things and/or>> actions.>>>> ## Recent NEWS:>>>> Version 0.1.2>>>> - New general actions: "delete-region" and "yank-and-swap".>>>> - New hook `do-at-point-mode-hook'.>>>> - New minor mode `do-at-point-persist-mode', bound to M-<return> by>> default. If enabled, it disables the disactivation of selections>> after an action. To enable it by default, evaluate>>>> (add-hook 'do-at-point-hook #'do-at-point-persist-mode)>>>> - Allow customising the quick-confirm key (by Visuwesh).>
--
Philip Kaludercic on peregrine
>> This reminds me of (the core of) Hyperbole.>> Am I confused or there's indeed some overlap? If I'm not confused,>> could you add some brief comparison to it in the `Commentary:`?>> I am not familiar with Hyperbole, or at least I have never understood> what it does. I took a more serious look and found this summary:https://www.gnu.org/software/hyperbole/ has a few videos which try and
give an idea of what it does. I hate videos but IIRC I did watch the
"Overview and Demo" at some point (when I was losing hope to ever
understand what it is that this thing does).
> Automatically match text in all buffers, to actions; example: if a URL> is seen, a special action can open a browser to visit it; for files,> open the file; for org-mode files, open them up and jump to the> referred section, etc. No need for special markup.>> Would you say that it is a fair summary?
I'm not very familiar either, but yes, it sounds like a fair summary.
So it does sound to me like "do at point". Hyperbole is a fairly big
monster, tho, but IIUC it's because of a large number of mode-specific
hacks for all kinds of situation-specific notions of "do" at
specific points.
The interesting part, to me, being whether Do-At-Point can be made to do
those same things (given enough ad-hoc configuration), and if not, why.
[ And vice versa, whether Hyperbole can be made to do what Do-At-Point
can do, and if not why not. ]
So I think it'd be instructive to try and take some mode-specific
support from Hyperbole and see if it can be "translated" to Do-At-Point.
BTW, I think EEV might be related as well.
Stefan
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>>> This reminds me of (the core of) Hyperbole.>>> Am I confused or there's indeed some overlap? If I'm not confused,>>> could you add some brief comparison to it in the `Commentary:`?>>>> I am not familiar with Hyperbole, or at least I have never understood>> what it does. I took a more serious look and found this summary:>> https://www.gnu.org/software/hyperbole/ has a few videos which try and> give an idea of what it does. I hate videos but IIRC I did watch the> "Overview and Demo" at some point (when I was losing hope to ever> understand what it is that this thing does).
I tried watching those as well but my attitude to videos is mostly the
same as yours. I have some idea, but I'd really have to invest the time
to try it out and that is the difficult part, especially if I am not
interested in the package per se, but just to provide a comparison.
>> Automatically match text in all buffers, to actions; example: if a URL>> is seen, a special action can open a browser to visit it; for files,>> open the file; for org-mode files, open them up and jump to the>> referred section, etc. No need for special markup.>>>> Would you say that it is a fair summary?>> I'm not very familiar either, but yes, it sounds like a fair summary.> So it does sound to me like "do at point". Hyperbole is a fairly big> monster, tho, but IIUC it's because of a large number of mode-specific> hacks for all kinds of situation-specific notions of "do" at> specific points.>> The interesting part, to me, being whether Do-At-Point can be made to do> those same things (given enough ad-hoc configuration), and if not, why.> [ And vice versa, whether Hyperbole can be made to do what Do-At-Point> can do, and if not why not. ]> So I think it'd be instructive to try and take some mode-specific> support from Hyperbole and see if it can be "translated" to Do-At-Point.
My understanding is that Hyperbole actually annotates the buffers, and
recognises specific parts, attaching specific actions. The difference
with Do-At-Point is that the "thing" being operated on has to be
manually selected first, and then an action is dispatched. Both of
these steps are manual.
> BTW, I think EEV might be related as well.
EEV seems like another "big idea" package that I never seriously looked at.
>> Stefan>
Stefan Monnier <monnier@iro.umontreal.ca> writes:
> According to> https://www.reddit.com/r/emacs/comments/nirwpk/my_understanding_of_gnu_hyperbole/> your package might also be related to Embark.
That is true and I acknowledge that explicitly in the commentary section:
Inspired by Embark and `isearch-forward-thing-at-point'.
as this is the more direct inspiration. I wanted some kind of simple
generalised find-file-at-point, that used thing-at-point, and
Do-At-Point is the result of that experiment.
>> Stefan>
--
Philip Kaludercic on peregrine
> I tried watching those as well but my attitude to videos is mostly the> same as yours. I have some idea, but I'd really have to invest the time> to try it out and that is the difficult part, especially if I am not> interested in the package per se, but just to provide a comparison.
🙂
>> BTW, I think EEV might be related as well.> EEV seems like another "big idea" package that I never seriously looked at.
🙂
>> According to>> https://www.reddit.com/r/emacs/comments/nirwpk/my_understanding_of_gnu_hyperbole/>> your package might also be related to Embark.> That is true and I acknowledge that explicitly in the commentary section:> Inspired by Embark and `isearch-forward-thing-at-point'.
Ah, I missed that, sorry.
I think I'm hoping for some kind of unification between these various
packages, maybe in the form of a middle layer where you can
provide "patterns" to recognize specific "things" as well associating
"actions" to particular kinds of "things", and then different packages
can provide different UIs on top of that info (e.g. where some UIs may
scan the buffer to highlight some "things" while others only look for
a "thing" around point on-demand, and some UIs may provide a menu to
choose which action to take while others only focus on running "the
usual action").
I'm not sufficiently familiar with any of these packages to have a clear
idea of how feasible this is, admittedly.
Stefan
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>>> According to>>> https://www.reddit.com/r/emacs/comments/nirwpk/my_understanding_of_gnu_hyperbole/>>> your package might also be related to Embark.>> That is true and I acknowledge that explicitly in the commentary section:>> Inspired by Embark and `isearch-forward-thing-at-point'.>> Ah, I missed that, sorry.>> I think I'm hoping for some kind of unification between these various> packages, maybe in the form of a middle layer where you can> provide "patterns" to recognize specific "things" as well associating> "actions" to particular kinds of "things", and then different packages> can provide different UIs on top of that info (e.g. where some UIs may> scan the buffer to highlight some "things" while others only look for> a "thing" around point on-demand, and some UIs may provide a menu to> choose which action to take while others only focus on running "the> usual action").>> I'm not sufficiently familiar with any of these packages to have a clear> idea of how feasible this is, admittedly.
Knowing Do-At-Point and being familiar with Embark, I could imagine that
extending thing-at-point by some system that would link actions to
objects could be imaginable. I am not sure if I mentioned it in my last
message, but Omar told me he was interested in re-using thing-at-point
for Embark, but that he felt it wasn't reliable enough yet. I have
noticed a few peculiarities that have or should be fixed at some point,
and perhaps I can get parts of Do-At-Point to be moved into the core to
provide an abstract foundation for the idea you propose.
(Though one should note that Embark does more than Do-At-Point, as the
package also extends the `completing-read' interface by actions on
completions, and extracting completions into a static, but actionable
buffer. I think that these features, especially the last one is
something that the notion of completion in Emacs should support OOTB,
without the hacks that Embark employs. I extracted this snippet to
demonstrate the basic idea
--8<---------------cut here---------------start------------->8---
(let ((command 'rename-file)
(input "~/.bashrc"))
(minibuffer-with-setup-hook
(lambda ()
(delete-minibuffer-contents)
(insert input))
(let ((unread-command-events '(?\C-m))
(this-command command))
(command-execute command))))
--8<---------------cut here---------------end--------------->8---
and while I appreciate it as a hack, there should be a better way to do
these things IMO).
>> Stefan>
--
Philip Kaludercic on peregrine
> Knowing Do-At-Point and being familiar with Embark, I could imagine that> extending thing-at-point by some system that would link actions to> objects could be imaginable.
Yeah, I think this is the core idea, indeed.
> I am not sure if I mentioned it in my last message, but Omar told me> he was interested in re-using thing-at-point for Embark, but that he> felt it wasn't reliable enough yet.
The code of `thingatpt.el` used to be pretty horrible.
It improved over time but I think it's still pretty bad.
[ `forward-thing` looks pretty poor, for one. ]
Also, I think it should be extended so that it doesn't just start from
a position but instead starts from a region (which by default is the
empty region at point). This way it could be used for `expand-region`.
> I have noticed a few peculiarities that have or should be fixed at> some point, and perhaps I can get parts of Do-At-Point to be moved> into the core to provide an abstract foundation for the idea> you propose.
I like the sound of it.
> (Though one should note that Embark does more than Do-At-Point, as the> package also extends the `completing-read' interface by actions on> completions, and extracting completions into a static, but actionable> buffer.
What do you mean by "extracting completions into a static, but
actionable buffer"?
> I think that these features, especially the last one is> something that the notion of completion in Emacs should support OOTB,> without the hacks that Embark employs. I extracted this snippet to> demonstrate the basic idea>> --8<---------------cut here---------------start------------->8---> (let ((command 'rename-file)> (input "~/.bashrc"))> (minibuffer-with-setup-hook> (lambda ()> (delete-minibuffer-contents)> (insert input))> (let ((unread-command-events '(?\C-m))> (this-command command))> (command-execute command))))> --8<---------------cut here---------------end--------------->8---
This hack is awful enough that I don't understand what it's supposed
to do, so I guess that means I agree that we need a better alternative.
Stefan
Stefan Monnier <monnier@iro.umontreal.ca> writes:
> The code of `thingatpt.el` used to be pretty horrible.> It improved over time but I think it's still pretty bad.> [ `forward-thing` looks pretty poor, for one. ]>> Also, I think it should be extended so that it doesn't just start from> a position but instead starts from a region (which by default is the> empty region at point). This way it could be used for `expand-region`.
So would you say that it would make sense to continue building on
thing-at-point, or should one consider a generalised-thing-at-point,
that would have the advantage of retrospective? Perhaps we can get some
help from Omar here.
>> I have noticed a few peculiarities that have or should be fixed at>> some point, and perhaps I can get parts of Do-At-Point to be moved>> into the core to provide an abstract foundation for the idea>> you propose.>> I like the sound of it.>>> (Though one should note that Embark does more than Do-At-Point, as the>> package also extends the `completing-read' interface by actions on>> completions, and extracting completions into a static, but actionable>> buffer.>> What do you mean by "extracting completions into a static, but> actionable buffer"?
Embark has this command called "embark-collect" that can be invoked
during completing-read. It aborts the completion while pulling out all
the results of `all-completions' into a separate buffer. In this
buffer, you can press RET and continue the operation. So for example,
you type
M-x
M-x embark-collect
[ the completion aborts and a buffer pops up with command ]
RET
[ the command at point is handled by execute-extended-command, while
the buffer remains in place ]
C-n
RET
[ some other command is executed ]
the same works with find-file, switch-to-buffer, ...
>> I think that these features, especially the last one is>> something that the notion of completion in Emacs should support OOTB,>> without the hacks that Embark employs. I extracted this snippet to>> demonstrate the basic idea>>>> --8<---------------cut here---------------start------------->8--->> (let ((command 'rename-file)>> (input "~/.bashrc"))>> (minibuffer-with-setup-hook>> (lambda ()>> (delete-minibuffer-contents)>> (insert input))>> (let ((unread-command-events '(?\C-m))>> (this-command command))>> (command-execute command))))>> --8<---------------cut here---------------end--------------->8--->> This hack is awful enough that I don't understand what it's supposed> to do, so I guess that means I agree that we need a better alternative.
It executes a command, but before doing to it sets up a temporary hook
that will inject some `input' into the minibuffer. The
`unread-command-events' makes sure that as soon as the command is done,
and Emacs would usually wait for user input, it would simulate a RET,
confirming the input that was just inserted into the minibuffer (the
mots robust approach would be to look up the key for
`minibuffer-complete-and-exit'). So you can think of it as a kind of
apply for interactive commands.
Here is another example
--8<---------------cut here---------------start------------->8---
(defun apply-interactivly (command &rest args)
"Call COMMAND interactivly and simulating input strings ARGS in order."
(let ((inject (lambda ()
(delete-minibuffer-contents)
(insert (pop args)))))
(add-hook 'minibuffer-setup-hook inject)
(unwind-protect
(let ((unread-command-events (make-list (length args) ?\C-m)))
(command-execute command))
(remove-hook 'minibuffer-setup-hook inject))))
(defun prompt-twice (a b)
(interactive "sA: \nsB: ")
(message "A: %s, B: %s" a b))
(apply-interactivly 'prompt-twice "foo" "bar")
--8<---------------cut here---------------end--------------->8---
Just now I realise that this what embark-collect does. It remembers the
command, and then invokes this kind of a procedure on the items in the
buffer, replicating some kind of a continuation approach.
Examples this, or consult-lines (it prompts the user for a line in the
current buffer, and appends line information to each selection to
distinguish these textually), demonstrate a gap between what
completing-read formally provides and the UI/UK that people want to use.
(As an aisde: I mention a "selecting-read" interface from time to time
on the mailing list, and still have a sketch[0] of how it could look
like lying around. Fundamentally the idea is not to have selection
operate on text, but remember the objects that have text
representations. Furthermore, I think it points to some interesting
extension, such as hierarchical presentation of options, persistent
presentation of options (as with embark-collect), selecting multiple
objects to operate on and annotating completion with structured data. A
neat consequence-example is that it makes a kind of tree-view selection
for a file system a trivial variation of a "regular" file prompt, with
the potential to provide a uniform interface. I argue that all of this
underlies the acknowledgement of the semantic distinction between the
expansion of partial input and the selection of objects (usually by
means of narrowing some search-space). The former emulates the latter
well enough, if the map of objects being selected to their textual
representations is injective. This is given when selecting commands (or
symbols in general), file names, buffers, etc. but not necessarily for
Imenu. I still have to find the time and motivation to tackle this
question properly, and even though I have tried to convince people like
Daniel Mendler to collaborate, there has not been too much interest :/
[0] https://paste.sr.ht/~pkal/c926a37e4270e8c67ab20ff1e75f117d5107a8a5)
>> Stefan>
--
Philip Kaludercic on peregrine
> So would you say that it would make sense to continue building on> thing-at-point, or should one consider a generalised-thing-at-point,> that would have the advantage of retrospective?
I think we should devise a clean "core API" by which I mean the way to
define new "things" along with the way to find them and some way to
categorize the "things" in order to associate them with actions.
Then we can make other packages use/obey it, slowly obsoleting previous
ways to specify "things".
The packages I know of which should hopefully be able to make use of it are:
- thing-at-point
- expand-region
- expreg
- do-at-point
- embark
- ffap
- hyperbole
- eev
[ as well as non-packages: ]
- up-list
- context-menu-functions
>> What do you mean by "extracting completions into a static, but>> actionable buffer"?>> Embark has this command called "embark-collect" that can be invoked> during completing-read. It aborts the completion while pulling out all> the results of `all-completions' into a separate buffer. In this> buffer, you can press RET and continue the operation. So for example,> you type>> M-x> M-x embark-collect> [ the completion aborts and a buffer pops up with command ]> RET> [ the command at point is handled by execute-extended-command, while> the buffer remains in place ]> C-n> RET> [ some other command is executed ]>> the same works with find-file, switch-to-buffer, ...
Ah, I see, thanks. A bit reminiscent of Helm.
> Just now I realise that this what embark-collect does. It remembers the> command, and then invokes this kind of a procedure on the items in the> buffer, replicating some kind of a continuation approach.
Continuation sounds like the right term, indeed, thanks.
> Examples this, or consult-lines (it prompts the user for a line in the> current buffer, and appends line information to each selection to> distinguish these textually), demonstrate a gap between what> completing-read formally provides and the UI/UK that people want to use.
Right, tho it's only "accidentally" linked to completion.
It's not completely clear how to do this kind of thing cleanly.
I see two alternate ways:
- Black box: Add some kind of support for continuations, so things like
`embark-collect` don't need to know what command is being executed
and "it just works".
- Transparent box: make `call-interactively` and `interactive` more
declarative so things like `embark-collect` can know what command
we're running and in which part of the interactive spec we currently
are (which previous args have already been read, for instance), how to
"get back", and how to provide specific arguments while still reading
further args if needed. And UIs like Helm can use that same info to
call commands in different ways.
> (As an aisde: I mention a "selecting-read" interface from time to time> on the mailing list, and still have a sketch[0] of how it could look
I'm a bit ambivalent about that: OT1H, it's clearly important to
recognize the difference between the two (and admit the fact that
current non-Emacs UIs have largely decided to focus on selection rather
than completion), OTOH in the context of a text-oriented tool it's hard
to come up with good concrete examples where a non-injective map from
objects to their text representation doesn't suffer from various other
shortcomings, so in practice our completion API "does the job" for those
cases as well (with some caveats, but with the advantage of keeping
a single API).
A new API would work best if added to Emacs' core, but if so, I think
it'd need to be fairly clean and simple *and* come with at least one or
two concrete uses.
I'll send you feedback on your sketch of the API in a separate email.
[ If I forget, please remind me (unless you don't want my feedback,
of course 🙂). ]
Stefan
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>> So would you say that it would make sense to continue building on>> thing-at-point, or should one consider a generalised-thing-at-point,>> that would have the advantage of retrospective?>> I think we should devise a clean "core API" by which I mean the way to> define new "things" along with the way to find them and some way to> categorize the "things" in order to associate them with actions.> Then we can make other packages use/obey it, slowly obsoleting previous> ways to specify "things".>> The packages I know of which should hopefully be able to make use of it are:>> - thing-at-point> - expand-region> - expreg> - do-at-point> - embark> - ffap> - hyperbole> - eev> [ as well as non-packages: ]> - up-list> - context-menu-functions
That sounds like a good list. So the idea would be abstract enough, so
that the definition of a thing could be given by tree-sitter, some
regular expressions or a elisp function, right?
>>> What do you mean by "extracting completions into a static, but>>> actionable buffer"?>>>> Embark has this command called "embark-collect" that can be invoked>> during completing-read. It aborts the completion while pulling out all>> the results of `all-completions' into a separate buffer. In this>> buffer, you can press RET and continue the operation. So for example,>> you type>>>> M-x>> M-x embark-collect>> [ the completion aborts and a buffer pops up with command ]>> RET>> [ the command at point is handled by execute-extended-command, while>> the buffer remains in place ]>> C-n>> RET>> [ some other command is executed ]>>>> the same works with find-file, switch-to-buffer, ...>> Ah, I see, thanks. A bit reminiscent of Helm.>>> Just now I realise that this what embark-collect does. It remembers the>> command, and then invokes this kind of a procedure on the items in the>> buffer, replicating some kind of a continuation approach.>> Continuation sounds like the right term, indeed, thanks.
Yes, just that it doesn't involve continuations at all ^^. (Out of
curiosity, because I don't have any experience with the topic and I
understand that you do: How difficult would it be for an existing Lisp
like Elisp to gain a call/cc-like functionality? What would be the
specific difficulties.)
>> Examples this, or consult-lines (it prompts the user for a line in the>> current buffer, and appends line information to each selection to>> distinguish these textually), demonstrate a gap between what>> completing-read formally provides and the UI/UK that people want to use.>> Right, tho it's only "accidentally" linked to completion.>> It's not completely clear how to do this kind of thing cleanly.> I see two alternate ways:>> - Black box: Add some kind of support for continuations, so things like> `embark-collect` don't need to know what command is being executed> and "it just works".
Do we really need continuation, or wouldn't closures be enough? I'd
imagine that associating each object with an action (a function that
takes an argument), would be enough.
> - Transparent box: make `call-interactively` and `interactive` more> declarative so things like `embark-collect` can know what command> we're running and in which part of the interactive spec we currently> are (which previous args have already been read, for instance), how to> "get back", and how to provide specific arguments while still reading> further args if needed. And UIs like Helm can use that same info to> call commands in different ways.>>> (As an aisde: I mention a "selecting-read" interface from time to time>> on the mailing list, and still have a sketch[0] of how it could look>> I'm a bit ambivalent about that: OT1H, it's clearly important to> recognize the difference between the two (and admit the fact that> current non-Emacs UIs have largely decided to focus on selection rather> than completion), OTOH in the context of a text-oriented tool it's hard> to come up with good concrete examples where a non-injective map from> objects to their text representation doesn't suffer from various other> shortcomings, so in practice our completion API "does the job" for those> cases as well (with some caveats, but with the advantage of keeping> a single API).
Yeah, in most cases it works out, but whenever I see people using
completing-read on a alist and then looking up the item in the alist, I
see someone who actually wants some other API.
Another issue is that when people think of completing-read as a way for
a user to select an option, e.g. `TeX-command-master' gives me
--8<---------------cut here---------------start------------->8---
21 possible completions:
LaTeX View File Ps2pdf Index Check Clean
BibTeX Print Dvips LaTeXMk upMendex ChkTeX Clean All
Biber Queue Dvipdfmx Glossaries Xindy Spell Other
--8<---------------cut here---------------end--------------->8---
For some reason, and I haven't articulated this yet properly, I dislike
this kind of an approach. Perhaps it would be better to use a keymap or
`read-multiple-choice'?
> A new API would work best if added to Emacs' core, but if so, I think> it'd need to be fairly clean and simple *and* come with at least one or> two concrete uses.
I agree 100%.
> I'll send you feedback on your sketch of the API in a separate email.> [ If I forget, please remind me (unless you don't want my feedback,> of course 🙂). ]
It is not really necessary, since that POC is almost three years old,
and I'd do a number of things differently now. But if you have general
ideas on how this kind of an API could look like, I'm glad to hear about
it.
--
Philip Kaludercic on peregrine
> That sounds like a good list. So the idea would be abstract enough, so> that the definition of a thing could be given by tree-sitter, some> regular expressions or a elisp function, right?
Well, that's what needs to be chosen/designed. I haven't looked enough
at the details to have a clear idea. I can't see a good reason why we'd
need a special case for tree-sitter, tho. Or rather, if there's
a "tree-sitter thingy" as an option, then it should be possible to use
any other "thingy" (as long as you provide the corresponding methods,
presumably).
>>>> What do you mean by "extracting completions into a static, but>>>> actionable buffer"?>>>>>> Embark has this command called "embark-collect" that can be invoked>>> during completing-read. It aborts the completion while pulling out all>>> the results of `all-completions' into a separate buffer. In this>>> buffer, you can press RET and continue the operation. So for example,>>> you type>>>>>> M-x>>> M-x embark-collect>>> [ the completion aborts and a buffer pops up with command ]>>> RET>>> [ the command at point is handled by execute-extended-command, while>>> the buffer remains in place ]>>> C-n>>> RET>>> [ some other command is executed ]>>>>>> the same works with find-file, switch-to-buffer, ...>>>> Ah, I see, thanks. A bit reminiscent of Helm.>>>>> Just now I realise that this what embark-collect does. It remembers the>>> command, and then invokes this kind of a procedure on the items in the>>> buffer, replicating some kind of a continuation approach.>>>> Continuation sounds like the right term, indeed, thanks.>> Yes, just that it doesn't involve continuations at all ^^. (Out of> curiosity, because I don't have any experience with the topic and I> understand that you do: How difficult would it be for an existing Lisp> like Elisp to gain a call/cc-like functionality? What would be the> specific difficulties.)
call/cc involves "capturing" the current stack of activation frames so
we can "return" several times. IOW, it would need us to implement
something like "fork" except it only keeps a copy of the "stack" and not
of the whole memory.
Also, we're really talking about *delimited* continuations, i.e. you
only want to capture the top N frames of the stack, more specifically
the frames since the beginning of the current command and now.
I suspect it's doable to a large extent: capturing a continuation would
involve a `memcpy` of a chunk of each of the C stack, the specpdl stack,
the bytecode stack and the handler stack. Then calling a continuation
would re-install those stack chunks, replacing the current ones
(actually, probably *swapping* them, so that when the continuation
finishes it can return to the old stack, i.e. the caller of the
continuation). It would require some low-level (asm?) code to set the
C stack pointer, and it would inherently make unportable assumptions
about the shape of the C stack, but I think these are the same
assumptions we make in the "conservative stack scanning" part of the GC
(which we'd have to adjust to also scan conservatively the copied chunks
of the C stack).
Sounds like a fun project, now that I think about it. 🙂
It may prove tricky/brittle/unreliable.
>> Right, tho it's only "accidentally" linked to completion.>>>> It's not completely clear how to do this kind of thing cleanly.>> I see two alternate ways:>>>> - Black box: Add some kind of support for continuations, so things like>> `embark-collect` don't need to know what command is being executed>> and "it just works".>> Do we really need continuation, or wouldn't closures be enough? I'd> imagine that associating each object with an action (a function that> takes an argument), would be enough.
The problem is to know what it is that is supposed to happen "normally"
after you finish entering the current minibuffer input.
`this-command` tells you something about it, but if that command takes
more than one argument, you don't have access to the other arguments we
already read. And you have no guarantee that `call-interactively` will
open up the same minibuffer we're currently in (whether it prompts the
user or not may depend on variables that may change between calls).
> Another issue is that when people think of completing-read as a way for> a user to select an option, e.g. `TeX-command-master' gives me>> --8<---------------cut here---------------start------------->8---> 21 possible completions:> LaTeX View File Ps2pdf Index Check Clean> BibTeX Print Dvips LaTeXMk upMendex ChkTeX Clean All> Biber Queue Dvipdfmx Glossaries Xindy Spell Other> --8<---------------cut here---------------end--------------->8---
[ FWIW, I use `tex-compile` instead. ]
> For some reason, and I haven't articulated this yet properly, I dislike> this kind of an approach. Perhaps it would be better to use a keymap or> `read-multiple-choice'?
Some people may say "transient"?
[ Personally I feel like transient is nice but it lacks the "UI
variety" of completion. ]
Stefan
> I'll send you feedback on your sketch of the API in a separate email.> [ If I forget, please remind me (unless you don't want my feedback,> of course 🙂). ]
[ I had a sudden urge to procrastinate. ]
> (defcustom selecting-read-indent 1> "Number of columns to indent subtrees by."> :type 'number)
My first comment is that it's important to separate the API from the UI:
there will be several UIs, so the one implemented here should just be
"an example" and the separation should be very clear in the code
Two separate files would be ideal from that standpoint tho it might
not be the most convenient, but the parts should be separated at least
by `;;;` sections and the API should come first with no reference to the
UI code (i.e. even the `defcustom` for the UI should come after the API).
I'll try and refrain from commenting on the UI.
> (defvar selecting-read-function #'selecting-read-default> "Function called by `selecting-read'.> This function must accept two variables LIST and OPTIONS.> LIST may be a list or an alist.> OPTIONS may consist of the following keys:>> :multiple>> If non-nil, `selecting-read' will return multiple results in> form of a list.")
Not sure if I like `:multiple` compared to a separate
`selecting-read-multiple`. Also I suspect in many cases "multiple" is
because we want a set rather than a list, in which case the UI could use
"checkboxes" but the above API doesn't really allow that (or doesn't
distinguish the set vs list cases).
IOW, I think the "multiple" case might be too complex to be able to get
it right at first and might deserve a different UI, so I'd keep it for
a later extension.
Another issue is the fact you require a list. I think it'd be more
convenient to be able to pass some abstract object (call it "choices")
from which the list can be generated dynamically via some `defgeneric`
(`all-completions` is fundamentally a generic function).
> ;;; object framework> (cl-defgeneric selecting-read-represent (object)> "Return a string to represent OBJECT.")
I see a problem here in that this is global.
Say we first want to `selecting-read` a face and then `selecting-read`
a function. The "objects" may be symbols (or strings) in both cases,
but we may want to represent them differently.
I don't think it's a deal breaker, but it means this generic function
should be only a "default representer function" and the caller should
probably be able to to provide its own "representer function" to
override it.
Maybe we can get the same result if we provide the original "choices"
object as extra argument, so we can stash the "representer function"
inside the "choices" object.
The other problem is "Return a string": UIs will likely want to be able
to offer richer representations , e.g. where each object has several
pieces of data, neatly displayed in columns. We can't easily align the
columns if each object is rendered as a single string.
Accommodating all the cases quickly gets us far from "a clean & simple"
API, admittedly, so maybe it's OK to start simple, but we should expect
this to morph into "Return a list of strings" or something like that.
OTOH, we do need a function that turns a function into a single string,
for the purpose of searching/filtering.
> (cl-defgeneric selecting-read-properties (object)> "Return a plist of properties for OBJECT."> (ignore object))
[ Plist are convenient when written by hand literally but alist are
a better option from a programmatic point of view. ]
More importantly, I have no idea what this is for.
> (cl-defgeneric selecting-read-children (object)> "Return a list of children for OBJECT."> (ignore object))
Hmm... so the "list" argument to `selecting-read` is really a tree (or
more generally graph)? I wonder how that might impact UIs (this is
related to the `completion-bounds` monster).
I think I'd rather skip this in a first "clean & simple" first cut.
> (cl-defgeneric selecting-read-flags (object)> "Return a list of symbols indicating flags for OBJECT."> (ignore object))
I have no idea what this is for either.
Stefan
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>>>>> What do you mean by "extracting completions into a static, but>>>>> actionable buffer"?>>>>>>>> Embark has this command called "embark-collect" that can be invoked>>>> during completing-read. It aborts the completion while pulling out all>>>> the results of `all-completions' into a separate buffer. In this>>>> buffer, you can press RET and continue the operation. So for example,>>>> you type>>>>>>>> M-x>>>> M-x embark-collect>>>> [ the completion aborts and a buffer pops up with command ]>>>> RET>>>> [ the command at point is handled by execute-extended-command, while>>>> the buffer remains in place ]>>>> C-n>>>> RET>>>> [ some other command is executed ]>>>>>>>> the same works with find-file, switch-to-buffer, ...>>>>>> Ah, I see, thanks. A bit reminiscent of Helm.>>>>>>> Just now I realise that this what embark-collect does. It remembers the>>>> command, and then invokes this kind of a procedure on the items in the>>>> buffer, replicating some kind of a continuation approach.>>>>>> Continuation sounds like the right term, indeed, thanks.>>>> Yes, just that it doesn't involve continuations at all ^^. (Out of>> curiosity, because I don't have any experience with the topic and I>> understand that you do: How difficult would it be for an existing Lisp>> like Elisp to gain a call/cc-like functionality? What would be the>> specific difficulties.)>> call/cc involves "capturing" the current stack of activation frames so> we can "return" several times. IOW, it would need us to implement> something like "fork" except it only keeps a copy of the "stack" and not> of the whole memory.>> Also, we're really talking about *delimited* continuations, i.e. you> only want to capture the top N frames of the stack, more specifically> the frames since the beginning of the current command and now.>> I suspect it's doable to a large extent: capturing a continuation would> involve a `memcpy` of a chunk of each of the C stack, the specpdl stack,> the bytecode stack and the handler stack. Then calling a continuation> would re-install those stack chunks, replacing the current ones> (actually, probably *swapping* them, so that when the continuation> finishes it can return to the old stack, i.e. the caller of the> continuation). It would require some low-level (asm?) code to set the> C stack pointer, and it would inherently make unportable assumptions> about the shape of the C stack, but I think these are the same> assumptions we make in the "conservative stack scanning" part of the GC> (which we'd have to adjust to also scan conservatively the copied chunks> of the C stack).>> Sounds like a fun project, now that I think about it. 🙂> It may prove tricky/brittle/unreliable.
Yes, and as I said, I am not sure that we want to use this for a
completion system in the first place, or if we can replicate what is
necessary with simpler means.
(Also thank you for the explanation, I have encountered these concepts a
number of times during my studies or on the internet, but your sketch of
continuation involve in the context of Emacs was very informative and
make a lot of sense to me!)
>>> Right, tho it's only "accidentally" linked to completion.>>>>>> It's not completely clear how to do this kind of thing cleanly.>>> I see two alternate ways:>>>>>> - Black box: Add some kind of support for continuations, so things like>>> `embark-collect` don't need to know what command is being executed>>> and "it just works".>>>> Do we really need continuation, or wouldn't closures be enough? I'd>> imagine that associating each object with an action (a function that>> takes an argument), would be enough.>> The problem is to know what it is that is supposed to happen "normally"> after you finish entering the current minibuffer input.>> `this-command` tells you something about it, but if that command takes> more than one argument, you don't have access to the other arguments we> already read. And you have no guarantee that `call-interactively` will> open up the same minibuffer we're currently in (whether it prompts the> user or not may depend on variables that may change between calls).
It might be that we shouldn't think about replacing completing-read as a
function that you can invoke and that returns a value -- which remains a
fair use-case.
I could also imagine that we define selecting-read prompts as commands,
using some kind of a `define-selection' macro or function. This would
make sense, if we decided that it a selection prompt should only be
invoked interactively at the top-level.
>> Another issue is that when people think of completing-read as a way for>> a user to select an option, e.g. `TeX-command-master' gives me>>>> --8<---------------cut here---------------start------------->8--->> 21 possible completions:>> LaTeX View File Ps2pdf Index Check Clean>> BibTeX Print Dvips LaTeXMk upMendex ChkTeX Clean All>> Biber Queue Dvipdfmx Glossaries Xindy Spell Other>> --8<---------------cut here---------------end--------------->8--->> [ FWIW, I use `tex-compile` instead. ]
I don't use the command either, at least since discovering
`TeX-command-run-all'.
>> For some reason, and I haven't articulated this yet properly, I dislike>> this kind of an approach. Perhaps it would be better to use a keymap or>> `read-multiple-choice'?>> Some people may say "transient"?> [ Personally I feel like transient is nice but it lacks the "UI> variety" of completion. ]
Perhaps, but my issue with transient is that it seems to take over the
MVC of Emacs, breaking some of the usual interaction patterns I'd like
to expect, like being able to search a buffer with C-s.
>> Stefan>
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>> I'll send you feedback on your sketch of the API in a separate email.>> [ If I forget, please remind me (unless you don't want my feedback,>> of course 🙂). ]>> [ I had a sudden urge to procrastinate. ]>>> (defcustom selecting-read-indent 1>> "Number of columns to indent subtrees by.">> :type 'number)>> My first comment is that it's important to separate the API from the UI:> there will be several UIs, so the one implemented here should just be> "an example" and the separation should be very clear in the code> Two separate files would be ideal from that standpoint tho it might> not be the most convenient, but the parts should be separated at least> by `;;;` sections and the API should come first with no reference to the> UI code (i.e. even the `defcustom` for the UI should come after the API).>> I'll try and refrain from commenting on the UI.
1+
>> (defvar selecting-read-function #'selecting-read-default>> "Function called by `selecting-read'.>> This function must accept two variables LIST and OPTIONS.>> LIST may be a list or an alist.>> OPTIONS may consist of the following keys:>>>> :multiple>>>> If non-nil, `selecting-read' will return multiple results in>> form of a list.")>> Not sure if I like `:multiple` compared to a separate> `selecting-read-multiple`. Also I suspect in many cases "multiple" is> because we want a set rather than a list, in which case the UI could use> "checkboxes" but the above API doesn't really allow that (or doesn't> distinguish the set vs list cases).>> IOW, I think the "multiple" case might be too complex to be able to get> it right at first and might deserve a different UI, so I'd keep it for> a later extension.
I can imagine that it is not worth the complexity to describe everything
in a single interface.
> Another issue is the fact you require a list. I think it'd be more> convenient to be able to pass some abstract object (call it "choices")> from which the list can be generated dynamically via some `defgeneric`> (`all-completions` is fundamentally a generic function).
Agree.
>> ;;; object framework>> (cl-defgeneric selecting-read-represent (object)>> "Return a string to represent OBJECT.")>> I see a problem here in that this is global.> Say we first want to `selecting-read` a face and then `selecting-read`> a function. The "objects" may be symbols (or strings) in both cases,> but we may want to represent them differently.>> I don't think it's a deal breaker, but it means this generic function> should be only a "default representer function" and the caller should> probably be able to to provide its own "representer function" to> override it.
I noticed this issue as well,
> Maybe we can get the same result if we provide the original "choices"> object as extra argument, so we can stash the "representer function"> inside the "choices" object.>> The other problem is "Return a string": UIs will likely want to be able> to offer richer representations , e.g. where each object has several> pieces of data, neatly displayed in columns. We can't easily align the> columns if each object is rendered as a single string.> Accommodating all the cases quickly gets us far from "a clean & simple"> API, admittedly, so maybe it's OK to start simple, but we should expect> this to morph into "Return a list of strings" or something like that.>> OTOH, we do need a function that turns a function into a single string,> for the purpose of searching/filtering. >>> (cl-defgeneric selecting-read-properties (object)>> "Return a plist of properties for OBJECT.">> (ignore object))>> [ Plist are convenient when written by hand literally but alist are> a better option from a programmatic point of view. ]> More importantly, I have no idea what this is for.>> (cl-defgeneric selecting-read-children (object)>> "Return a list of children for OBJECT.">> (ignore object))>> Hmm... so the "list" argument to `selecting-read` is really a tree (or> more generally graph)? I wonder how that might impact UIs (this is> related to the `completion-bounds` monster).> I think I'd rather skip this in a first "clean & simple" first cut.>>> (cl-defgeneric selecting-read-flags (object)>> "Return a list of symbols indicating flags for OBJECT.">> (ignore object))>> I have no idea what this is for either.>>> Stefan>
--
Philip Kaludercic on peregrine
My previous email was sent-off to early;
Philip Kaludercic <philipk@posteo.net> writes:
[...]
>>> ;;; object framework>>> (cl-defgeneric selecting-read-represent (object)>>> "Return a string to represent OBJECT.")>>>> I see a problem here in that this is global.>> Say we first want to `selecting-read` a face and then `selecting-read`>> a function. The "objects" may be symbols (or strings) in both cases,>> but we may want to represent them differently.>>>> I don't think it's a deal breaker, but it means this generic function>> should be only a "default representer function" and the caller should>> probably be able to to provide its own "representer function" to>> override it.>> I noticed this issue as well,
and I am not sure if using cl-defgeneric is the best idea. A hack to
fix this would be to recommend defining custom methods via `head', but
that wouldn't be efficient.
>> Maybe we can get the same result if we provide the original "choices">> object as extra argument, so we can stash the "representer function">> inside the "choices" object.>>>> The other problem is "Return a string": UIs will likely want to be able>> to offer richer representations , e.g. where each object has several>> pieces of data, neatly displayed in columns. We can't easily align the>> columns if each object is rendered as a single string.>> Accommodating all the cases quickly gets us far from "a clean & simple">> API, admittedly, so maybe it's OK to start simple, but we should expect>> this to morph into "Return a list of strings" or something like that.>>>> OTOH, we do need a function that turns a function into a single string,>> for the purpose of searching/filtering.
Right.
>>> (cl-defgeneric selecting-read-properties (object)>>> "Return a plist of properties for OBJECT.">>> (ignore object))>>>> [ Plist are convenient when written by hand literally but alist are>> a better option from a programmatic point of view. ]>> More importantly, I have no idea what this is for.
IIRC the idea was to provide some way to provide structured data. So
for example, a file-selection could specify attributes like permissions,
mtime, owner, inode, etc. A UI could then provide a filter UI that
takes a string like
user:george perm:^rwx /file_name_including_foo
>>> (cl-defgeneric selecting-read-children (object)>>> "Return a list of children for OBJECT.">>> (ignore object))>>>> Hmm... so the "list" argument to `selecting-read` is really a tree (or>> more generally graph)? I wonder how that might impact UIs (this is>> related to the `completion-bounds` monster).>> I think I'd rather skip this in a first "clean & simple" first cut.
Morally it should be a tree, but I recognise that it might turn out to
be a graph... Either way, I think that this is important, because it is
a common UI element that a lot of selection interfaces replicate,
e.g. RefTeX, Ibuffer, Gnus all have hierarchical presentations.
>>> (cl-defgeneric selecting-read-flags (object)>>> "Return a list of symbols indicating flags for OBJECT.">>> (ignore object))>>>> I have no idea what this is for either.
I am not sure either (as I said, I don't stand by the concrete
implementation), I believe they could have been used as a kind of
boolean properties.
--
Philip Kaludercic on peregrine
>> Sounds like a fun project, now that I think about it. 🙂>> It may prove tricky/brittle/unreliable.
Having considered it a bit more, I confirm: it would probably be fun
(and shouldn't require any assembly code) but it's probably too
unreliable to be of much use: too many continuations would include code
which frees up something allocated earlier, so that when we call that
continuation a second time we'd get double-free and dangling pointers.
We can come up with ways to adjust the code to make it work, but it
require changes where the `xfree` happens so it would require pervasive
changes rather than being neatly confined to `call/cc`.
[ And not just to the C code: similar problems can occur (tho without
causing segfaults) with `unwind-protect`, of course, which is why
Scheme's equivalent (`dynamic-wind`) is more complex specifying how to
"come back inside". BTW, we have somewhat similar needs already in
`backtrace-eval`. ]
>> Some people may say "transient"?>> [ Personally I feel like transient is nice but it lacks the "UI>> variety" of completion. ]> Perhaps, but my issue with transient is that it seems to take over the> MVC of Emacs, breaking some of the usual interaction patterns I'd like> to expect, like being able to search a buffer with C-s.
+1
Having other UIs for it would be nice.
>> IOW, I think the "multiple" case might be too complex to be able to get>> it right at first and might deserve a different UI, so I'd keep it for>> a later extension.> I can imagine that it is not worth the complexity to describe everything> in a single interface.
+1
Stefan
>>>> (cl-defgeneric selecting-read-properties (object)>>>> "Return a plist of properties for OBJECT.">>>> (ignore object))>>> [ Plist are convenient when written by hand literally but alist are>>> a better option from a programmatic point of view. ]>>> More importantly, I have no idea what this is for.> IIRC the idea was to provide some way to provide structured data. So> for example, a file-selection could specify attributes like permissions,> mtime, owner, inode, etc. A UI could then provide a filter UI that> takes a string like>> user:george perm:^rwx /file_name_including_foo
Ah, right, just was I was complaining about w.r.t the previous method 🙂
Makes sense, but for the UI to be able to use it, there needs to be some
kind of convention about which properties can appear and which values
they can take. That can depend on something else (like a "category" of
selection), but we need some kind of convention here otherwise the UI
can't know how to use that info.
>>> Hmm... so the "list" argument to `selecting-read` is really a tree (or>>> more generally graph)? I wonder how that might impact UIs (this is>>> related to the `completion-bounds` monster).>>> I think I'd rather skip this in a first "clean & simple" first cut.>> Morally it should be a tree, but I recognise that it might turn out to> be a graph... Either way, I think that this is important, because it is> a common UI element that a lot of selection interfaces replicate,> e.g. RefTeX, Ibuffer, Gnus all have hierarchical presentations.
Hmm, yeah, I guess Imenu is there as well. I agree it's an important
use case, but it's also a much more complex one, with many different
possible UIs and where some choices aren't very compatibles.
E.g. one might want to offer a UI that doesn't force the user to go
"level by level" but instead directly matches the user's input against
all the possible leaves and their parents, but that doesn't always work
well with "virtually infinite" trees like file systems.
This is basically the whole motivation behind the
`completion-boundaries` horror. 99% of the completion tables don't need
it (and most of the UI implementers take a while to understand why we
might need it and by the time they understand it, it's difficult for
them to retrofit support for it into their code). Maybe the problem
will be simpler for selection than it is for completion, but
just: beware!
>>>> (cl-defgeneric selecting-read-flags (object)>>>> "Return a list of symbols indicating flags for OBJECT.">>>> (ignore object))>>>>>> I have no idea what this is for either.>> I am not sure either (as I said, I don't stand by the concrete> implementation),
🙂
Stefan
Hi all,
I found this discussion interesting and I thought I could share a few
cents in hopes that it will be useful.
[வியாழன் ஜூன் 27, 2024] Stefan Monnier wrote:
> The interesting part, to me, being whether Do-At-Point can be made to do> those same things (given enough ad-hoc configuration), and if not, why.> [ And vice versa, whether Hyperbole can be made to do what Do-At-Point> can do, and if not why not. ]> So I think it'd be instructive to try and take some mode-specific> support from Hyperbole and see if it can be "translated" to Do-At-Point.
I didn't go as far as mode-specific "things" but I started defining
"personal" "things" for do-at-point when I was using it. The main
insufficiency I found was a lack of buffer-local version of
bounds-of-thing-at-point but this is addressed in Emacs 30 with the
introduction of bounds-of-thing-at-point-provider-alist [1]. Otherwise,
do-at-point (or thing-at-point) is sufficiently flexible.
Things are easier to define with thing-at-point-looking-at but it would
be nice if the DISTANCE argument could be replaced with a more flexible
BEG..END to specify the region for which the regexp should be searched
for. Otherwise, it is hard to exactly restrict the search to e.g., the
current line.
The focus of do-at-point and Hyperbole are orthogonal AFAIU. Hyperbole
functions more like xref in that it is more focused on jumping to things
whereas do-at-point is like context-menu-mode. IOW, do-at-point can
replace Hyperbole but not the converse. A bridge between these two
paradigms is what the command embark-dwim aims to do I think.
> BTW, I think EEV might be related as well.
I think Hyperbole and EEV differs in that the former is more focused on
"implicit" links whereas EEV forces you to write explicit links. I far
prefer the Hyperbole approach since I can follow links in e.g., shell
command output a la compilation-mode. But Hyperbole lets you define
"explicit" links too.
[செவ்வாய் ஜூலை 02, 2024] Stefan Monnier wrote:
>> So would you say that it would make sense to continue building on>> thing-at-point, or should one consider a generalised-thing-at-point,>> that would have the advantage of retrospective?>> I think we should devise a clean "core API" by which I mean the way to> define new "things" along with the way to find them and some way to> categorize the "things" in order to associate them with actions.> Then we can make other packages use/obey it, slowly obsoleting previous> ways to specify "things".
I would say thing-at-point is mostly fine but it would be so much more
better if (1) non-string things could be returned, and (2) non-string
returns could be clearly communicated to the caller. This should make
it possible to return things "backed" by tree-sitter, org-element, etc.
I also wish for a way to signal to do-at-point alike interfaces that
returning BEG..END for the action is impossible. I faced this when
returning things by looking at text-properties. Currently,
(bounds-of-thing-at-point 'url)
when point is over on a bug-reference reference returns the bounds of
bug#NNN but this is nonsensical. "bug#NNN" only alludes to a URL, it is
not the URL itself so this return value makes no sense IMO. See again
[1].
> The packages I know of which should hopefully be able to make use of it are:>> - thing-at-point> - expand-region> - expreg> - do-at-point> - embark> - ffap> - hyperbole> - eev> [ as well as non-packages: ]> - up-list> - context-menu-functions
Xref might fit the bill too with xref-backend-identifier-at-point. Here
too, I specifically wish for non-string identifiers so that I can attach
metadata to the thing, or simply to pass parsed objects instead of
strings to e.g., xref-backend-definitions. I had to do some unnecessary
roundtrips in a xref backend I wrote for reftex (see [2]). I believe
this would simplify a future xref backend for org-mode too. I envision
something like xref-item with a "NAME" field for identifiers. The NAME
field could be used in completion, etc. Although, I am not sure how
practical this really is.
[ Personally, having used xref for a Hyperbole-inspired backend [3] I
think it might be a feasible replacement for Hyperbole. ]
1. https://lists.sr.ht/~pkal/public-inbox/%3C87ttqe3skf.fsf@gmail.com%3E#%3C8734xaw2j5.fsf@gmail.com%3E
and the message it quotes.
2. https://gist.github.com/9viz/c9bd136013db853774080ccd8825babf
Sorry for GitHub.
3. Well, really Plan 9's plumber inspired.
> The focus of do-at-point and Hyperbole are orthogonal AFAIU. Hyperbole> functions more like xref in that it is more focused on jumping to things> whereas do-at-point is like context-menu-mode. IOW, do-at-point can> replace Hyperbole but not the converse. A bridge between these two> paradigms is what the command embark-dwim aims to do I think.
Oh, indeed, the aim and UIs of those different packages can be quite
different and I don't necessarily want to unify all that (there are too
many tradeoffs involved).
I'm rather focusing on some parts of the internals which I hope could
benefit from some unification. AFAICT, taking a kind of naîve view, all
those packages can be a mix of 3 layers:
- Bottom layer specifies what things are and how to find them.
- Middle layer specifies what actions can be performed for each thing.
- Top layer provides a useful UI for it.
I suspect that the bottom layers can benefit from being unified.
I don't think unifying the top layers would make much sense in general
(there might be opportunities here and there, of course, but that's not
what I'm after).
As for the middle layer, it's still quite murky in my mind. I hope it
can also benefit from some unification, but unifying the bottom layer
seems to be a prerequisite for that in any case.
> I would say thing-at-point is mostly fine but it would be so much more> better if (1) non-string things could be returned, and (2) non-string> returns could be clearly communicated to the caller.
Yeah, I was thinking of returning "BOUNDS + TYPE".
> This should make it possible to return things "backed" by tree-sitter,> org-element, etc.
"BOUNDS + TYPE" is not ideal for them (since it takes work to recover
the actual tree node from the bounds). We could also imagine returning
something more abstract and then have a method to get the bounds from
the return value.
[ Many of the UIs would want to know the bounds, tho (e.g. to
highlight the thing, to compare/order the various things-at-point,
...). ]
> (bounds-of-thing-at-point 'url)>> when point is over on a bug-reference reference returns the bounds of> bug#NNN but this is nonsensical. "bug#NNN" only alludes to a URL, it is> not the URL itself so this return value makes no sense IMO. See again> [1].
Sounds like a bug, indeed, because it's not a URL: the fact we can compute
a URL for it doesn't make it a URL.
>> The packages I know of which should hopefully be able to make use of it are:>>>> - thing-at-point>> - expand-region>> - expreg>> - do-at-point>> - embark>> - ffap>> - hyperbole>> - eev>> [ as well as non-packages: ]>> - up-list>> - context-menu-functions>> Xref might fit the bill too with xref-backend-identifier-at-point.
Quite right, thanks!
> Here too, I specifically wish for non-string identifiers so that I can> attach metadata to the thing, or simply to pass parsed objects instead> of strings to e.g., xref-backend-definitions. I had to do some> unnecessary roundtrips in a xref backend I wrote for reftex (see [2]).
Right, so my current mental model for a "thing at point (thap) provider" would
be a function that takes two args (BEG and END) and returns a "thap
descriptor" which needs to implement a few methods like
(thap-type THAP)
(thap-bounds THAP)
(thap-name THAP)
where `thap-type` returns a symbol (could default to delegating to
`cl-type-of`?), `thap-bounds` should always return bounds that cover
BEG..END (for many UIs, BEG and END are always equal to point), and
`thap-name` by default would simply return the string delimited by
`thap-bounds`.
Stefan
[புதன் ஜூலை 17, 2024] Stefan Monnier wrote:
>> The focus of do-at-point and Hyperbole are orthogonal AFAIU. Hyperbole>> functions more like xref in that it is more focused on jumping to things>> whereas do-at-point is like context-menu-mode. IOW, do-at-point can>> replace Hyperbole but not the converse. A bridge between these two>> paradigms is what the command embark-dwim aims to do I think.>> Oh, indeed, the aim and UIs of those different packages can be quite> different and I don't necessarily want to unify all that (there are too> many tradeoffs involved).>> I'm rather focusing on some parts of the internals which I hope could> benefit from some unification. AFAICT, taking a kind of naîve view, all> those packages can be a mix of 3 layers:>> - Bottom layer specifies what things are and how to find them.> - Middle layer specifies what actions can be performed for each thing.> - Top layer provides a useful UI for it.>> I suspect that the bottom layers can benefit from being unified.> I don't think unifying the top layers would make much sense in general> (there might be opportunities here and there, of course, but that's not> what I'm after).> As for the middle layer, it's still quite murky in my mind. I hope it> can also benefit from some unification, but unifying the bottom layer> seems to be a prerequisite for that in any case.
The three layers make sense and like you note, thus far there hasn't
been a clear cut definition of them. The middle layer is perhaps the
hardest to define since it leaks into both the bottom and the top layer.
I think do-at-point has the simplest communication between the middle
and the top layer from a user POV. The function arity signals to the
UI/top layer what the actions need. This is much more simple than
embark's 'embark-around-action-hooks', which I think exists mostly to
reuse existing functions/commands as actions but do-at-point can also
reuse a lot of commands/functions just fine so I think there is some
learning to do.
Overall, I envision the middle layer to be a mix of both user-defined
and package-defined functions. Something like context-menu-functions is
what comes to my mind. I think you have captured the essence of other
two layers well.
>> I would say thing-at-point is mostly fine but it would be so much more>> better if (1) non-string things could be returned, and (2) non-string>> returns could be clearly communicated to the caller.>> Yeah, I was thinking of returning "BOUNDS + TYPE".>>> This should make it possible to return things "backed" by tree-sitter,>> org-element, etc.>> "BOUNDS + TYPE" is not ideal for them (since it takes work to recover> the actual tree node from the bounds). We could also imagine returning> something more abstract and then have a method to get the bounds from> the return value.
I may be a bit biased but I think a more abstract object from which the
bounds could be obtained is better. This would make it easier to
use/define "meta-things" (or Heisen-things). Taking the example of
bug-reference-mode refs, I can see two things that could be defined for
it: 'bug-ref' and 'url'. 'bug-ref' thing could have actions defined
that would tag the bug, close it, move it to another package, read it in
Gnus, etc. Of course, one can argue that URL could be side-lined into
'bug-ref' actions but I think that would lead to duplication. You would
need to write actions again that would translate the 'bug-ref' thing to
'url' then feed it to the already existing 'url' actions. I believe it
would lead to less duplication if you can signal to the middle and top
layers that the bug-reference-at-point can be both 'bug-ref' and 'url'.
While 'bug-ref' has a well-defined BOUNDS, 'url' has none. However, I
do recognise the importance of BOUNDS. I was worried about losing some
useful actions that act on BEG..END for these "meta-things." Perhaps,
we could switch to a temp buffer without the action "knowing" and
loosely define a BOUNDS that way?
I can think of a similar situation with the \cite macro where it can
function as a 'cite', 'doi' or 'file' thing. In fact, we might have
more than one of each of those things at point which begs the question:
which one do we prefer?
[ In general, I think "meta-things" will help in sub-categorising a
thing. Sacha's config has a "video-url" target for embark for which
she defines actions that does not work for other 'url' things. See
https://sachachua.com/dotemacs/index.html#embark-video. ]
An abstract object might also reduce re-computation. If I use
tree-sitter or org-element or reftex to define the 'thing' with BOUNDS,
NAME, etc. and later only pass on BOUNDS, it feels like a waste since
users in the middle and the top layer might need access to the full
parsed object to do their job. They might recompute it which might
wasting CPU cycles if there's no cache. I am am also anxious about
losing some buffer-state dependent "properties" of the thing.
> [ Many of the UIs would want to know the bounds, tho (e.g. to> highlight the thing, to compare/order the various things-at-point,> ...). ]
For highlighting specifically, I did not miss it for the "meta-things"
like that I would. Either in do-at-point or embark. I think the second
example would be something like expand-region or expreg?
>> (bounds-of-thing-at-point 'url)>>>> when point is over on a bug-reference reference returns the bounds of>> bug#NNN but this is nonsensical. "bug#NNN" only alludes to a URL, it is>> not the URL itself so this return value makes no sense IMO. See again>> [1].>> Sounds like a bug, indeed, because it's not a URL: the fact we can compute> a URL for it doesn't make it a URL.
Should I file a bug report?
>>> The packages I know of which should hopefully be able to make use of it are:>>>>>> - thing-at-point>>> - expand-region>>> - expreg>>> - do-at-point>>> - embark>>> - ffap>>> - hyperbole>>> - eev>>> [ as well as non-packages: ]>>> - up-list>>> - context-menu-functions>>>> Xref might fit the bill too with xref-backend-identifier-at-point.>> Quite right, thanks!>>> Here too, I specifically wish for non-string identifiers so that I can>> attach metadata to the thing, or simply to pass parsed objects instead>> of strings to e.g., xref-backend-definitions. I had to do some>> unnecessary roundtrips in a xref backend I wrote for reftex (see [2]).>> Right, so my current mental model for a "thing at point (thap) provider" would> be a function that takes two args (BEG and END) and returns a "thap> descriptor" which needs to implement a few methods like>> (thap-type THAP)> (thap-bounds THAP)> (thap-name THAP)>> where `thap-type` returns a symbol (could default to delegating to> `cl-type-of`?), `thap-bounds` should always return bounds that cover> BEG..END (for many UIs, BEG and END are always equal to point), and> `thap-name` by default would simply return the string delimited by> `thap-bounds`.
This sounds good. `thap-name' specifically reminds me of xref-item's
summary which can be anything not just the line where the xref-item
resides in. On the account of above discussion, I would suggest a
(thap-get THAP PROP)
that would return the property PROP of THAP. Something like button-get
which a button could have any arbitrary property the command later using
the button could use. The users in middle and top layer will know what
PROPs are available for a specific THAP.