Vim Tips Wiki
Tip 1570 Printable Monobook Previous Next

created 2008 · complexity basic · author Jlepak · version 7.0

This tip uses the opfunc option to define two operators. Assuming the default backslash leader key, the operators are:

  • \c    to comment lines (insert a comment string before each line)
  • \C    to uncomment lines


For example, put the cursor anywhere in the def block in the following Python code:

class Example:

    def f(self, x):
        if x < 5:
            print "Pointless function."
        return 0

The command yip (yank inner paragraph) would copy the block. In a similar manner, the command \cip will comment-out the block, resulting in:

class Example:

    # def f(self, x):
    #     if x < 5:
    #         print "Pointless function."
    #     return 0

Later, you could remove the comment signifiers with \Cip.

An operator can be used in two ways:

  • Invoke the operator, then enter a movement command. or
  • Visually select a block, then invoke the operator.


  • \ciB    comment inner block (between braces)
  • \c}    comment to end paragraph
  • \cG    comment to end buffer
  • Vjjj\c    comment visually-selected lines


Here's the code. It only handles linewise comments.

" Comment or uncomment lines from mark a to mark b.
function! CommentMark(docomment, a, b)
  if !exists('b:comment')
    let b:comment = CommentStr() . ' '
  if a:docomment
    exe "normal! '" . a:a . "_\<C-V>'" . a:b . 'I' . b:comment
    exe "'".a:a.",'".a:b . 's/^\(\s*\)' . escape(b:comment,'/') . '/\1/e'

" Comment lines in marks set by g@ operator.
function! DoCommentOp(type)
  call CommentMark(1, '[', ']')

" Uncomment lines in marks set by g@ operator.
function! UnCommentOp(type)
  call CommentMark(0, '[', ']')

" Return string used to comment line for current filetype.
function! CommentStr()
  if &ft == 'cpp' || &ft == 'java' || &ft == 'javascript'
    return '//'
  elseif &ft == 'vim'
    return '"'
  elseif &ft == 'python' || &ft == 'perl' || &ft == 'sh' || &ft == 'R'
    return '#'
  elseif &ft == 'lisp'
    return ';'
  return ''

nnoremap <Leader>c <Esc>:set opfunc=DoCommentOp<CR>g@
nnoremap <Leader>C <Esc>:set opfunc=UnCommentOp<CR>g@
vnoremap <Leader>c <Esc>:call CommentMark(1,'<','>')<CR>
vnoremap <Leader>C <Esc>:call CommentMark(0,'<','>')<CR>


A custom operator can be defined using visual-mode maps (to apply the operator to a selection), and using the operatorfunc (opfunc) option (to apply the operator to a movement).

The g@ operator can be used in a map to define your own operator. When g@ is invoked, the function defined by the opfunc option is called with an argument indicating the type of motion ("line", "char" or "block"). In addition, the '[ and '] marks identify the start and end positions of the motion.

You can let the script determine the comment string from the filetype, or you can define the buffer-local variable comment, for example:

:let b:comment='#---'

See also[]