New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature idea: Command swapping #606

Open
justbur opened this Issue Jul 29, 2016 · 21 comments

Comments

Projects
None yet
8 participants
@justbur
Copy link
Contributor

justbur commented Jul 29, 2016

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

This comment has been minimized.

Copy link
Owner

abo-abo commented Jul 29, 2016

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

This comment has been minimized.

Copy link
Contributor Author

justbur commented Jul 29, 2016

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

This comment has been minimized.

Copy link

habamax commented Jul 30, 2016

@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

This comment has been minimized.

Copy link
Owner

abo-abo commented Jul 30, 2016

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

@habamax

This comment has been minimized.

Copy link

habamax commented Jul 30, 2016

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.

abo-abo added a commit to abo-abo/function-args that referenced this issue Jul 31, 2016

Use "C-M-j" and "C-M-k" for tag jumps
Example 1: start with "C-M-k" to find something in the current file.
It doesn't exist, to press "C-M-j" to search for the /same input/ in the
whole directory.

Example 2: start with "C-M-j" and get to many cands. Press "C-M-k" to
narrow the candidates to the file of the current candidate.

* function-args.el (map): Update keymap.
(moo-complete--candidates): New defun.
(moo-complete): Update.
(moo-completion-at-point): New defun.
(moo-jump-keymap): New keymap.
(moo-jump-local): Now it's really local, get tags for the current file,
bound to "C-M-k".
(moo-jump-directory): Jump to tags in the current dir, bound to "C-M-j".
(moo-select-candidate): Update.
(moo-action-jump): Update.

Re abo-abo/swiper#606
@manuel-uberti

This comment has been minimized.

Copy link
Contributor

manuel-uberti commented Aug 13, 2016

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

@erreina

This comment has been minimized.

Copy link

erreina commented Aug 30, 2016

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

This comment has been minimized.

Copy link

noctuid commented Nov 23, 2017

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

This comment has been minimized.

Copy link
Owner

abo-abo commented Nov 24, 2017

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

@basil-conto

This comment has been minimized.

Copy link
Contributor

basil-conto commented Feb 10, 2018

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

This comment has been minimized.

Copy link
Contributor

basil-conto commented Feb 10, 2018

@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

This comment has been minimized.

Copy link

noctuid commented Feb 11, 2018

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

This comment has been minimized.

Copy link
Contributor

basil-conto commented Feb 11, 2018

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

This comment has been minimized.

Copy link
Owner

abo-abo commented Feb 11, 2018

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

This comment has been minimized.

Copy link

noctuid commented Feb 11, 2018

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

This comment has been minimized.

Copy link
Contributor

basil-conto commented Feb 12, 2018

@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

This comment has been minimized.

Copy link
Contributor

basil-conto commented Feb 12, 2018

@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

This comment has been minimized.

Copy link
Owner

abo-abo commented Feb 12, 2018

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

This comment has been minimized.

Copy link

novoid commented Mar 1, 2018

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

This comment has been minimized.

Copy link

noctuid commented Mar 1, 2018

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

This comment has been minimized.

Copy link

novoid commented Mar 2, 2018

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment