Skip to content
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

Enhance f to flash numerical labels for later ; jumps #160

Closed
joeytwiddle opened this issue Jun 8, 2014 · 43 comments
Closed

Enhance f to flash numerical labels for later ; jumps #160

joeytwiddle opened this issue Jun 8, 2014 · 43 comments
Labels

Comments

@joeytwiddle
Copy link

I would like to suggest an alternative to request #133 .

Add a multi-line search on <Plug>(easymotion-flash-f) that will immediately go to the first match, but will also _flash_ EasyMotion labels for all the other matches on the screen. (They could disappear on CursorHold event or after 150ms.) If the user realises they didn't want the first match, they will at least know what keys to press to get to the match they want. That should be very fast!

In this case, it might feel more Vim-like to use numerical labels. I am currently using fanfingtastic (multi-line f,t,F,T) so for me a good flow would be:

  • fX jumps straight to the next 'X' character, but flashes red numbers over all the other 'X's on screen
  • Then 5; would jump to the 5th next match, the one that EasyMotion labelled with a 5.

But you may have your own idea about the keys. I would accept anything with a small number of keystrokes!

I would be quite happy if matches in the opposite direction were also labelled. (To feel like ftFT, I would bind a key so these could be reached with 5,)

The reason I want this is:

  • EasyMotion requires extra keystrokes (by default at least).
  • It is a little slow on this machine, so I have to wait for the labels to appear!
  • And before I perform jump I don't even know if I will need it. Making that decision requires extra thinking, which I don't have time for!
  • For those reasons, I rarely use EasyMotion. I usually just do a fast immediate jump with f or F.
  • But then sometimes I realise I needed to jump further. EasyMotion could help me with recommendations of what keys to hit next!

This is a slight re-thinking of EasyMotion, as a hinter during normal Vim usage, rather than something we specifically request. (But I would only use this for Normal and Visual modes, and keep the other EasyMotion features for operator-pending mode.)

Arguably this feature belongs in one of the f-enhancement plugins, but I am looking at EasyMotion because it already has the complicated code for labelling!

On a related note, here is a little experiment I am trying in flashing/suggesting, as an alternative for (easymotion-j) and (easymotion-k)

" Find line easily from Home / Middle / Last keys
nmap <silent> H M:set relativenumber<CR>
nmap <silent> M M:set relativenumber<CR>
nmap <silent> L L:set relativenumber<CR>
autocmd CursorHold * set norelativenumber

Is there any bidirectional alternative to (easymotion-j/k)? It seems I have to decide whether I am going up or down, so I have to think about my cursor position. It might be nicer if EasyMotion could just label all the lines on screen (starting with those nearby, or near the middle).

@joeytwiddle
Copy link
Author

Actually waiting for CursorHold before doing label cleanup would be dangerous. The user might start editing the buffer while it is in the labelled state!

But there is an alternative I used in sexyscroller.vim. You can block Vim with many small sleeps in a loop, but break out for early cleanup if you detect the user has pressed a key, by checking getchar(1).

@haya14busa
Copy link
Member

Is there any bidirectional alternative to (easymotion-j/k)?

Have you tried <Plug>(easymotion-bd-jk) ? It's bidirectional JK motions.

@haya14busa
Copy link
Member

Actually waiting for CursorHold before doing label cleanup would be dangerous. The user might start editing the buffer while it is in the labelled state!

Yep, if I should implement jupm-to-first-match motions, maybe the feedkeys() features which justinmk/vim-sneak have already implemented is worth considering instead of using CursorHold or something.

Hm, but there are a lot of things we should consider before implement this feature, like how should we deal with bi-directional motions? Jump to next match?
IMO, it isn't intuitive to jump to first match and show the labels at the same time, at least this is very confusing.

Have you tried let g:EasyMotions_enter_jump_first = 1 or let g:EasyMotion_space_jump_first = 1?
If you know the target position is the first match before invoking EasyMotion, you can smoothly type f{char}<CR> or f{char}<Space> to move the cursor to the first match.

