I have a simple custom operator, it echoes v:register, it's mapped to ,f. After reading this, i creat it like this :

function! s:foo(type,...)
  echo v:register

function! s:setupOpfunc(func)
  let &opfunc = a:func
  return 'g@'

nnoremap <expr> ,f <sid>setupOpfunc('<sid>foo')

It wors fine on built in textobjects, e.g. "x,faw output


But it doesn't work on custom textobject. Assume i have this simple custom text object:

vnoremap ix iw

" I know you can map to `iw` directly, but that's not the point of this problem.
onoremap ix :normal vix<cr>

"x,fix output


I think it doesn't work because :normal ... erased v:resiter. I tried to use onoremap <expr> ... to create omap for textobject, but :h map-expression says :normal( needed to visually select text object ) is blocked during it's expression evaluation.

Built in operator has no this kind of problem "xyix happily copy word into register x.

One crude way i know to make this work is to use some global variable:

function! s:setupOpfunc(func)
  let g:__opfunc_rgister = v:register

But it doesn't feel right, are there any other ways to make this work?

  • One option is to make the text objects take care of this. For instance, :onoremap ix :execute '"' . v:register . "normal vix"<cr> will preserve the registers. However, I looked up a popular text objects package (wellle/targets.vim) and noticed it doesn't preserve v:register across invocations, so I don't think that's the answer... This is a great question, also curious to figure out what the right answer to it is! – filbranden Jun 15 '19 at 18:15
  • @filbranden very few plugin do it the "right way" so that's not a great benchmark. Not to mention this could be a vim bug – Mass Jun 16 '19 at 1:30
  • @filbranden Your solution does output the right rigister, but " is start of comment in command line, so "xnormal vix is no different form "xbalabalabala, it's ignored, `] won't be set to the right value in opfunc. – dedowsdi Jun 16 '19 at 3:20
  • @dedowsdi Great catch! I noticed sometimes I wasn't seeing a motion, didn't realize that was working as a comment... – filbranden Jun 16 '19 at 3:37

Consider if you were doing some operation in visual mode for deleting in a word, you would use the following to put the deletion in register a.

viw"ad      ->   v        [iw]      ["a]       d
                 visual   object    register   operator

Similarly, placing "a in the omap after the operator ix passes it to the opfunc. Either of these alternatives seems to work:

onoremap ix :normal vix"<c-r>=v:register<cr><cr>

onoremap <expr> ix ':normal vix"'.v:register.'<cr>'

At the point of creation of the normal string, v:register is set correctly.

I think this is a general principle. If you make an omap that uses visual mode, you should save and append the register invocation for the sake of any following opfuncs

func! MyOmap()
    let l:reg = v:register
    normal! vaw
    execute 'normal! "' . l:reg 

All that being said, custom text objects do work built-in operators without this extra work, so maybe vim should be fixed do the right thing here, with custom op + custom object.

  • 1
    Nice solution! It never comes to me that one can add extra stuff after normal v** in the omap. I always thought "* must appears before g@. – dedowsdi Jun 16 '19 at 2:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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