18

I'm trying to understand how can I use operator with subsequent motion inside a mapping. For example:

nmap /c c{here we pending for a motion}/<C-r>"<CR>

The map should do the following:

  1. Activate c operator and listen for the next motion;
  2. Eg, I can type here t, to change everything before the next comma;
  3. Go to insert mode deleting everything between cursor and the comma;
  4. Deleted text is automatically searched as a pattern

So to put it simple, after motion text is removed, I'm leaved in insert mode with highlighted occurrences of the deleted text. I would be very grateful if somebody help me to puzzle out this case.

UPDATE

The answers are almost what I want. But! When I press /cw, type something instead of the word, then press <Esc>. After I expect to do the same with the next occurrences. But after presing n (go to next occurrence) and . (repeat last command) it just prepend last typed text instead of replacing it. The main goal of the mapping is using it with n/N and . to repeat. Have I missed something?

3 Answers 3

12

vim supports operator-mapping :h map-operator.

What you need is an operatorfunc, and a mapping. for your needs, the followings codes work. Well it is just an example, you refine further.

nmap  <silent> /c :set opfunc=SpecialChange<CR>g@
function! SpecialChange(type)
    silent exec 'normal! `[v`]d'    
    silent exec 'let @/=@"' 
    startinsert
endfunction

Note that exec 'let @/=@"' just for highlighting the codes in buffer. If you don't want to see the highlighting immediately, you can just let @/=@"

5
  • I've update my question. It seems your answer is the closest one to my needs, but it doesn't do the same with the next occurrence, so I've explained that moment in the question. Commented Nov 16, 2015 at 15:19
  • You have two operators (the /c and i) vim's . just repeats the last one. If you want to extend it, check repeat plugin. @TimurFayzrakhmanov also there are :g and :s commands, may help you too.
    – Kent
    Commented Nov 16, 2015 at 15:20
  • Is there odd " in your code 'let @/=@""'? I thought it is possible, for example silent exec 'normal! [v]c', because c operator do what I expect, except it doesn't add the changed text to the search pattern. Commented Nov 16, 2015 at 15:32
  • 1
    @TimurFayzrakhmanov the "" was a typo.. fix.. I came up with c too, however it added a space, because there is mode change n-i-n-i I guess... do some test you will see.
    – Kent
    Commented Nov 16, 2015 at 15:34
  • @TimurFayzrakhmanov you can make it repeatable without any plugin by changing d to y and startinsert to call feedkeys("cgn", 'n') (see my answer below) Commented Jul 9, 2016 at 10:26
4

It is easier to implement (and to document) complex mappings by using functions:

function! DoMagic()
   execute "normal! d".input("enter motion: ")
   let @/=@"
   startinsert
endfunction

Then make your mapping call that function:

nmap /c :call DoMagic()<CR>

Edit:

If your intention is to perform search & replace in a large number of places you should try the substitute command: :s. You could change your mapping to copy the visual selection to the search pattern:

function! DoMagic2()
   normal! gv"ay
   return @a
endfunction

vmap /c :<c-w>%s/<C-r>=DoMagic2()<CR>//gc<left><left><left>
6
  • 1
    this is even with a "nice" UI, works for OP's need, and can be "more" than expected, depends on what you input. E.g. when one see enter motion:, he gave: t,ggVGJZZ ^_^ It is a common problem for command concatenation
    – Kent
    Commented Nov 16, 2015 at 15:09
  • That is almost what I want. Eg: I press /cw then type something instead of the word, then press <Esc>. After I want to do the same with next occurrences. But after presing n (go to next occurrence) and . (repeat last command) it just prepend last typed text instead of replacing it. The main goal of the mapping is using it with n/N and . to repeat. Have I missed something? Commented Nov 16, 2015 at 15:13
  • 1
    This function is to clarify the idea of using functions when you have trouble with complex mappings. You should change it to meet your needs; try following Kent's advice to make the . repeat your command.
    – mMontu
    Commented Nov 16, 2015 at 15:22
  • Thank you for the answer. It's useful too, but I accept the @Kent 's one, because it do motion immediately after I type them (without pending <CR> from input()). Commented Nov 16, 2015 at 15:41
  • 1
    @TimurFayzrakhmanov you could also try the substitute, as explained in the update.
    – mMontu
    Commented Nov 16, 2015 at 15:42
2

This solution works with "repeat last command" (aka .):

nmap <silent> /c :set opfunc=ChangeOccurence<CR>g@

function! ChangeOccurence(type)
  exec 'normal! `[v`]y'
  let @/ = @"
  call feedkeys('cgn', 'n')
endfunction

When repeating, you don't even need to use n, you can just use . to directly fetch and replace the next occurence if you want to go faster.

Optionally, if you like having highlights of the matching words, you can replace 'cgn' by 'n``cgn'.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.