Also, if you set g:EasyMotion_keys like this let g:EasyMotion_keys = ';HKLYUIONM,WERTXCVBASDGJF', put ; char to the first keys, you can also move to the first match with f{char};.

I know there are a lot of cases you want to jump to first match immediately instead of waiting the EasyMotion labels, but for me, it's easy, comfortable, and intuitive enough to deal these cases with g:EasyMotion_space_jump_first and g:EasyMotions_enter_jump_first, so I have no motivations to implement jump-to-first-match motions right now....

@joeytwiddle
Copy link
Author

Thanks so much for your feedback and all your suggestions. There really are a lot of ways to use EasyMotion these days!

I thought a bit more about what feature I really want, and reworded it: I want the same behaviour that the default f,F,t, T, ; and , keys perform, but with EasyMotion assisting me by labelling possible future ; and , motions.

I got excited and went and made a proof-of-concept! (I'm sure you know the feeling.)

https://github.com/joeytwiddle/vim-easymotion - Helpful hinting for f,F,t, and T (search "hinting")

It works quite ok for the next 1 to 9 matches, although right now , and ; work the opposite way around after F or T.

I understand this is using EasyMotion in quite a different way from normal, but it is:

  • An additional feature, there is no change to any existing behaviour.
  • Not too intrusive into the code. (Although a few more changes are needed to finish it off! ;) )

I'd love to know what you think of it so far, and if I should be working in different ways.

I hope to finish off some of the TODOs in future...

  • I used ..., a:0 and a:1 in the #S() and #T() functions, but they may not be needed, assuming those functions are only ever called via the keybinds in find_motion_map_helper(). Is that the case?
  • Some minor refactoring may be needed to switch to numerical _keys for hinting/flashing mode.
  • Major refactoring would be required to jump before flashing, but actually I can live without that.
  • Could this be useful for other keys/motions/behaviours? If so I could keep them in mind...

@joeytwiddle joeytwiddle changed the title Jump to first match, flash labels for other matches f jumps to first match, flashes numerical labels for other matches Jun 10, 2014
@joeytwiddle joeytwiddle changed the title f jumps to first match, flashes numerical labels for other matches Enhance f to flashes numerical labels for later jumps (on ;) Jun 10, 2014
@joeytwiddle joeytwiddle changed the title Enhance f to flashes numerical labels for later jumps (on ;) Enhance f to flash numerical labels for later ; jumps Jun 10, 2014
@haya14busa
Copy link
Member

Wow!!! It's amazing! It broaden my mind.

I understood what you are trying to do. Your proof-of-concepts looks so promissing! 🍣

I'll look into it more deeply to improve it :)

@joeytwiddle
Copy link
Author

Thanks! ❤️

And may I say, EasyMotion is very awesome these days. So many features, and so well behaved! 🍧

@haya14busa
Copy link
Member

https://github.com/Lokaltog/vim-easymotion/tree/feature-flash-motions

I'll work on this branch for this flash-motions.
It works with 1-99 numbers & support bi-direction.

Although the code definitely should be improved more, but it works better i think :)

let g:EasyMotion_move_highlight = 0
map f <Plug>(easymotion-flash-bd-f2)

@haya14busa
Copy link
Member

consistent highlight

highlight! link EasyMotionTarget2First EasyMotionTarget
highlight! link EasyMotionTarget2Second EasyMotionTarget

@haya14busa
Copy link
Member

Options: imo, it should be better to set like this if you use flash-motions

let g:EasyMotion_do_shade = 0 

@joeytwiddle
Copy link
Author

Wow so fast. That's really great, thanks! Yes it works better. I am beginning to understand the code a little... ;)

