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

Thoughts on the let g:sneak#streak = 1 feature #88

Closed
lydell opened this issue Mar 17, 2014 · 57 comments
Closed

Thoughts on the let g:sneak#streak = 1 feature #88

lydell opened this issue Mar 17, 2014 · 57 comments
Labels

Comments

@lydell
Copy link

lydell commented Mar 17, 2014

I’ve been using vim-sneak for a while, and I really like it! Most of the time it takes me exactly where I want to go really fast.

Every now and then, though, I choose two particularly bad characters to sneak for, and the screen gets all magenta. Suddenly my target might be like 10 ; away (but I cannot be sure that it’s exactly 10, so usually I find myself spamming ; instead of doing 10;). In those cases I either try to find a less common pair of characters close to my original target to sneak for those, or I use some other motion to get a bit closer and then sneak again.

A few weeks ago, I decided to give the let g:sneak#streak = 1 feature a try. My first impression was “Wow, this is brilliant!” when one of those “screen got all magenta” scenarios happened. Suddenly my target was just a single keystroke (the label character) away. It felt like it really sped me up—without changing the sneak experience I’ve gotten used to at all (except that the highlighted matches now are covered with some letter and a space, but that doesn’t matter since I know what I sneaked for).

However, after some time I realized that this option actually slowed me down. Many times a sneak gets many results, but I actually want to go to the first result. Then I usually want to edit the text at that point, for example by typing a or cw. But 'a' and 'c' are likely a label to jump to another match! It’s really annoying to press a to append some text after the cursor, but instead be taken to some other place! That caused my to press Esc after most sneaks, increasing the number of keystrokes!

I’ve thought about changing the labels, so they won’t likely collide with other commands, but I can’t come up with such a set.

So what first seemed to be a really convenient option did not work out very well in the end. Now I have disabled the option and bound SneakStreak to its own key.

However, I still turn the screen all magenta every now and then. Now, I usually re-perform my sneak using my SneakStreak key followed by enter instead. That works pretty good ... but it feels like we could do better, just like the let g:sneak#streak = 1 option tries to.

What about the following? If there are more than two matches, give labels to every match. But don’t go to the match if the label is typed: Make every key work just as normal instead. If I do want to follow a label, I have to press my SneakStreak key first. This is how I’m thinking:

I sneak for something. Then one of three things usually happen:

  • I get to my target immediately. Great—let’s start editing, without fear that I will be unexpectedly taken to some other place!
  • I get to one match too early from my target. No worries, I just hit ; once and I’m there!
  • The characters I sneaked for happened to be really common, so there are lots of matches and my target is far away. Then I could just look at the label of my target, press the special key and then the label to get there. This is better that re-performing the sneak using SneakStreak, because: (1) one less keystroke (I don’t have to press enter) and (2) because the label is already there.
@justinmk
Copy link
Owner

Good thoughts! I think you are on the right track. I've been trying to solve this, but didn't consider that workflow. I think it's a good compromise.

@liujoey
Copy link

liujoey commented Mar 19, 2014

i come up with this set of labels which i feel very much fit my needs.
let g:sneak#target_labels = "sfjkgtunbqz/SFKGHLTUNBRMQZ?"
The listed labels are those you most likely will not use them after a streak search, and they are far more then enough to label all matches in one screen.

@lydell
Copy link
Author

lydell commented Mar 20, 2014

A really transparent way of solving my workflow could be not to use sneak streak at all, but number each highlighted match (like sneak streak puts labels on each). Then I could simply hit like 8; (or 26; in worse cases) to get to the match I want really quickly.

@justinmk
Copy link
Owner

@liujoey Those are good defaults (g might be problematic), thanks.

@lydell That's a good idea, but display of number labels >9 is tricky if there are adjacent matches: 2627282930. EasyMotion has an elaborate multi-colored solution for this (some good discussion on the EM bug tracker). Maybe adjacent numbers are easier to scan though.

But, I'm definitely going to experiment with the idea of "trigger key" to invoke streak-mode immediately after a sneak. In fact, this is probably already possible after this commit.

@justinmk
Copy link
Owner

@lydell Just tested, it works (be sure to update, I fixed a small bug with <Plug>(SneakStreak)). You can test a basic implementation of your first idea like this:

nmap <silent> <expr> <space> sneak#is_sneaking() ? '<Plug>(SneakStreak)<cr>' : ':'

Instead of <space> use whatever you want your "trigger key" to be, and the part after the : in the expression should be whatever you normally use that key for.

@lydell
Copy link
Author

lydell commented Mar 20, 2014

Cool! :)

There is one problem, though: If I used a backwards sneak, backwards streak mode should be invoked.

(And one thing is missing: The labels do not show up until I press the trigger key. But you did say that it is a basic implementation.)

About adjacent numbers and hints:

  • Is that something that really happens? Would be nice to see some real code and a relevant sneak that triggers it.
  • Adjacent numbers are definitely less of a problem than adjacent hints.
  • Sneak streak already has this problem; Introducing the numbering feature wouldn’t add a new problem.

@justinmk
Copy link
Owner

@lydell Yeah, it's not perfect. It also doesn't work with repeat-operation. But I'm going to try it for a few days to see how it feels. I will add first-class support ASAP.

Is that something that really happens?

Not often. Here's a lot more discussion: easymotion/vim-easymotion#47

Sneak streak already has this problem; Introducing the numbering feature wouldn’t add a new problem.

With s there's always a blank space between adjacent matches.

Numbers could solve some problems. One new problem though: since the the cursor will go to the first match, the numbers wouldn't help with operations like d or c (you'd have to jump back, then do the operation with the number).

@liujoey
Copy link

liujoey commented Mar 20, 2014

(g might be problematic)

well, i'm not a heavy g user, the only command I use starts with g is gg, and I don't think I will use gg right after streak search. Some people might use gu or gU, so Yes I agree, if you use this setting as the defaults, then it's better take off g from it.

@justinmk
Copy link
Owner

@lydell FYI, in order for the test mapping above to work for 2-char sneak, clever-s needs to be disabled:

let g:sneak#s_next = 0

@lydell
Copy link
Author

lydell commented Mar 20, 2014

No problem, I already had it disabled ;)

@paldepind
Copy link

I think the initial solution proposed by lydell in this thread is a great idea! In its current incarnation I think the streak mode is kinda broken. I came here after just checking trying it briefly because I knew I would face the same issues as lydell.

The biggest problem in my opinion is that the predictability of sneak is ruined. You never know if pressing an editor command will take you to another match or do as you expect.

I don't think the number solution is as good. After the first nine matches is ends up taking an extra keypress.

I'm excited to see what is going to happen with this issue :) Till a solution is found I'll just enjoy vim-sneak as better alternative to vim-seek.

@mon10a
Copy link

mon10a commented Apr 1, 2014

Just to give a different perspective . . .

I write prose every day and love and use streak mode exclusively. More than 90% of the time I don't want the first match. I would prefer NOT to have to press a special key before jumping, although I understand the arguments being made. I hope that remains an option.

I am thinking about implementing liujoey's idea (without the 'g' which I use frequently). Good idea.

m

@justinmk
Copy link
Owner

justinmk commented Apr 1, 2014

@mon10a It will definitely remain an option, possibly remain default.

@mon10a
Copy link

mon10a commented Apr 1, 2014

I just updated and see that highlighting now remains ON after jumping to a
match in streak mode. Is there a way to go back to the previous behavior
which turned the highlighting off immediately after you jump (but turns it
back on if you press ';' or ',')?

thanks -- this plugin (and Unite) really make Vim a pleasure to use.

m

On Tue, Apr 1, 2014 at 6:34 PM, justinmk [email protected] wrote:

@mon10a https://github.com/mon10a It will definitely remain an option,
possibly remain default.

Reply to this email directly or view it on GitHubhttps://github.com//issues/88#issuecomment-39269350
.

@justinmk
Copy link
Owner

justinmk commented Apr 1, 2014

@mon10a Would you mind creating an issue? I think that behavior is a side-effect of trying to gracefully handle ctrl-c.

@mon10a
Copy link

mon10a commented Apr 1, 2014

sure, thanks

m

On Tue, Apr 1, 2014 at 7:03 PM, justinmk [email protected] wrote:

@mon10a https://github.com/mon10a Would you mind creating an issue? I
think that behavior is a side-effect of trying to gracefully handle ctrl-c
.

Reply to this email directly or view it on GitHubhttps://github.com//issues/88#issuecomment-39271494
.

@mon10a
Copy link

mon10a commented Apr 2, 2014

Hi,

I opened an issue last night (#98, I think), but it has not shown up in the
list. Do I need to do that again or are curating?

Regards.

m

On Tue, Apr 1, 2014 at 7:03 PM, justinmk [email protected] wrote:

@mon10a https://github.com/mon10a Would you mind creating an issue? I
think that behavior is a side-effect of trying to gracefully handle ctrl-c
.

Reply to this email directly or view it on GitHubhttps://github.com//issues/88#issuecomment-39271494
.

@justinmk
Copy link
Owner

justinmk commented Apr 2, 2014

@mon10a I got it, should be fixed soon. Github does some weird caching on the list, I bet if you refresh it will appear.

justinmk added a commit that referenced this issue Apr 9, 2014
@justinmk
Copy link
Owner

justinmk commented Apr 9, 2014

@lydell If you pull the activate_or_fallthrough branch you can try this out. The "activation" key to activate the target labels is whatever you have mapped to <Plug>SneakStreak and/or <Plug>Sneak_s. Currently there's no UI feedback to let you know that this key is required, except for the prompt text s{target} (which obviously makes no sense if you mapped sneak to something other than s).

I have been using this for more than a week. Contrary to my expectations, I found it to be more trouble than it saved. But since you never have used streak-mode, maybe you won't find it as jarring. Can you try it out?

I think there are other ways to address the original issue:

  • use @liujoey 's restricted g:sneak#target_labels (or even smaller subset) for the initial labeling to prevent 99.999% of false-positives. If the user hits <tab>, then show 52+ extended labels.
  • highlight streak-mode in a different color (other than magenta)
  • better "recovery" to get back to the intended location if a false positive occurs

@lydell
Copy link
Author

lydell commented Apr 17, 2014

I finally got time to try it out today.

Some thoughts after using it for a few minutes:

  • I seem to like <Plug>Sneak_s as the activation key.
  • I like that you can press Esc followed by <Plug>Sneak_s to make another sneak directly after a another (I actually do that sometimes).
  • It seems to work the way I imagined.

But I’ll have to try it out for some time to know for sure.

But since you never have used streak-mode, maybe you won't find it as jarring.

What do you mean? As I mentioned in my first post “I decided to give the let g:sneak#streak = 1 feature a try.” I had it on for a couple of weeks. Then I opened this issue with my experiences.

@justinmk
Copy link
Owner

@lydell I didn't word that very well, but I just meant you might have less permanent habits. I usually am able to adapt, but somehow this feature was very awkward to me.

@lydell
Copy link
Author

lydell commented May 13, 2014

I’ve tried this for a while now. Some things I’ve found:

  • I seem to have improved my sneaking skills: I usually get where I want without using labels.
  • I don’t like the “random flashes”. Many times I get to my target directly, but the pair of letters I sneaked for happens to be common, so labels show up, which also remove the syntax highlighting (until the next keypress). The thing here is that I cannot anticipate if labels are going to be shown or not, making me perceive this as “random blinking”.
  • Almost like the above, the labels sometimes obscure the text I want to edit (making me hit Esc).

The feature feels awkward to me, too, now. I’ve come to the conclusion that g:sneak#streak = 1 is nothing for me. I’m currently trying the following out:

nmap <silent> <expr> <tab> sneak#is_sneaking() ? '<Plug>(SneakStreak)<cr>' : '<Plug>Sneak_s'

I just wish that pressing tab when sneak#is_sneaking() didn’t move the cursor. Is that possible?

@justinmk
Copy link
Owner

@lydell Thanks for trying it and sharing your thoughts. That mapping is very cool!

I just wish that pressing tab when sneak#is_sneaking() didn’t move the cursor. Is that possible?

I could make it an option, but would like to know the use case(s). Only reason I can think of: the next match is off-screen, and you want to label only the currently on-screen matches.

@lydell
Copy link
Author

lydell commented May 13, 2014

I understand why <Plug>(SneakStreak) moves to the next match automatically. It’s good feature—when used “an intended”. So it should definitely be optional. My use case is this:

  1. I sneak for something
  2. “Oh crap, that’s a lot of matches, and my target is far away!”
  3. I press my sneak key again to get labels for all matches.
  • Now my target is definitely not the next match—if it would have been I would have pressed ;.
  • My mapping doesn’t know in what direction I sneaked. If I sneak backwards and then press tab I’d expect the cursor to move backwards when entering streak mode, but my mapping only goes forward. And in that case I could be taken off-screen (downwards), possibly moving my target out of view! The easiest way to fix those is to not move the cursor when I press tab (then we don’t have to determine which direction the current sneak is).
  • The cursor has no reason to move in my case. I just want labels. Moving then is just confusing.

@justinmk
Copy link
Owner

Good points. There are several ideas now that I am enthusiastic about. Hope to work on this in the next week or two.

@notEvil
Copy link

notEvil commented May 28, 2014

Showcase is over, now there is a separate function which does a little more than just entering streak every time. All dirty hacks are reverted, so you can now check out my fork without loosing the sneak functionality.

@justinmk: in visual mode it does some weird stuff. I couldn't figure out why. Maybe you know better. You will find a lot of familiar code in the function. It would be great if you would look at it some time.

@justinmk
Copy link
Owner

@notEvil Just to check, you are aware of <Plug>SneakStreak and how it differs from <Plug>Sneak_s?

@notEvil
Copy link

notEvil commented May 29, 2014

I had a thorough look at the code when I wrote the function, so yes, I'm aware of (SneakStreak).

sneak.vim, line 182: let target = (2 == a:streak || (a:streak && g:sneak#opt.streak)) && !max(bounds) && s.hasmatches(2)

=> after sneak is finished it enters streak mode only if there are at least 2 matches left, no matter whether a:streak is 2 (forced streak) or just 1. Maybe you mixed up the brackets.

In any case sneak's and therefore also streak's search direction is fixed, and streak is an addon for sneak which, by the users perception, is entered unpredictably. So my function does something very different to (SneakStreak)

Edit: found and fixed mistake. +update. now it works perfectly and has extra features

@lydell
Copy link
Author

lydell commented Jun 15, 2014

I’ve used this mapping for a while now:

nmap <silent> <expr> <tab> sneak#is_sneaking() ? '<Plug>(SneakStreak)<cr>' : '<Plug>Sneak_s'
  • I don’t mind that that <Plug>(SneakStreak) moves the cursor to the next match automatically anymore.
  • The only thing that bothers me is that if I’m currently sneaking, I want to be able to press <tab> to get labels for each match, regardless of which direction I’m sneaking. Currently, that mapping can only switch into forwards streak mode. Something like this is what I need:
nmap <silent> <expr> <tab> sneak#is_sneaking() ? (forwards ? '<Plug>(SneakStreak)<cr>' : '<Plug>(SneakStreakBackward)<cr>') : '<Plug>Sneak_s'

Should sneak#is_sneaking() tell the current direction? Should there be another method for getting the direction? Should I keep track of the current direction myself using a variable in my .vimrc? Should sneak provide an option like “clever_s” that includes my desired behavior by default?

Either way, I think the g:sneak#streak=1 feature is good the way it is—it’s just nothing for me.

@lydell
Copy link
Author

lydell commented Jun 15, 2014

I don’t mind that that (SneakStreak) moves the cursor to the next match automatically anymore.

Seems like I actually do, after all, but for a different reason. <tab>[letter][letter]<tab>[label]<c-o> should take me back to where I was when pressing <tab> (invoking <Plug>sneak_s) the first time, but actually takes me to where I was after hitting <tab> (invoking <Plug>(SneakStreak)) the second time.

@notEvil
Copy link

notEvil commented Jun 17, 2014

first of all, interesting way to use sneak/streak :)
what you would need is a function that doesn't remember the starting position if specified. And because streak is just on top of/after sneak, you would need to pass this information as argument rather than as global option. Imo streak should get it's own function and then it makes sense And is no big deal to add an option to prevent jumplist entries.
I could, for instance, easily add this to my fork. But it's a different streak and I suppose not everyones taste

@justinmk
Copy link
Owner

Should sneak#is_sneaking() tell the current direction? Should there be another method for getting the direction?

@lydell I could expose the state (s:st in plugin/sneak.vim) as a "locked" global.

@justinmk
Copy link
Owner

<tab>[letter][letter]<tab>[label]<c-o> should take me back to where I was when pressing <tab> (invoking <Plug>sneak_s) the first time, but actually takes me to where I was after hitting <tab> (invoking <Plug>(SneakStreak)) the second time.

@lydell How about if s<C-j> does the same thing as s<Enter> except it doesn't add to the jumplist. Then you could do this:

nmap <silent> <expr> <tab> sneak#is_sneaking() ? '<Plug>(SneakStreak)<C-j>' : '<Plug>Sneak_s'

@lydell
Copy link
Author

lydell commented Jun 20, 2014

That would work. But if you need to add something anyways, why not just add the possibility to enter streak mode without moving? Doesn’t really matter to me in the end, though.

@justinmk
Copy link
Owner

why not just add the possibility to enter streak mode without moving?

I'm trying to avoid creating a ton of specialized <Plug> mappings or adding more parameters to sneak#to() or sneak#wrap(). Unless you have something else in mind.

@lydell
Copy link
Author

lydell commented Jun 20, 2014

It could be an option like let g:sneak#foo = 1

@justinmk
Copy link
Owner

Oh, you want to turn it off completely? Else, how would we know when to save the jump and when not to?

@lydell
Copy link
Author

lydell commented Jun 20, 2014

I want to use vanilla sneak, which means I can use ; and , to explore of trail of matches, and then use c-o to return to the initial location. If there are lots of matches, though, using ; and , isn’t effective. That’s why the let g:sneak#streak = 1 feature exists. However, though it seems to work well for many, it doesn’t for me. Instead, during a sneak I want to be able to get labels for all the current matches by pressing my sneak key (I do not use “clever_s”). Regardless of if I got to a match using the initial sneak movement, one or more ; or ,, or through typing a label, I want to get back to the initial location by pressing c-o.

How this is implemented doesn’t matter to me—built into sneak as an option (in case there are more people like me for whom the let g:sneak#streak = 1 feature doesn’t really work out), or by using custom mappings utilizing sneak’s API. You decide—you always have sensible opinions.

@lydell
Copy link
Author

lydell commented Jul 1, 2014

I think that if a sneak (any kind of sneak) is invoked while already sneaking, then that sneak shouldn’t add to the jump list. I think that makes sense for all users.

@justinmk
Copy link
Owner

justinmk commented Jul 1, 2014

I like it. Easy to implement too. Gimme a minute.

justinmk added a commit that referenced this issue Jul 1, 2014
@justinmk
Copy link
Owner

justinmk commented Jul 1, 2014

@lydell Done. Thanks.

@lydell
Copy link
Author

lydell commented Jul 1, 2014

Nice! Then the only missing piece is exposing the current sneak direction.

@lydell
Copy link
Author

lydell commented Jul 6, 2014

The conclusion here is that the let g:sneak#streak = 1 feature is good the way it is. For people who don’t like sneak is now flexible enough to allow custom replacements of the feature. (When #119 is fixed.)

Thanks for all the time and work in this issue! :)

@lydell lydell closed this as completed Jul 6, 2014
@justinmk
Copy link
Owner

justinmk commented Jul 6, 2014

@lydell I'm actually 99% convinced of several tweaks for g:sneak#streak = 1, such as:

  • always invoke the target selection for edit operations (d, c, y, ...)
  • use the minimal non-false-positive target set for the initial labels, and showing extended set (a-z, 0-9) on <tab>
    • this won't interfere with your mappings, it just changes the current <tab> behavior that already exists

@justinmk justinmk reopened this Jul 6, 2014
@lydell
Copy link
Author

lydell commented Jul 7, 2014

always invoke the target selection for edit operations (d, c, y, ...)

Sounds awesome!

use the minimal non-false-positive target set for the initial labels, and showing extended set (a-z, 0-9) on

Sounds like a good idea to me (although I won't use it). Just make sure that the minimal set is only uses when streak is automatically invoked, not manually.

@justinmk
Copy link
Owner

AFAICT everything here worth implementing, was implemented.

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

6 participants