Vim Tips Wiki
(New page: If you want to treat commenting lines of code just like deleting, whatever keystrokes you use should act just like the 'd' command. Defining a custom operator with the opfunc setting lets...)
 
(Change <tt> to <code>, perhaps also minor tweak.)
 
(3 intermediate revisions by 2 users not shown)
Line 1: Line 1:
  +
{{TipNew
If you want to treat commenting lines of code just like deleting, whatever keystrokes you use should act just like the 'd' command. Defining a custom operator with the opfunc setting lets you do that. With the set of commands below, you can use ,c (here mapleader is set to ,) just like d in normal mode.
 
  +
|id=1570
  +
|previous=1569
  +
|next=1571
  +
|created=2008
  +
|complexity=basic
  +
|author=Jlepak
  +
|version=7.0
  +
|subpage=/200806
  +
|category1=
  +
|category2=
  +
}}
  +
This tip uses the <code>opfunc</code> option to define two operators. Assuming the default backslash leader key, the operators are:
  +
*<code>\c</code> &nbsp;&nbsp; to comment lines (insert a comment string before each line)
  +
*<code>\C</code> &nbsp;&nbsp; to uncomment lines
   
  +
==Usage==
As an added bonus, the funny business in the first function with the c-v visual-block command formats things pretty nicely, with the comment signifiers lined up at the point of indentation of the first line of the commented block.
 
 
For example, put the cursor anywhere in the def block in the following Python code:
 
As an example, with the cursor anywhere in the def block, typing
 
 
<pre>
 
<pre>
 
class Example:
,cip
 
</pre>
 
will transform
 
<pre>
 
class Dumb:
 
   
def f(self,x):
+
def f(self, x):
 
if x < 5:
 
if x < 5:
 
print "Pointless function."
 
print "Pointless function."
 
return 0
 
return 0
 
</pre>
 
</pre>
  +
to
 
  +
The command <code>yip</code> (yank inner paragraph) would copy the block. In a similar manner, the command <code>\cip</code> will comment-out the block, resulting in:
 
<pre>
 
<pre>
class Dumb:
+
class Example:
   
# def f(self,x):
+
# def f(self, x):
 
# if x < 5:
 
# if x < 5:
 
# print "Pointless function."
 
# print "Pointless function."
Line 26: Line 36:
 
</pre>
 
</pre>
   
  +
Later, you could remove the comment signifiers with <code>\Cip</code>.
Here's the code (pretty short, but could certainly be made cleaner by someone who knows better). It only handles linewise comments.
 
   
  +
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.
  +
  +
Examples:
  +
*<code>\ciB</code> &nbsp;&nbsp; comment inner block (between braces)
  +
*<code>\c}</code> &nbsp;&nbsp; comment to end paragraph
  +
*<code>\cG</code> &nbsp;&nbsp; comment to end buffer
  +
*<code>Vjjj\c</code> &nbsp;&nbsp; comment visually-selected lines
  +
  +
==Script==
  +
Here's the code. It only handles linewise comments.
 
<pre>
 
<pre>
" Comment lines from mark a to mark b
+
" Comment or uncomment lines from mark a to mark b.
  +
function! CommentMark(docomment, a, b)
fun DoCommentMark(a,b)
 
exe "normal '" . a:a . "_\<C-V>'" . a:b . 'I' . b:comment . ' '
+
if !exists('b:comment')
 
let b:comment = CommentStr() . ' '
endfun
 
 
endif
" Handles the opfunc call
 
  +
if a:docomment
fun DoCommentOp(...)
 
 
exe "normal! '" . a:a . "_\<C-V>'" . a:b . 'I' . b:comment
call DoCommentMark('[', ']')
 
  +
else
endfun
 
  +
exe "'".a:a.",'".a:b . 's/^\(\s*\)' . escape(b:comment,'/') . '/\1/e'
" Handles visual mode
 
  +
endif
fun DoCommentV()
 
  +
endfunction
call DoCommentMark('<', '>')
 
endfun
 
   
" Uncomment lines from mark a to mark b
+
" Comment lines in marks set by g@ operator.
 
function! DoCommentOp(type)
fun UnCommentMark(a,b)
 
 
call CommentMark(1, '[', ']')
exe "'" . a:a . ",'" . a:b . 's/^\(\s*\)' . escape(b:comment,'/') . ' /\1/e'
 
  +
endfunction
endfun
 
fun UnCommentOp(...)
 
call UnCommentMark('[', ']')
 
endfun
 
fun UnCommentV()
 
call UnCommentMark('<', '>')
 
endfun
 
   
  +
" Uncomment lines in marks set by g@ operator.
map <leader>c <Esc>:set opfunc=DoCommentOp<CR>g@
 
map <leader>C <Esc>:set opfunc=UnCommentOp<CR>g@
+
function! UnCommentOp(type)
 
call CommentMark(0, '[', ']')
vmap <leader>c <Esc>:call DoCommentV()<CR>
 
  +
endfunction
vmap <leader>C <Esc>:call UnCommentV()<CR>
 
   
  +
" Return string used to comment line for current filetype.
" There's probably a better way to set the commenting character ...
 
  +
function! CommentStr()
au BufEnter * call SetComment()
 
 
if &ft == 'cpp' || &ft == 'java' || &ft == 'javascript'
fun SetComment()
 
  +
return '//'
let b:comment = ''
 
 
elseif &ft == 'vim'
if &ft == 'cpp' || &ft == 'java' || &ft == 'javascript'
 
  +
return '"'
let b:comment = '//'
 
elseif &ft == 'vim'
+
elseif &ft == 'python' || &ft == 'perl' || &ft == 'sh' || &ft == 'R'
  +
return '#'
let b:comment = '"'
 
  +
elseif &ft == 'lisp'
elseif &ft == 'python' || &ft == 'perl' || &ft == 'sh' || &ft == 'R'
 
  +
return ';'
let b:comment = '#'
 
  +
endif
elseif &ft == 'lisp'
 
  +
return ''
let b:comment = ';'
 
  +
endfunction
endif
 
  +
endfun
 
 
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>
 
</pre>
 
</pre>
  +
  +
==Explanation==
  +
A custom operator can be defined using visual-mode maps (to apply the operator to a selection), and using the <code>operatorfunc</code> (<code>opfunc</code>) option (to apply the operator to a movement).
  +
  +
The <code>g@</code> operator can be used in a map to define your own operator. When <code>g@</code> is invoked, the function defined by the <code>opfunc</code> option is called with an argument indicating the type of motion ("line", "char" or "block"). In addition, the <code>'[</code> and <code>']</code> 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 <code>comment</code>, for example:
 
<pre>
 
:let b:comment='#---'
 
</pre>
  +
  +
==See also==
  +
*[[VimTip1555|Creating new text objects]]
  +
*[[VimTip1516#Operator pending mode maps|Operator pending mode maps]]
  +
*[[VimTip271|Easy (un)commenting out of source code]]
  +
  +
==References==
  +
*{{help|'opfunc'}}
  +
  +
==Comments==

Latest revision as of 06:34, 13 July 2012

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

Usage[]

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.

Examples:

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

Script[]

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() . ' '
  endif
  if a:docomment
    exe "normal! '" . a:a . "_\<C-V>'" . a:b . 'I' . b:comment
  else
    exe "'".a:a.",'".a:b . 's/^\(\s*\)' . escape(b:comment,'/') . '/\1/e'
  endif
endfunction

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

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

" 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 ';'
  endif
  return ''
endfunction

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>

Explanation[]

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[]

References[]

Comments[]