My only concern is that it doesn't jump-to-first-match, which traditionally f and t would do. I wouldn't want to enable jump-to-first-match for all motions, but I would for these f/t enhancements. I would personally make it the default behaviour for f and t motions, perhaps the only behaviour, but you might not be so keen on that! I'm not sure if an option, or an extra set of bindings, is a better way to provide both behaviours. I think you are already planning this anyway, with let char = '0'. To implement it, I just changed let i = 1 to let i = 0. When I do that, commands like ct. act as expected. (Although there is a bug that Vim doesn't believe me when I undo them. It claims the file has been modified!)

I agree about the shading. I don't know if people might want a mixed shading mode (off for flashes but on for traditional EasyMotions). Possibly users will be happy with one mode all the time.

(I found a bug in my original version, but you appear to have fixed it already. It seems somehow I broke normal operation. In my version (easymotion-bd-f), which has no flashing and should have been unchanged, was failing to keep the prompt open when two keystrokes were needed to jump to a faraway character. Hopefully that bug is gone for good in your branch.)

I thought of some more keys where flashing might be useful: / ? n and N. I guess it's fairly easy for for n and N (already supported by EasyMotion) but it might be harder for / and ? because we only want to flash after the user has hit <Enter>. I feel these keys should all jump-to-first-match also, so they keep their traditional Vim behaviour.

Thanks a lot for the branch. I hope I can contribute some more in future...

Some other advanced uses for hinting could be ... 😆 ... g; and g, (previous edit positions) <C-o> and <C-i> (previous cursor locations) and maybe even u (previous undo locations)! Extracting the information from :changes and :jumps might be possible via :redir but still not easy!

@joeytwiddle
Copy link
Author

cf. is working nicely as expected today! But ct. is not jumping to the first match.

As well as moving in the correct (last set) direction, I would like ; to flash the numbers again. We could make that an option, or default behaviour, for (easymotion-next-in-dir).

@joeytwiddle
Copy link
Author

I have pushed to my repository a fix for jump-to-first-when-flashing. (I did in #T() the same as you had done in #S()). And I added two new mappings (easymotion-next-in-dir) and (easymotion-prev-in-dir) which can be used to make ; and , behave more like default Vim behaviour (at least until a different easymotion is performed!).

I wonder if we should try to keep these mappings for ; and , tied to the previous f and t searches, or let them adopt the most recent easymotion search char like they do now.

@joeytwiddle
Copy link
Author

Later in my branch I updated the README, and added count-flash mappings for n and N.

Then in order for 3n to work, and display future choices correctly, I had to shift the numbers on the target labels. (Try 5n with the -flash-n mapping to see what I mean!)

It might be desirable to have a call similar to -flash-n and -N which flashes but does not move; this could be invoked after using * or #.

I am unsure of your plans for flash-bd-W and friends. Should they jump-to-first-match (in which case we should store s:current.v_count1) or are they supposed to stay still, and only flash a preview of where we could move to if we had used -W?

@haya14busa
Copy link
Member

I thought of some more keys where flashing might be useful: / ?

I added <Plug>(easymotion-flash-sn), this is / like motions with flash.
As for ? motions (with bi-direction incremental search), the default easymotion
have not implement it yet... ref: #108

@haya14busa
Copy link
Member

Some other advanced uses for hinting could be ... 😆 ... g; and g, (previous edit positions) <C-o> and <C-i> (previous cursor locations) and maybe even u (previous undo locations)! Extracting the information from :changes and :jumps might be possible via :redir but still not easy!

I fix to work with <C-o>/<C-i> for flash motions now.
The default flash motions shouldn't add jump-list because it always jump to first match and it's consistent with default f.
next/previous motions with counts now add a jump-list, and it's consistent with default n & N.

As for g;/g,, I think I fixed already with another issue #114 and it works well in my environment.
Would you mind opening another issue if the changelist still dosen't work for you.

@haya14busa
Copy link
Member

I am unsure of your plans for flash-bd-W and friends. Should they jump-to-first-match (in which case we should store s:current.v_count1) or are they supposed to stay still, and only flash a preview of where we could move to if we had used -W?

I fixed it. I think all flash motions should be count sensitive. 848b34b

@haya14busa
Copy link
Member

Now, the dot repeat for flash motions works :)

