hlissner / evil-snipe

2-char searching ala vim-sneak & vim-seek, for evil-mode
MIT License
340 stars 25 forks source link

Add Option to Use Jump Characters #25

Closed noctuid closed 8 years ago

noctuid commented 9 years ago

I think it would be awesome if evil-snipe had jump characters like sneak's streak mode (or like avy, easymotion, etc.). This is the one thing I'm really missing from sneak. For example, if there are more than 2 matches, the start of each match is highlighted with a home row key (or from user defined keys), and pressing that key moves the point directly to the match. If there are more matches then defined keys, each match is highlighted with multiple letters/keys.

hlissner commented 9 years ago

Is there a reason you can't use avy together with snipe? This is exactly what avy offers (it even has a 2-char search). I use snipe for short range jumps and avy for long range ones.

noctuid commented 9 years ago

I don't see any downside in combining the functionality of the two. With vim, I use sneak for all visible range jumps; there is no need to have two keys bound. Avy's 2-char search is lacking features of evil-snipe. For example, it is not interactive and isn't integrated with evil (motions and the ability to cycle with same key) .

hlissner commented 9 years ago

Fair point, I agree. I'll look into it.

PythonNut commented 9 years ago

@angelic-sedition you might want to look into evil-easymotion, specifically integrating with evil-snipe. :-)

hlissner commented 9 years ago

@PythonNut that looks interesting! I'll give it a try myself.

noctuid commented 9 years ago

@PythonNut I wasn't familiar with your plugin; thank you for mentioning it. I was able to briefly test this, and I think it's pretty much what I want. I did notice that doing it this way, it won't automatically jump to the first match like sneak does (though I think I might prefer this behaviour if it isn't the case when there is only one match). Also, it's very slow when using after d or c (you can see the highlights appearing from bottom to top for maybe a second). Unlike avy, if multiple keypresses are needed, they don't show up all at the beginning; is it possible to change this behaviour?

Unfortunately, avy keeps giving me errors that it fails to autoload suddenly. Anyway, I think having evil-easymotion able to integrate with various motions is a great idea.

PythonNut commented 9 years ago

@noctuid would you be so kind as to post each of your problems in a separate issue on my issue tracker? :)

I'll admit, I don't know why composing the motions with operators is slow. I'm not entirely sure what you mean by the multiple keypresses.

sooheon commented 8 years ago

Does @noctuid or the maintainer mind reopening this? I think there is still something to be explored here.

Take ivy-avy for example: You do a regular search with swiper and when you have n candidates, you can call ivy-avy to add avy targetable letter overlays to each candidate. To have functionality like this, I think we need a function snipe-avy which will take a list of all currently highlighted snipe targets in buffer, and add avy overlays to them. Then we can simply bind this to a key in snipe transient map (maybe C-; to build on ;). This is different from easymotion because you don't need to take on the overhead of typing the target letters every time--it's there if while sniping you decide you need them. There is also no upfront choice about "should I snipe or easymotion?" when you start your movement. It should always be snipe, and if after sniping you find there are too many candidates, avy can step in and take you to a specific one.

hlissner commented 8 years ago

Sure. Having integrated easymotion into my workflow, I've found myself thinking the same. When I fix #35, I'll make it easier for users to bind keys to the transient maps. Then, you'll be able to do something like this to integrate easymotion into evil-snipe:

(define-key evil-snipe-parent-transient-map "\C-;" 
  (evilem-define "gs" 'evil-snipe-repeat
      :pre-hook (save-excursion (call-interactively #'evil-snipe-s))
      :bind ((evil-snipe-scope 'buffer)
             (evil-snipe-enable-highlight)
             (evil-snipe-enable-incremental-highlight))))

A little messy, but it works.

Alternatively, directly integrating avy is an option, but I don't know anything about avy under the hood. I'll look into it.

PythonNut commented 8 years ago

@sooheon although the transient map is out of my control, do note that the :pre-hook is optional.

You should be able to do something like this to repeat without first prompting for characters to search:

(evilem-define "gs" 'evil-snipe-repeat
               :bind ((evil-snipe-scope 'buffer)
                      (evil-snipe-enable-highlight)
                      (evil-snipe-enable-incremental-highlight)))

@hlissner also, if you want to bind an easymotion to a map without also binding it in normal/motion state, you can use evilem-create:

(define-key evil-snipe-parent-transient-map "\C-;" 
  (evilem-create 'evil-snipe-repeat
                 :pre-hook (save-excursion (call-interactively #'evil-snipe-s))
                 :bind ((evil-snipe-scope 'buffer)
                        (evil-snipe-enable-highlight)
                        (evil-snipe-enable-incremental-highlight))))
hlissner commented 8 years ago

@PythonNut perfect! Thanks! I removed the pre-hook so it doesn't prompt on repeats:

(define-key evil-snipe-parent-transient-map (kbd "C-;")
  (evilem-create 'evil-snipe-repeat
                 :bind ((evil-snipe-scope 'buffer)
                        (evil-snipe-enable-highlight)
                        (evil-snipe-enable-incremental-highlight))))

@sooheon evil-snipe-parent-transient-map was just pushed in https://github.com/hlissner/evil-snipe/commit/ddba4fc7f83d52d2715c776c746259d595b30bfb — this should work now.

sooheon commented 8 years ago

I've tried out your snippet, and it looks like it has 90% of the functionality. The only thing is that there's no reason for the avy targets to only be applied to the initial direction of the search. I think the limitation is that evilem takes a motion, and the evil motions just happen to be directional. Would there be value in having a special function defined just so that all candidates in a buffer could be targets?

PythonNut commented 8 years ago

@sooheon you can also pass multiple functions to evilem-create and co.:

(define-key evil-snipe-parent-transient-map (kbd "C-;")
  (evilem-create (list 'evil-snipe-repeat
                       'evil-snipe-repeat-reverse)
                 :bind ((evil-snipe-scope 'buffer)
                        (evil-snipe-enable-highlight)
                        (evil-snipe-enable-incremental-highlight))))
sooheon commented 8 years ago

Ah I should have rtfm :) Thanks, this looks great. And if you let evil-snipe take over f/t bindings, this gives the easy motion functionality to those as well for free.

hlissner commented 8 years ago

Great! Thanks for the help @PythonNut

That wraps this up.