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

How to map s/S in operator mode with vim-surround #268

Closed mgarort closed 2 years ago

mgarort commented 3 years ago

I love both vim-surround and vim-sneak. However, I would like the mappings for vim-sneak to be more consistent, such that s moves the cursor to the next match in every mode, and S moves the cursor to the previous match in every mode, including visual mode. This clashes with the default mappings for vim-surround in visual mode and in operator mode.

In visual mode I have been able to obtain the behaviour I want by putting the following in ~/.vim/after/plugin/vim-sneak.vim (where I retain vim-surround functionality through z):

xmap <S-s> <Plug>Sneak_S
xmap z <Plug>VSurround

However, I haven't been able to succeed in operator mode, where I've tried a similar approach:

omap s <Plug>Sneak_s
omap S <Plug>Sneak_S

I've put this both in my .vimrc and in ~/.vim/after/plugin/vim-sneak.vim, to no avail. How can I map s to <Plug>Sneak_s and S to <Plug>Sneak_S in operator mode?

Thanks a lot in advance! And thanks for the great plugin too. I can't imagine working without sneak :)

ggandor commented 3 years ago

That should indeed work, but it seems that vim-surround hijacks its default keycombos no matter what. It is documented nowhere, but you can set g:surround_no_mappings though, and then set custom mappings for surround commands, that would work fine. (See this issue.)

mgarort commented 3 years ago

Wow, thanks a lot! This completely fixes my problem. Now I can map everything in my vimrc, without requiring the after directory at all.

Maybe g:surround_no_mappings could be documented in the vim-sneak documentation? It seems appropriate to me, since it is clearly mentioned that s and S are not used more because of vim-surround. @justinmk what are your thoughts?

Personally, I think it is a shame that the vim-sneak mappings are not more consistent by default (although I understand the reasoning behind it, given that vim-surround is so commonplace). I think that if the vim-sneak mappings were more consistent, it would be even more popular, since the two-character motion makes so much sense that it should be integrated into vim by default. vim-surround is also awesome but in my opinion you can get the same functionality with roughly the same number of keystrokes by using only the visual mode version of vim-surround:

  1. Select visually.
  2. Press S.
  3. Press whatever character you want to surround with, for example ).

Since this workflow uses a single key (S), it is very easy to remap vim-surround without losing any functionality. I have remapped <Plug>VSurround to z (mnemonics: vim-zurround).

ggandor commented 3 years ago

One disadvantage of Visual mode commands is that they are not repeatable with the dot operator. Whereas in Normal mode, especially with targets.vim available, you can do crazy things like <cursor here>(foo, bar, baz, qux) -> ((foo), (bar), (baz), (qux)) just by typing ysinab.... Besides, Sneak is also useful in Visual mode, so some conflict is inevitable.

However, it is true that the default mappings could be improved. The current mapping scheme is one that cannot possibly be remembered (s for forward and Z for backward in Visual mode?), and kinda beats the purpose of the plugin; moreover, it makes little sense in the first place: it is based on avoiding conflicts with surround.vim, but the default mappings of surround are based on the assumption that s/S is not remapped, which is obviously not the case if one is using Sneak. We could make both plugins' mappings more consistent instead, at least ensuring that only one key is used in one mode.

Since s/S is remapped anyway, in surround.vim, we could make Visual mode consistent with Normal by changing S to s and gS to S:

xmap s <Plug>VSurround
xmap S <Plug>VgSurround

Whereas in Sneak, we could use z/Z in Visual mode without exceptions:

xmap z <Plug>Sneak_s
xmap Z <Plug>Sneak_S

I think this would be a more agreeable default, wouldn't it? s/S in Normal mode, z/Z in every other situation.

P.S.: Sneak is indeed the most ingenious motion plugin created so far (personally I'd love to see it integrated into (Neo)Vim too, but that's probably not gonna happen), so I also seize the occasion and give kudos of epic proportions to Justin here.

mgarort commented 3 years ago

You are right about visual mode. I guess I've been missing out on vim-surround's ys operator and repeating with .

In that case, yes, the mappings you are suggesting seem the most coherent to me in order to

  1. keep all of vim-surround's functionality, and
  2. have consistent vim-sneak mappings.

I agree that it's better to have z/Z for forward and backward in visual and operator-pending mode than to have s/Z.

LanHikari22 commented 3 years ago

This is literally the first thing I looked up, so I thought a gist with example configuration is appropriate. Taken from mgarort

ggandor commented 3 years ago

@mgarort, @LanHikari22 Recently, I've been reminded that in Visual mode z is occupied by fold commands (I rarely use those as Visual commands, that is why I forgot about it), so it is a better arrangement to map Sneak to s/S in Visual, with vim-surround using gs/gS. Now I get the reason for the inconsistent s/Z default mappings for Sneak, but still, it's much better to remap surround S instead (surround's mappings will also be more consistent and easier to remember then).

molleweide commented 2 years ago

Hi guys.

@mgarort, may I ask you if the omap F vF mapping actually works for you because when I do such mappings for myself, then vim freezes. I have been looking at you .vimrc for inspiration but I get really confused since it freezes vim. However, it seems to work for me with nore.

mgarort commented 2 years ago

Hi @molleweide

Sorry that it took so long for me to get back to you. Work is pretty busy and I've neglected GitHub notifications a bit...

I think the problem is that omap F vF is recursive, since F will be remapped to vF, and then the F in that vF will be remapped to another vF, and then the F in that vF will be remapped to yet another vF, and so on and so forth until the end of time.

That is why using the nore variant (which doesn't remap anything on the left hand side, in this case anything in vF) works. You should use the nore version in this case.

My vimrc does something slightly different because it doesn't map to F, it maps to a whole different command, <Plug>Sneak_F. This is different from just F, so there is no ambiguity and I can do

omap F v<Plug>Sneak_F

I hope that helps.

mgarort commented 2 years ago

An addition to the above: note the other mappings where I don't use <Plug>. In these, I need to use the nore versions or else I get the same infinite recursion problem as you:

onoremap b vb
onoremap B vB

(lines 1090 and 1091)

molleweide commented 2 years ago

Hey @mgarort , thanks for replying!

Yeah I figured out that I needed to add the nore. Then it works! However, one thing that irritates me a bit is that the default surround mappings don't get removed when I remap surround. For example, if I press ds or cs vim still thinks that I want to do a surround command and not a sneak command. Is this an issue that you have been encountering as well? I am on the lookout for a way to remove the default mappings from surround so that I can access all sneak mappings.

mgarort commented 2 years ago

Have you tried this gist?

You need to disable vim-surround mappings with let g:surround_no_mappings= 1, map all the surround commands to another key combination of your choice, and then map the vim-sneak command to s and S.

molleweide commented 2 years ago

Ooohh I missed that one. I am going to test that later today.

molleweide commented 2 years ago

wow it worked!! thank you very very much! this has been bothering me for a while now lol.

mgarort commented 2 years ago

No worries. Glad it worked :)