@haya14busa
Copy link
Member

I'm considering the flash motions feature should be provided as a separate plugin, not as a easymotion's new feature.

Because, the flash motions' feature is almost completely different from original ones and the code will be more and more messed up with the s:flag.flash or something...(You can say the easymotion's code has been already messed up though... I want to perform radical refactoring).

The flash motions feature is so awesome and I'm also so excited with developing this feature, but I don't want to merge it into easymotion's master branch, at least right now even though it works well.

However, it's true that we needs the function which overwrite buffer text with the labels provided with easymotion and several functions.

If you don't mind, I want to make another plugin like vim-flashmotion or whatever.

I'd want to know what you think, @joeytwiddle?

@haya14busa
Copy link
Member

I improved the code for the feature of jump-to-first match.

If you are interested in, please see cb8e2d9

I'm sorry to delete your comment because now I used completely different method to implement jump-to-first match, but your detail comments really helps me a lot to understand what you are trying to do. Thanks ❤️

@haya14busa
Copy link
Member

I'm sorry, 😭 I push -f to the branch. If you pulled while this 15 minitues, please reset --hard or something. If you didn't, it supposed to be no problems. 👌

@haya14busa
Copy link
Member

(partial commit is very useful but I messed up the orders of the commits... I should have be more careful...)

@joeytwiddle
Copy link
Author

Hi hajaybusa, nice work! I see you also fixed some bugs too. 👍 I didn't notice your push -f and I hope you didn't notice mine yesterday! 👀 (Perhaps we should learn to do partial commits on a wip branch, then cleanup/rebase later on a more stable branch.)

I am very happy you have been changing my code to fit your plugin's style, and deleting my comments when they were no longer needed. The flash behaviour is the same as before, and better, so I cannot be sad!! 😃

About splitting into a separate plugin, that is your choice to make of course! Disadvantage: Many users may not try the new flashing feature if it is not in the core. Advantage: Code will be smaller and cleaner, for users who never wanted flashing anyway. (And I am interested how you would design it!)

Either way, refactoring will be required, but that is also good; it will be useful for anyone else who wants to add features in the future.

