Closed noctuid closed 3 years ago
For the sake of other readers, the requested functionality is to take the candidates from the minibuffer and put them into a real buffer, with a keymap that allows you to select one of them. That seems like a reasonable feature because it will not require any particularly bad contortion of the abstractions. Thanks for making me aware of this feature; I had not seen it before.
+1 for this feature. Very useful for finding all of the faces a package provides.
+1 That would be the big missing piece for me as well. It's the only reason I am still using ivy instead of selectrum.
+1 This is why I still use Ivy. My use case is counsel-rg, where I do a search for a term with ripgrep, turn it into an ivy-occur buffer, and then use wgrep to make it an editable buffer and make changes across files.
@b3n out of curiosity, is rg.el a suitable replacement for counsel-rg + ivy-occur?
@raxod502 I just tried out rg.el and it does indeed meet my needs, thank you for pointing me to it! The only downside is I can't get search results in the minibuffer as I type, but I can live with that.
@raxod502 I don't think rg.el shows results as you type, counsel-rg does. rg is so fast that it is nearly instantaneous. It makes it usable for project navigation for instance.
I looked at this and I do not think it is a good idea to try to implement the whole thing like Ivy does it. That is because it breaks the completing-read
abstraction badly. Ivy relies on knowing what action to take with the selected candidate, but Selectrum does not know this because it only implements completing-read
.
That said, I'm not opposed to adding something more moderate to Selectrum which will allow commands like ivy-occur
for specific use cases to be implemented. Here is what I am thinking. We add a new keyword argument to selectrum-read
that allows you to get the list of currently displayed candidates when the user exits the minibuffer (whereas normally the return value is just the selected candidate). Then, to implement a replacement for (as an example) ivy-occur
+ Ibuffer, you simply make a command that uses selectrum-read
to read a buffer, but with the new keyword argument -- and then feed the resulting list to Ibuffer. What do you all think? Would that be workable? I'm open to other ideas.
I think it might be desirable to make this feature controlled by a dynamically bound variable rather than a keyword argument, since that would allow you to use selectrum-read-buffer
, selectrum-read-file-name
, etc. in these ivy-occur
replacements without modification or hacks.
The code is at https://github.com/raxod502/selectrum/pull/56.
The following might be funny given that I also added suggestions a while back to implement features which deter from the default API but I'm no more in favour of adding such features now. This will lead to a state where selectrum more and more can't just be used a a replacement for completing-read, people will implement all kinds of things which use such extra features and all of the sudden you have new selectrum-this, selectrum-that packages.
Not that there would be something wrong with that but I would wish we would have more packages which just use the default API and don't require a specific completion framework. For some things which need features not compatible with the default API it might make sense to use such a framework but for those we already have helm or counsel.
Personally I would prefer to focus on providing a better completing-read interface and afterwards think about how to implement extensions on top of the default API which are not framework dependent.
I looked at this and I do not think it is a good idea to try to implement the whole thing like Ivy does it. That is because it breaks the
completing-read
abstraction badly. Ivy relies on knowing what action to take with the selected candidate, but Selectrum does not know this because it only implementscompleting-read
.That said, I'm not opposed to adding something more moderate to Selectrum which will allow commands like
ivy-occur
for specific use cases to be implemented. Here is what I am thinking. We add a new keyword argument toselectrum-read
that allows you to get the list of currently displayed candidates when the user exits the minibuffer (whereas normally the return value is just the selected candidate). Then, to implement a replacement for (as an example)ivy-occur
+ Ibuffer, you simply make a command that usesselectrum-read
to read a buffer, but with the new keyword argument -- and then feed the resulting list to Ibuffer. What do you all think? Would that be workable? I'm open to other ideas.I think it might be desirable to make this feature controlled by a dynamically bound variable rather than a keyword argument, since that would allow you to use
selectrum-read-buffer
,selectrum-read-file-name
, etc. in theseivy-occur
replacements without modification or hacks.The code is at #56.
That sounds like a decent solution, I am liking the direction of keeping selectrum lightweight as well and from what I understood a "selectrum-occur" can happen separately with this.
Here's an idea: I think a main advantage of ivy-occur
is we can call the command first without thinking, and then choose to select a candidate or call ivy-occur
based on the situation. As far as I can tell, #56 doesn't give us the mechanism to do this, so I would say it's not much useful. To me, pop up the Selectrum UI to only get a list of candidates from it seems weird.
If we seriously want a mechanism to implement similar feature, I think a new action must be added. When calling it, the list of candidates is returned like in #56. The command build on top of selectrum-read
could do different things based on whether it receives a list or not from selectrum-read
. If we do this, we need an argument in selectrum-read
to explicitely enable this "occur" action, so it's blocked by default (and in selectrum-completing-read
).
@AmaiKinono If, as you suggest, selectrum-read
grows an argument to enable the "occur" action, then how is that different from the :return-candidates
argument? In either case, the caller must explicitly declare that they want a list rather than just the selected candidate.
I agree that it is nice to be able to start the session first and then later decide whether to use the occur-like feature, but I am not sure it's possible to implement this without violating the design philosophy of completing-read
rather badly.
how is that different from the
:return-candidates
argument? In either case, the caller must explicitly declare that they want a list rather than just the selected candidate.
With the :return-candidates
argument, the caller declares it want a list. But with an enable-occur-action
argument, the caller declares it can deal with a string (given by the selectrum-select-current-candidate
or selectrum-submit-exact-input
action), or a list (given by the suggested "occur" action).
With multiple selection (https://github.com/raxod502/selectrum/commit/5f59af278b3b7010c56e222c2196b24b1a56675d), there is a more elegant way: the caller invokes completing-read-multiple
, and then we can have a keybinding ("the ivy-occur
keybinding") which selects all the candidates before exiting the minibuffer. Then the caller decides what to do based on whether more than one candidate was returned in the list. What do you think of that? The only disadvantage I see is that you can't do an ivy-occur
action on only one candidate -- but I have a hard time seeing many use cases for that.
the caller invokes completing-read-multiple, and then we can have a keybinding
I think that sounds great! Instead of a key binding we could also accept *
as the input for meaning all candidates. This would mean one can not select a single element with completing-read-multiple
which is just *
easily but this shouldn't be relevant to often.
When *
is returned the caller has to handle it, and decide to continue with all elements. This way we would force commands using this feature to be implemented in a way which work with or without selectrum. Another benefit I see is that the the choice in this case is not "one or all" but "one, multiple or all".
Oh I just noticed I missed the case of returning all matched candidates of current query. I don't have an idea how to implement this in a way that is independent from selectrum. Actually the comment I initially replied to seems to have already provided a solution for that.
Yeah, I think the thing people are mostly excited about is doing an occur operation on the list of currently matched candidates.
Yep, I missed that. Without such a concept in the default API this seems to be hard to achieve in a compliant way.
See comment below
Maybe another way we could achieve that would be by using a wrapper which always rebinds commands in crm-local-completion-map
and crm-local-must-match-map
before calling completing-read-multiple
. This rebound commands could then add all currently matching candidates to the input and submit them. When the commands using completing-read-multiple
are written using this wrapper they would also work without selectrum.
Edit: Actually I was kind of confused, the wrapper is also a dependency so this does not help really. Given that it is probably not possible to do this ins a standard API compliant way I would say it's ok to add a general command that works for any completion session. Forcing people to explicitly use completing-read-multiple isn't nice. Maybe one day this option gets added to default API of completing-read and than we can easily switch to it. BTW what about making selectrum--read
a private function so usage of any selectrum specific features is discouraged outside of selectrum?
I had another idea: We could avoid such an API altogether and provide selectrum
commands like:
(push (cons [remap dired] 'selectrum-dired)
selectrum-minibuffer-bindings)
(defun selectrum-dired ()
(interactive)
(let ((cands ()))
(dolist (c (cl-delete-if-not
#'file-exists-p
(mapcar #'selectrum--get-full
selectrum--refined-candidates)))
(push (if (file-directory-p c)
(directory-file-name c)
c)
cands))
(run-at-time
0 nil
(lambda ()
(dired (cons "*selectrum-dired*" (nreverse cands)))
(goto-char (point-min))))
(abort-recursive-edit)))
This way there is no need for the caller to write a command a certain way, or to explicitly use completing-read-multiple
. We also don't need to do something like ivy
which knows what action to use for the candidates, the user knows better ;). It would just work with any completion commands which contain file candidates. Similar commands could be written to build an ibuffer
buffer and maybe others. There is no swiper equivalent but from isearch you can call occur, too.
Huh. I guess using an idle timer together with abort-recursive-edit
does in fact allow you to implement alternate actions without breaking the completing-read
API. Very clever, I had not thought of that.
If it were done this way, I don't think I would mind alternate action support being added to Selectrum, although I think I would prefer to see alternate actions that are global across all commands (and available under special keybindings) rather than ones that are defined by the caller to selectrum-read
. That way we don't fall into the trap of needing to write a wrapper for every possible Emacs function that uses completing-read
.
The same approach allows not only alternate actions, but also occur-like actions, as you observed. I think this is great!
If it were done this way, I don't think I would mind alternate action support being added to Selectrum, although I think I would prefer to see alternate actions that are global across all commands (and available under special keybindings)
That would be great! Maybe we could use a prefix key which would show a menu for available categories: file-actions, buffer-actions and so on. After selecting one of them with a key another one shows up with the actual actions. For some candidates like files or buffers we could actually auto detect them with file-exists-p
or get-buffer
but one problem is candidate names might exists in multiple contexts.
Edit: For files we could also just check for minibuffer-completing-file-name
and for buffers we could let bind a similar variable in selectrum-read-buffer
so we know what type of candidates we have for these cases.
For some candidates like files or buffers we could actually auto detect them
As long as the auto-detection is customizable -- it needs to be clear that such functionality will necessarily make some decisions arbitrarily which may not always be correct, and therefore can be tweaked if needed by the user.
We can also detect the type of some collections by querying the metadata category. We also would need the metadata for adding support for display-sort-function
and maybe others as discussed in #82.
You can get ivy-occur like functionality using embark, the corresponding command is called embark-export
. In addition to that you get minibuffer actions on candidates (like ivy-actions) and some other features which come with embark (see the project readme/wiki for details). See Selectrums wiki for setup instructions.
Can be closed? Embark is pretty solid now :)
This thread is being closed automatically by Tidier because it is labeled with "waiting on response" and has not seen any activity for 90 days. But don't worry—if you have any information that might advance the discussion, leave a comment and I will be happy to reopen the thread :)
One of my favorite features of ivy is
ivy-occur
. The integration with ibuffer forivy-switch-buffer
, with grep-mode forswiper
, and with dired forcounsel-find-file
and similar commands is especially nice. I wouldn't want to try to do file management in a minibuffer with a bunch of keybindings (like helm), but it's nice to be able to populate a dired or grep buffer with results and then do whatever in it.