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
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.
Consider one of the plugins for writing custom text objects. For example: https://github.com/kana/vim-textobj-user
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.