About <C-o> <C-i> g; g, and u. I was not talking about fixing bugs with those keys, which you have fixed already. I was suggesting new features: we could make mappings for these keys, so when they are used, they will flash where repeat presses will go in future! So the user could do one u, see the markers flash, then do 5u to go all the way back to where they wanted. (I know ... this is a crazy idea! Don't worry about it; I may look at it in future. I think the data is availabe by parsing :changes and :jumps via :redir. We would also create non-flashing prompting modes for the same features, so the user could do \\u and \\<C-o>)

Things that might be worth considering during future refactoring:

  • Jump-to-first-match should happen before the flashes are displayed. This would feel more responsive to the user.
  • It feels a little inconsistent that f = flash-f does not apply move_highlight but ; = next-in-dir does.
  • Perhaps an option to show numeric flashing when using -next, -prev, -next-in-dir and -prev-in-dir, so the user will see numbers flash before the EasyMotion_move_highlight appears.
  • Performance: Can we make showing and hiding markers more efficient? The move_highlight appears very fast, but on a big window flashing/prompting is quite slow. (It might be because Vim is having to re-parse for syntax highlighting - I don't know; need to do some profiling.)
  • You could consider providing an easy way for users/developers to plug in new EasyMotion behaviours, such as the <C-o> feature above. For example, a developer could register a new targetting function simply by running this: :call EasyMotion#AddNewMotion('(easymotion-jump-back)', { 'flash': 0,jump-to-first: 1, 'keys': 0 }, "g:MyFuncToGenerateTargetsFromJumpList") Ideally then (easymotion-jump-back-flash) could be created with a similar call and the same Target generating function! (I guess you already did some plugin API for the op-line and op-phrase plugins, but I haven't read all of that yet!)

Anyway, what you have done so far is really great! 🍰 The branch is currently quite usable for me, so I will enjoy it. 😁 Thanks a lot, and please don't get assassinated by Logitech! 🐹

@haya14busa
Copy link
Member

Jump-to-first-match should happen before the flashes are displayed. This would feel more responsive to the user.

I think I implemented like you said. Isn't it the behavior you think? https://github.com/Lokaltog/vim-easymotion/blob/cb8e2d9c9ab3c923da8a385257e98348619768b4/autoload/EasyMotion.vim#L1205-L1210

@haya14busa
Copy link
Member

It feels a little inconsistent that f = flash-f does not apply move_highlight but ; = next-in-dir does.

let g:EasyMotion_landing_highlight = 1

I suppose this config should fix this inconsistentency.

@haya14busa
Copy link
Member

Vim is having to re-parse for syntax highlighting

Yeah, maybe it's true... overwriting buffer text invoke the re-parse for syntax highlighting. I can use the conceal feature to avoid it, but now another problems come up, so it's difficult...

I think it's fast with 2-key find motions like <Plug>(easymotion-flash-bd-f2), but it's different from default f and there's no accounting for taste.

@haya14busa
Copy link
Member

You could consider providing an easy way for users/developers to plug in new EasyMotion behaviours

I totally agree with you. Actually, there are already a function (undocumented) for the users to customize the motions, but it's not good enough to open it as an API. https://github.com/Lokaltog/vim-easymotion/blob/master/autoload/EasyMotion.vim#L258

I'll improve it in the future.

@haya14busa
Copy link
Member

Anyway, what you have done so far is really great!

The idea of flash motions excited me! Thanks for the great suggestion! 👍

@haya14busa
Copy link
Member

Gif

recorded

@haya14busa
Copy link
Member

@joeytwiddle
Copy link
Author

I think I implemented like you said. Isn't it the behavior you think?

My apologies, you are right, the cursor is at the target while the flashes are displaying. I was just confused by the initial delay in setting up the flashes on a slower machine. I have put a redraw after the call to repetitive_jump which helps give feedback to the user a bit sooner (although it does break the display up into 2 stages).

I tested switching :syn off and on to see how much syntax highlighting is slowing stuff down. I estimate it's at least 50% of the bottleneck! But I should test with other plugins disabled too. (Have you considered syn off and syn on as an alternative way to implement do_shade? More disruptive but maybe a little faster.) I did also wonder if conceal could help in future...

Anyway things are fast enough for real-world usage. It's only slow when I test with a lot of matches (f<Space> or (easymotion-flash-w)) but I rarely actually use EasyMotion that way.

let g:EasyMotion_landing_highlight = 1

Great! I wonder if that should default to the same value as move_highlight.

" XXX: It's a workaround to show **same** labels for
"      the different 2 targets.

That is a nice trick, it fooled me hehe! 😉 I did wonder about the black areas ... but it didn't really bother me.

@joeytwiddle
Copy link
Author

(Sorry I should have started a separate issue for this!)

To match Vim's usual n and N keys, I want (easymotion-n) and (easymotion-N) to respect the last search direction: they should search in reverse if the last search was started with ? or #.

I have achieved this using Vim's v:searchfoward variable. If you agree with this behaviour, you may want to cherry-pick this one commit into your master branch, since it is not specific to the flashing feature. (Although easymotion's non-flashing n is quite different from Vim's default n so maybe users don't expect it to act the same, and won't appreciate a change in behaviour.)

joeytwiddle@fccd317 on my motion-n-respects-last-search-direction branch.

(Note that the value of v:searchforward is the reverse of our direction vars: 1=forwards 0=back.)

I could have put this on new -n-in-dir and -N-in-dir bindings, but it seemed to me to be always desirable behaviour. I could be wrong..?

I was worried that v:searchforward would not be reset to 1 after (easymotion-sn) but amazingly it is! So everything seems well behaved. It may be worth checking that it is reset correctly for other EasyMotions too.

If you later implement a reverse -sn motion (e.g. to bind to ?) then we will need to ensure that v:searchfoward is set to 0. And similarly for any * and # equivalents in EasyMotion's future.

@haya14busa
Copy link
Member

joeytwiddle/vim-easymotion@fccd317 on my motion-n-respects-last-search-direction branch.

Thanks! I'll look into it. I didn't know v:searchforward.

I also want to keep the current n & N motions which consistently move forward or backward.
So, maybe I'll include it with the config or other mappings

@haya14busa
Copy link
Member

Hmm, syntax off and syntax on or enable are also slow in some time, but maybe I should reconsider it.
If the users set g:EasyMotion_do_shade to 1 and the syntax on & off is faster than I thought, I'll implement it.

@haya14busa
Copy link
Member

That is a nice trick, it fooled me hehe! 😉 I did wonder about the black areas ... but it didn't really bother me.

If I separate this feature into another plugin, it's possible without these dirty hack.

@haya14busa
Copy link
Member

About plugin name

I'm looking for a better plugin name than vim-flashmotion, if you come up with any good name, please tell me :) 🍦

@joeytwiddle
Copy link
Author

I actually like your proposed name. vim-flashmotion works nicely as a tribute to its heritage from vim-easymotion!

But just to brainstorm, you could consider some permutations of the following words: vim-motionhints vim-flashtastic vim-jumpcounter vim-suggestjumps vim-count-to-next vim-easymotion-flashes (as an add-on)

For me, I am very happy using what we have come up with so far. I have not once thought "I should disable this annoying plugin" which is pretty unusual when trying a new Vim plugin! 😆

So creating a whole new plugin might be overkill. But perhaps you have lots of extra plans for flashing which would not fit well in the existing codebase.

I think I might delay implementing flashes for g; and <C-o> until 2015/16. For now I will keep it as something to look forward to...

@haya14busa
Copy link
Member

I actually like your proposed name. vim-flashmotion works nicely as a tribute to its heritage from vim-easymotion!

Oh, really? 😄

Hm, I don't think it's bud name and I like it to some extent, but flash motions helps count input literally unlike easymotion, so if there good name which imply (or contain) count like word or whatever, the name could be better.

But maybe I'll work on this with this name, flash-motions. Thanks for the suggestions 🍣

@haya14busa
Copy link
Member

For me, I am very happy using what we have come up with so far.

Thanks :)
I also think this feature has a great potentials.

IMHO, there maybe are more people who likes this feature if you look into this sneak's issues justinmk/vim-sneak#88

Some of these suggestions mentioned in that issues is just the same as the flasmotions feature.

@lydell
Copy link

lydell commented Jun 28, 2014

Thanks for referencing this issue! I’ll definitely try that flash-motions plugin out!

@joeytwiddle
Copy link
Author

Hi @lydell. I have not tried vim-sneak yet.

If you want to try flash-motions now, it is on this branch (or my version). Just add the key mappings from the README section "Add helpful hinting".

@joeytwiddle
Copy link
Author

joeytwiddle commented Nov 26, 2014

Here is another plugin that offers a similar feature. https://github.com/boucherm/ShowMotion "Tiny vim plugin to highlight landing places when moving with w,W,b,B" (and f,F,t,T) Edit: Oh it doesn't show numbers, just a block flash.

And this one, just for f,F,t,T. It highlights the targets at all times. https://github.com/bradford-smith94/quick-scope

@haya14busa
Copy link
Member

Thanks for the info :)

@haya14busa
Copy link
Member

This issue becomes a bit outdated and I don't have any plan to work on this at least for now. sorry, i'll close it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants