justinmk / vim-sneak

The missing motion for Vim :athletic_shoe:
http://www.vim.org/scripts/script.php?script_id=4809
MIT License
3.24k stars 88 forks source link

extended labels ("smart label mode") #304

Closed ggandor closed 1 year ago

ggandor commented 1 year ago

I'm raising this because this would be a major QoL-improving feature that could be adapted from leap.nvim at minimal cost (non-breaking change, negligible API surface and documentation, simple implementation).

The idea is to add another label list, that can contain any character (an "unsafe" list). If target_labels cannot cover all matches, we automatically switch to that longer one, but in that case do not jump with the cursor (i.e., you have to select a label, just like in operator-pending mode). This provides the best of both worlds between Sneak's and EasyMotion's method. Users can fine-tune the behavior merely by adjusting the two lists - if you empty out either one, you can force "always jump" or "never jump". (See https://github.com/ggandor/leap.nvim/tree/8facf2eb6a378fd7691dce8c8a7b2726823e2408#smart-autojump, https://github.com/ggandor/leap.nvim/blob/8facf2eb6a378fd7691dce8c8a7b2726823e2408/doc/leap.txt#L174-L203.)

Benefit: at least twice as many label characters can be used, if necessary. This means you almost never have to reach for tab, even if the window area is considerably big.

Trade-off: introducing a certain degree of non-determinism. That is, you might have to use a label even if the targeted match is right in front of you, you cannot blindly count on the automatic jump anymore. Still, if kept as an opt-in feature, I see no harm in it - it works very well in practice, the gains much outweigh the rare and minor annoyance. If there are more than 20 matches, chances are pretty low that you're aiming for the very first one, since you could probably reach that with trivial native motions - f, ww and the like. (Remember that the search pattern is not a whole word, just a pair you pick, so it is supposed to be distributed somewhat evenly across the area.)

justinmk commented 1 year ago

Thanks for the request, but this is intentionally out of scope since day 1. https://github.com/justinmk/vim-sneak/issues?q=label%3Awontfix+is%3Aclosed

EasyMotion and other jump plugins try to do everything, and it becomes feature creep:

ggandor commented 1 year ago

Thanks for the response! First of all: I can totally relate to your sentiments about feature creep, minimalism and consistency, this high conceptual integrity is what I love about Sneak. But in this case the counter-arguments are not terribly convincing IMO, so I just leave my 2 cents here.

I think label mode already breaks with the original idea of Sneak being the "missing motion" halfway between f and /. / helps nothing if there are lots of identical words. This is the very point of using labels: you don't want to guess how "complex" a case is, ideally you just want to jump to a target you see, without scanning the window content first. With a labeling plugin available, you don't have to abuse / anymore to move around the screen, you can use it for it's intended purpose, that is, finding matches.

And I'd say this smart mode is the typical example of a high-leverage feature. There's nothing complicated about it. Only a few lines of code should be added, and exposing a g: var, which is optional to use. Instead of flags, the state of the two lists implicitly affects the behavior. Nothing should change about the default experience. On the other hand, one could reach 2-3x more targets with ease if they opt for it. It wouldn't just provide a choice between "Sneak or EasyMotion", but anything between the two.

I heavily agree with the importance of being as predictable as possible, but no search-based motion can be 100% predictable in the first place, since they presuppose contextual knowledge. There is always the possibility of an in-between match you might miss. This mode doesn't make matters noticeably worse, because as I said, it's rare that you want to target the first one among 20+ matches. And the gains are high, it will become easy to reach dozens of additional targets in exchange. Small optimizations like this are not unimportant, because you do these jumps hundreds of times a day. Switching with tab is awkward, especially in Sneak - you need to pause twice (or more) to process the visual feedback.

justinmk commented 1 year ago

There's nothing complicated about it. Only a few lines of code should be added, and exposing a g: var, which is optional to use. Instead of flags, the state of the two lists implicitly affects the behavior. Nothing should change about the default experience. On the other hand, one could reach 2-3x more targets with ease if they opt for it.

If that's true then I'm less strongly opposed, though it does increase the explanations needed in documentation.

But generally I suspect that when there are numerous matches, "efficiency" is a lost cause, so <tab> is fine as an escape-hatch. Eventually you run out of labels, even with "smart" mode.

ggandor commented 1 year ago

If I'm not mistaken, it's basically just adding a line like

label-list = ((#matches > #safe-list + 1) && (#unsafe-list > 0)) ? unsafe-list : safe-list

before assigning the labels, plus adding a similar check to the condition expression when deciding about jumping to the first match. The rest is details. This is why I dared to raise this, the implementation is more or less trivial.

I suspect that when there are numerous matches, "efficiency" is a lost cause

50 instead of 20 is considerable difference, we're talking about a 150% increase. This also adds to the power of tab itself - with only one switch, more than a hundred targets can be covered.