svermeulen / vim-subversive

Vim plugin providing operator motions to quickly replace text
MIT License
295 stars 4 forks source link

Option to put substituted (by motion) text into default register ("")? #22

Closed tuurep closed 3 years ago

tuurep commented 3 years ago

Hello, this might just be my favorite and most used plugin for the s-mappings that imitate the behavior of c, cc, C, d, dd and D, but I'm missing just one thing:

Similarly like c and d I would prefer if the default register was replaced by what was erased by s, so that I could quickly place that elsewhere by p or another s.

This would then be useful with dot-repeat (with tpope/vim-repeat) for rearranging parts of text. I'll make an example:

Given the mappings: nmap s <Plug>(SubversiveSubstitute) nmap ss <Plug>(SubversiveSubstituteLine) nmap S <Plug>(SubversiveSubstituteToEndOfLine)

Current behavior:

Text: one, two, three, four, five, six

Command sequence from start of line: ywWswW.W.W.W.0.

Result: one, one, one, one, one, one

If s were to put erased text to default register:

Result: six, one, two, three, four, five

This would then make it work the exact same as c and d by default, at least in my mind.

Maybe this already exists as an option in this plugin, but I wasn't able to get it to work? Or it's easily achievable by a small tweak/mapping? I'm just asking for advice on how to get it to work like this.

Best wishes!

svermeulen commented 3 years ago

I haven't considered this approach. I think you're right that it could be useful in some situations. But I would not want it to be default behaviour, since it would prevent the user from substituting the same text in multiple different places, which I think is common at least for me.

One way to kind of do what you want here is to use visual mode with paste. (eg. copy 'one', go to visual mode, select, two, hit p, repeat). You can also make a map for this with something like:

nmap <silent> sp :set opfunc=SwapRegisterSubstitute<CR>g@
vmap <silent> sp p

function! SwapRegisterSubstitute(type, ...)
  if a:0
    silent exe "normal! `<" . a:type . "`>p"
  elseif a:type == 'line'
    silent exe "normal! '[V']p"
  elseif a:type == 'block'
    silent exe "normal! `[\<C-V>`]p"
  else
    silent exe "normal! `[v`]p"
  endif
endfunction

You might also change the above to take an optional register as well, instead of always using default

tuurep commented 3 years ago

Thanks for replying!

Yes I understand it, I just find I have far more use for the case I described. It doesn't need to be default, but it would be perfect for me if that was an option that I could map / enable, and then for cases where I need to substitute many parts with the same content, I would just immediately yank what I just substituted, like:

siw yiw

^ and this kind of thing would be easy to deal with in simple .vimrc mappings.

I'll try what you suggested (vimscript is hard to read), although I'm a bit wary because I try to avoid visualmode as much as possible.

svermeulen commented 3 years ago

Yeah, try the above maps. They should not require going into visual mode. They should automatically enter visual mode for you. So you should be able to hit ywWspwW.W.W.W.0. and get the text you want

tuurep commented 3 years ago

Aha! Thank you, I'll give that a good try.

tuurep commented 3 years ago

Ok I've tried that out, and it's close to what I want.

I'd still want it to be more holistic though such that:

  1. I could omit pressing the p after s
  2. It would work not only with s{motion}, but also ss and S

All-in-all, I'm glad you've recognized my problem and saw that it makes sense, depending on personal preference. I'd be curious to know if others agree or disagree with me.

Would it mess with the plugin too much if there was a switch something like:

let g:erased_text_fills_register=1 (0 by default) ?

I have no clue what that would look like inside the plugin.

Edit:

Or, alternatively something like:

nmap s <Plug>(SubversiveSubstitute_and_fill_register) nmap ss <Plug>(SubversiveSubstituteLine_and_fill_register) nmap S <Plug>(SubversiveSubstituteToEndOfLine_and_fill_register)

svermeulen commented 3 years ago

I think the following should address your 1 and 2:

nnoremap <silent> s :set opfunc=SwapRegisterSubstitute<CR>g@
vnoremap <silent> s p

nmap ss s_
nmap S s$

function! SwapRegisterSubstitute(type, ...)
  if a:0
    silent exe "normal! `<" . a:type . "`>p"
  elseif a:type == 'line'
    silent exe "normal! '[V']p"
  elseif a:type == 'block'
    silent exe "normal! `[\<C-V>`]p"
  else
    silent exe "normal! `[v`]p"
  endif
endfunction

I don't really think it's worth putting it into this plugin though, since I doubt most people want it to work this way, and would add unnecessary scope. I think anyone that wants that behaviour can use something like the above

tuurep commented 3 years ago

Yes that's fine. Thank you, I'm trying that out and that code seems to work exactly the way I wanted. I didn't know how to do that on my own.

I think this can be closed then. :+1:

tuurep commented 3 years ago

Ok now I understand, if I use such function, it doesn't require the subversive plugin at all, so my request is really quite primitive. I think I should really get into vimscript now. Maybe I should read Learn Vimscript the Hard Way.

Thanks again, really, you helped me a lot.