abo-abo / swiper

Ivy - a generic completion frontend for Emacs, Swiper - isearch with an overview, and more. Oh, man!
https://oremacs.com/swiper/
2.3k stars 338 forks source link

Feature idea: Command swapping #606

Open justbur opened 8 years ago

justbur commented 8 years ago

This is possibly crazy/stupid, but here are two situations that I'm thinking of

  1. ivy-switch-buffer -> search for file buffer -> "damn the file's not open" -> C-g -> ivy-find-file -> Done
  2. swiper -> search for symbol in project -> "damn wrong file" -> C-g -> counsel-ag (or whatever) -> Done

I think it would be cool if you could somehow broaden the context (or narrow it) without leaving ivy. Helm sort of has this, because it has multiple sources and you can jump between sources, but I was thinking that instead of implementing that, which I think has drawbacks, you could implement a simple way of swapping commands while preserving some state (like the minibuffer input). So maybe there's a meta command for files that has keys like, C-1 for buffers C-2 for local files C-3 for remote files. I don't know.

Another thought, but probably more complicated would be if you could dynamically add candidates from more places. So search for buffer, buffer is not there, dump file names in there too dynamically.

abo-abo commented 8 years ago

It's a nice idea. I've had the same in mind, e.g. ivy-widen going from counsel-M-x to counsel-describe-function to a union of describe-function and describe-var. And in the other direction with ivy-narrow.

It only remains to implement it in a sane and extensible way. If only to find the time:). I've already spent today's allotment on a couple of bug fixes and the new swiper-all (which is totally awesome now, btw).

justbur commented 8 years ago

Help functions are a good example, all the way up to apropos

You could have a tree, where walking down the tree is more specific and walking up is more general. Then ivy-widen would preserve input and execute the prior command in the list. Then people could define their own trees like

(setq counsel-command-tree
      '(nil
        (apropos
         (describe-function counsel-M-x)
         describe-variable)
        (counsel-find-file ivy-switch-buffer)))
habamax commented 8 years ago

@abo-abo Just have tried swiper-all --- and it doens't work at the moment.

Error in post-command-hook (ivy--exhibit): (wrong-type-argument characterp ("s" . t))
abo-abo commented 8 years ago

@habamax Please open a new issue, with more details.

habamax commented 8 years ago

well, if there was exposed in documentation swiper-all function, I would definetely opened new issue.

But I have just found out from this thread that threre is awesome function implemented, tried it, it didn't work -- I let you know.

If you want to fix it -- okay, if not -- okay too.

There are no more details, the error is the same as in ivy-switch-buffer. But the workaround for buffers doesn't work for swiper-all.

manuel-uberti commented 8 years ago

Just passing by to say this would be an awesome add to Ivy.

erreina commented 8 years ago

I implemented some sort of swapping similar to the example that @justbur mentioned by doing:

(define-key counsel-find-file-map (kbd "C-b")
    (lambda ()
      (interactive)
      (ivy-quit-and-run (ivy-switch-buffer)))))

(define-key ivy-switch-buffer-map (kbd "C-f")
    (lambda ()
      (interactive)
      (ivy-quit-and-run (counsel-find-file))))
noctuid commented 6 years ago

I like switching to locate. I've just been using this for a while (requires lexical binding):

(defun my-ivy-switch-to-locate ()
  "Switch to using locate, preserving current input."
  (interactive)
  (let ((input (ivy--input)))
    (ivy-quit-and-run (counsel-locate input))))
abo-abo commented 6 years ago

@noctuid Can you make a PR adding ivy-set-actions using your function?

basil-conto commented 6 years ago

Isn't the idea of hot-swapping commands the same as Ivy actions? The only difference is that actions act on the current candidate, whereas command hot-swapping would act on the current input.

Unless I'm mistaken, this would allow piggy-backing the action machinery. Perhaps a prefix argument before M-o could be interpreted as a hot-swap dispatch instead of action dispatch. This would also preserve valuable key binding real-estate.

This implementation idea is slightly less elegant than the widen/narrow idiom (mostly because of more keybindings, though), and the current action machinery might need a mighty face-lift before piggy-backing becomes as easy as I'm suggesting, but the 3D action machinery already exists and has the potential to be much more flexible and customisable than 2D widen/narrow.

Edit: When I say 2D/3D I actually mean linear/exponential, the idea being that widen/narrow can only traverse commands along a single axis, whereas actions can connect any one command with any other command, potentially forming a complete graph in the graph theory sense.

basil-conto commented 6 years ago

