How to define a new Vim operator with a parameter?

前端 未结 3 1349
慢半拍i
慢半拍i 2020-12-24 08:35

I have been looking to map a new operator in Vim that takes an extra parameter.

For example, we know that ciw will “cut inside word” and will put you int

相关标签:
3条回答
  • 2020-12-24 08:47

    The title of the question might cause misunderstanding. What you want to do is to define a new operator like y, d and c, neither motions nor text objects, isn't it? :help :map-operator describes how to define a new operator. To take a parameter like the surround plugin, use getchar() in your 'operatorfunc'.

    Though :help :map-operator describes the basics, it's a bit troublesome to deal with arguments passed to 'operatorfunc'. You can use vim-operator-user to simplify the handling of arguments. With this plugin, surround-like operator can be written as follows:

    function! OperatorSurround(motion_wise)
      let _c = getchar()
      let c = type(_c) == type(0) ? nr2char(_c) : _c
      if c ==# "\<Esc>" || c == "\<C-c>"
        return
      endif
    
      let bp = getpos("'[")
      let ep = getpos("']")
      if a:motion_wise ==# 'char'
        call setpos('.', ep)
        execute "normal! \"=c\<Return>p"
        call setpos('.', bp)
        execute "normal! \"=c\<Return>P"
      elseif a:motion_wise ==# 'line'
        let indent = matchstr(getline('.'), '^\s*')
        call append(ep[1], indent . c)
        call append(bp[1] - 1, indent . c)
      elseif a:motion_wise ==# 'block'
        execute bp[1].','.ep[1].'substitute/\%'.ep[2].'c.\zs/\=c/'
        execute bp[1].','.ep[1].'substitute/\%'.bp[2].'c\zs/\=c/'
        call setpos('.', bp)
      else
      endif
    endfunction
    call operator#user#define('surround', 'OperatorSurround')
    map s  <Plug>(operator-surround)
    

    If you really want to define your own text objects, please consider vim-textobj-user.

    0 讨论(0)
  • 2020-12-24 09:04

    Consider one of the plugins for writing custom text objects. For example: https://github.com/kana/vim-textobj-user

    0 讨论(0)
  • 2020-12-24 09:08

    Here is an example implementation of the command described in the question, for illustrative purposes.

    nnoremap <silent> s :set opfunc=Surround<cr>g@
    vnoremap <silent> s :<c-u>call Surround(visualmode(), 1)<cr>
    
    function! Surround(vt, ...)
        let s = InputChar()
        if s =~ "\<esc>" || s =~ "\<c-c>"
            return
        endif
        let [sl, sc] = getpos(a:0 ? "'<" : "'[")[1:2]
        let [el, ec] = getpos(a:0 ? "'>" : "']")[1:2]
        if a:vt == 'line' || a:vt == 'V'
            call append(el, s)
            call append(sl-1, s)
        elseif a:vt == 'block' || a:vt == "\<c-v>"
            exe sl.','.el 's/\%'.sc.'c\|\%'.ec.'c.\zs/\=s/g|norm!``'
        else
            exe el 's/\%'.ec.'c.\zs/\=s/|norm!``'
            exe sl 's/\%'.sc.'c/\=s/|norm!``'
        endif
    endfunction
    

    To get user input, the function InputChar() is used, assuming that the required argument is a single character.

    function! InputChar()
        let c = getchar()
        return type(c) == type(0) ? nr2char(c) : c
    endfunction
    

    If it is necessary to accept a string argument, change the call to InputChar() in Surround() to the call to input(), instead.

    0 讨论(0)
提交回复
热议问题