@noctuid

requires lexical binding

Just to be clear, your function does not in and of itself require lexical-binding. It is probably the ivy/counsel functions you are calling which depend on lexical-binding.

noctuid commented 6 years ago

Just to be clear, your function does not in and of itself require lexical-binding. It is probably the ivy/counsel functions you are calling which depend on lexical-binding.

I don't quite understand your meaning, but that function itself does require lexical binding. I only mentioned this because the function will not work if normally evaluated (e.g. in the scratch buffer). The issue is not that ivy-quit-and-run is used; the issue is that there is a let around it. The body for ivy-quit-and-run ends up in a lambda, so lexical binding is required for input to be bound correctly.

basil-conto commented 6 years ago

Warning: The following is 100% off-topic and may contain traces of pedantry.

@noctuid

I don't quite understand your meaning [...]

What I am saying is that none of the Elisp language features (defun, let, etc.) used in my-ivy-switch-to-locate depend on lexical-binding. Whether the local variable input is bound dynamically or lexically would normally make no difference.

[...] but that function itself does require lexical binding. I only mentioned this because the function will not work if normally evaluated [...]

The reason the type of scoping of input does make a difference here is the fact that the macro ivy-quit-and-run depends on lexical-binding. The macro could well have been written to support both types of scoping (that run-at-time doesn't look dubious at all!), and then your function could be correctly evaluated even in Emacs 23 (which lacks lexical scoping) without changing a single word in it. This is what I mean when I say your function does not, in and of itself, depend on lexical features; rather it is calling macros/functions which are written in lexical Elisp (which is a different programming language altogether, despite appearances).

[...] (e.g. in the scratch buffer).

As a side note, you can enable lexical-binding in the *scratch* buffer like any other buffer. I always do this in my configuration, and there was recently some discussion on emacs-devel about enabling this by default in a future release.

The issue is not that ivy-quit-and-run is used; the issue is that there is a let around it.

The issue is not that there is a let around it, or that ivy-quit-and-run is used, but rather that ivy-quit-and-run depends on lexical scoping by placing the given body in a lambda. This is a leaky implementation detail which could be avoided by ivy if there was reason to.

The body for ivy-quit-and-run ends up in a lambda so lexical binding is required for input to be bound correctly.

Indeed, but that is a requirement/limitation of ivy, not your function, as I describe above.


P.S. Sorry for bringing this up; there really is little anyone could gain, even in the case of consensus on the definition of separation of concerns which I present above.

abo-abo commented 6 years ago

Isn't the idea of hot-swapping commands the same as Ivy actions? The only difference is that actions act on the current candidate, whereas command hot-swapping would act on the current input.

This is worthwhile to investigate as well. But I think the value of the narrow/widen pattern is the options are tightly coupled to the command at hand, grouping related information together.

noctuid commented 6 years ago

The issue is not that there is a let around it, or that ivy-quit-and-run is used, but rather that ivy-quit-and-run depends on lexical scoping by placing the given body in a lambda.

Indeed, but that is a requirement/limitation of ivy, not your function, as I describe above.

The issue is exactly that there is a let around the lambda. Lexical scoping is required for this to be a closure. It is as simple as that.

Lexical scoping would not be necessary if there were no bindings around the lambda. ivy-quit-and-run can work fine in cases without lexical scoping. This is not an issue of ivy-quit-and-run inherently requiring lexical scoping. The only reason my function doesn't work without lexical scoping is because it uses let to make bindings around a lambda.

basil-conto commented 6 years ago

@noctuid

The issue is exactly that there is a let around the lambda. Lexical scoping is required for this to be a closure. It is as simple as that. Lexical scoping would not be necessary if there were no bindings around the lambda. ivy-quit-and-run can work fine in cases without lexical scoping. This is not an issue of ivy-quit-and-run inherently requiring lexical scoping. The only reason my function doesn't work without lexical scoping is because it uses let to make bindings around a lambda.

Yes, this is what I said in my last reply. My (pointless) point is that, the fact that ivy-quit-and-run puts its arguments into a lambda, thus requiring lexical scope, is an implementation detail of ivy-quit-and-run. For example, a patch for ivy.el could theoretically land tomorrow which removed the lambda (this is actually something I want to look into, because that run-at-time looks like a bit of a kludge). In this case, you wouldn't have had to change a single word and yet my-ivy-switch-to-locate would suddenly work irrespective of the scoping used to evaluate it.

I understand if you don't agree with this way of looking at things from a separation of concerns standpoint; but my (pointless) point still stands from a technical standpoint. Anyway, sorry again for inciting this off-topic discussion.

basil-conto commented 6 years ago

@abo-abo

Isn't the idea of hot-swapping commands the same as Ivy actions? The only difference is that actions act on the current candidate, whereas command hot-swapping would act on the current input.

This is worthwhile to investigate as well. But I think the value of the narrow/widen pattern is the options are tightly coupled to the command at hand, grouping related information together.

Indeed, this is why I think it's slightly more elegant. But my guess is that the number of such groups of related functionality might turn out to be a bit limited. Perhaps the ideal implementation could combine the best of both worlds.

abo-abo commented 6 years ago

because that run-at-time looks like a bit of a kludge

While I agree, the Emacs command loop and recursive minibuffer quitting is a mystery/maze to me. I'm just happy ivy-quit-and-run works at all, and with a small amount to code to boot. Improvements welcome, of course.

See also this related post: https://oremacs.com/2015/07/16/callback-quit/.

novoid commented 6 years ago

Here is a reddit discussion about a related feature request: the poster suggests interactive scope change via a simple query language like any search engine provides:

Problem: There are too many helm- or counsel/ivy- functions.

Firstly, I don't want to manage, create, and remember many bindings. Secondly, its an effort on a brain(I need to do X, oh I need to call func counsel-X, what's the binding? Ah heck I'll just execute-extended-command it).

Ideally, I want one function and one simple button that does the following: By default, show me open buffers and files in default-directory. If I type src/ switch to find-file like functionality. If I type b:feature/make-it-work switch to a branch. If I type m: start a Maildir search with counsel-mu4e. Maybe if I type code: it does counsel-ag.

So essentially a simple query-like language to search stuff inside emacs that I get to define.

I do find this idea very interesting and maybe it contributes something to this issue here.

noctuid commented 6 years ago

Maybe something like this could be useful, but those examples seem to be worse than the current method for doing things. Running the hypothetical counsel-X followed b: or m: requires just as much memorization as would be required for a keybinding but requires extra keypresses. Typing code: doesn't seem much better than just using counsel-M-x (and maybe aliases, but counsel-ag is a lot more descriptive than just code and easily matched). I don't really feel like a middleground between execute-extended-command/counsel-M-x and keybindings is needed. Maybe a counsel-counsel for only ivy/counsel commands.

novoid commented 6 years ago

@noctuid I can follow your arguments. In the end, it's a trade-off between "using a separate keybinding for each command" which takes a few to many keybindings I have to memorize and "using one keybinding plus a mnemonic query language".

In my opinion, there are good arguments for both. The one should not replace the other. With this query language, commands could be solved via tab-completion and mnemonic enhanced commands are easier to remember IMO.

In my world, I tend to use keybindings for things I use on a daily basis and stick to M-x foo for the other things.

And "search" is quite often enhanced via those query keywords separated by colons. This is a common pattern which would be a dramatic improvement for people who are not using all those counsel-foo commands with separate keybindings.

YMMV of course.

Hugo-Heagren commented 3 years ago

I really like this idea, and I've got this.

I think this is quite a good solution to the basics. I wanted the implementation to satisfy all the ideas in the original issue thread, so:


I don't think its ready to make a PR yet. The last thing to implement is narrow/widen commands. I've held off from this yet though because I'm not sure I personally would use it, so I don't know how others would want it to work.

Mostly I'm wondering about whether widening/narrowing should be linear. A linear pathway seems to be @abo-abo 's original sentiment and would certainly be easier to implement. But I'm not sure how many commands really fit into linear hierarchies.

The other thing is less often used commands. If we stick to only commonly used commands (with keybindings) and logically wider/narrower commands, then structurally similar commands that people might want to switch to are left out of the feature. For example, I might be using counsel-describe-variable to look for something that is actually a face. Realising, I decide to switch to counsel-describe-face. This doesn't have a keybinding because it's so rarely used, but it does have something in common with counsel-describe-variable (they're both describe-* commands), so I feel it should be possible to switch to it. On the other hand, the more common switches should still be as quick and easy as possible.

One solution to this is to implement switching to any command the user chooses from a list, like in counsel-M-x. But by the time you've run the swith command, searched and selected, I think you've probably switched context enough that it isn't worth it.

A better solution this might be a default/alternate pattern, like ivy actions. I'm not sure how the data could be stored or accessed, but for every command there could be:

There could then be three keybindings in the ivy minibuffer, for:

This would work, but there would be a lot going on, so maybe just what I've got for now might be a start?