Vim Tips Wiki
Advertisement

Duplicate tip

This tip is very similar to the following:

These tips need to be merged – see the merge guidelines.

Tip 271 Printable Monobook Previous Next

created June 30, 2002 · complexity intermediate · author jmpatton · version 5.7


Something that I do quite a lot is comment out blocks of text, only to uncomment that same block later. The following mappings have proven useful to me. They can be applied using visually selected blocks, or with motion keys.

" lhs comments
map ,# :s/^/#/<CR>
map ,/ :s/^/\/\//<CR>
map ,> :s/^/> /<CR>
map ," :s/^/\"/<CR>
map ,% :s/^/%/<CR>
map ,! :s/^/!/<CR>
map ,; :s/^/;/<CR>
map ,- :s/^/--/<CR>
map ,c :s/^\/\/\\|^--\\|^> \\|^[#"%!;]//<CR>

" wrapping comments
map ,* :s/^\(.*\)$/\/\* \1 \*\//<CR>
map ,( :s/^\(.*\)$/\(\* \1 \*\)/<CR>
map ,< :s/^\(.*\)$/<!-- \1 -->/<CR>
map ,d :s/^\([/(]\*\\|<!--\) \(.*\) \(\*[/)]\\|-->\)$/\2/<CR>

The commands to comment a selection of text are as follows, beginning with beginning-of-line comments:

 ,# shell, perl, etc
 ,/ c++
 ,> email quote
 ," vim
 ,% latex, prolog
 ,! assembly/X-resources
 ,; scheme
 ,- sql, ada
 ,c clears any of the previous comments

Here are the wrapping comments, each line wrapped individually:

,* c
,( Standard ML
,< html
,d clears any of the wrapping comments

Comments[]

After executing this tip, the content of "/ is highlighted in my gvim; which is rather annoying. In e.g. MapBasic which I'm trying to figure out right now, I use:

map ,' :s/^/'/<CR> :let @/=""<CR>

Just add :nohlsearch <CR> at the end of the mapping and it would remove the highlight. Highlighting would be re-enabled when you do the next search. I checked it out it works

" lhs comments
map ,# :s/^/#/<CR> <Esc>:nohlsearch <CR>
map ,/ :s/^/\/\//<CR> <Esc>:nohlsearch <CR>
map ,> :s/^/> /<CR> <Esc>:nohlsearch<CR>
map ," :s/^/\"/<CR> <Esc>:nohlsearch<CR>
map ,% :s/^/%/<CR> <Esc>:nohlsearch<CR>
map ,! :s/^/!/<CR> <Esc>:nohlsearch<CR>
map ,; :s/^/;/<CR> <Esc>:nohlsearch<CR>
map ,- :s/^/--/<CR> <Esc>:nohlsearch<CR>
map ,c :s/^\/\/\\|^--\\|^> \\|^[#"%!;]//<CR> <Esc>:nohlsearch<CR>

" wrapping comments
map ,* :s/^\(.*\)$/\/\* \1 \*\//<CR> <Esc>:nohlsearch<CR>
map ,( :s/^\(.*\)$/\(\* \1 \*\)/<CR><Esc>:nohlsearch <CR>
map ,< :s/^\(.*\)$/<!-- \1 -->/<CR> <Esc>:nohlsearch<CR>
map ,d :s/^\([/(]\*\\|<!--\) \(.*\) \(\*[/)]\\|-->\)$/\2/<CR> <Esc>:nohlsearch<CR>

Adding :nohlsearch<CR> to the end of each pattern clears up the highlighting. Note that this doesn't turn off search highlighting, it just removes the highlighting that's there. Here are the amended regexs:

" lhs comments
map ,# :s/^/#/<CR>:nohlsearch<CR>
map ,/ :s/^/\/\//<CR>:nohlsearch<CR>
map ,> :s/^/> /<CR>:nohlsearch<CR>
map ," :s/^/\"/<CR>:nohlsearch<CR>
map ,% :s/^/%/<CR>:nohlsearch<CR>
map ,! :s/^/!/<CR>:nohlsearch<CR>
map ,; :s/^/;/<CR>:nohlsearch<CR>
map ,- :s/^/--/<CR>:nohlsearch<CR>
map ,c :s/^\/\/\\|^--\\|^> \\|^[#"%!;]//<CR>:nohlsearch<CR>

" wrapping comments
map ,* :s/^\(.*\)$/\/\* \1 \*\//<CR>:nohlsearch<CR>
map ,( :s/^\(.*\)$/\(\* \1 \*\)/<CR>:nohlsearch<CR>
map ,< :s/^\(.*\)$/<!-- \1 -->/<CR>:nohlsearch<CR>
map ,d :s/^\([/(]\*\\|<!--\) \(.*\) \(\*[/)]\\|-->\)$/\2/<CR>:nohlsearch<CR>

A more intelligent way is to save the search pattern highlighted. let hls=@/|s ... |let @/=hls


The problem with that is that it thwarts motion keys. The way it stands you can do something like '3,#' and it will comment 3 lines. Ranges don't work with 'let ...', unfortunately.


This function is based on the various comments here, and seems to work.

fun CppstyleQuote()
  let hls=@/
  s/^/\/\//
  let @/=hls
endfun
map ,/ :call CppstyleQuote()<CR>

" I use a single mapping, which can comment/uncomment line:
function! C_CommentLine()
  if getline(".") =~ '/\*.*\*/'
    normal ^2x$xx
  else
    normal I/*A*/
  endif
endfunction
nmap <buffer> <Esc>d :call C_CommentLine()<LF>

" <Esc>d on my term means <M-D> or Alt-D

" This mapping comments the visual selection, You have to perform a selection from left to
" right or from top to bottom. If you do a linewise selection, the cursor
" position, not the end of selection matters.
vmap <buffer> <Esc>d a*/gvoi/*

I use the mouse to select the lines I want to comment/uncomment and have this on the right click menu to insert/delete // at the start of the line:

:vmenu PopUp.Comments.Add :s/^/\/\//<CR>
:vmenu PopUp.Comments.Remove :s/^..//<CR>

those are great, but I don't like the highlighted search and also i like my // always at the very beginning of the line, so I've wrote this function (very newbie and unoptimized, but it works)

It comments and uncomments the current line:

function! MyComm()
  let linenum= line('.')
  let line = getline('.')
  let commpos= match(line, "//")
  let n = 0
  while n< commpos
    if line[n]!= " " && line[n]!= "\t"
      break
    endif
    let n= n+1
  endwhile
  if n== commpos && commpos!= -1
    let line= strpart(line, 0, commpos).strpart(line, commpos+2)
  else
    let line= "//".line
  endif
  let err= setline(linenum, line)
endfunction
map <M-c> :call MyComm()<CR>
imap <M-c> <Esc>:call MyComm()<CR>i

" for the /* */ pair, I use visual mode, an then alt-v:
vmap <M-v> v`<I<CR><Esc>k0i/*<Esc>`>I<CR><Esc>k0i*/<Esc>

Using visual block selection at the start of the lines you want to comment, along with 'I' (CAPS-EYE), and the comment character is also very handy. Decommenting would be to select the comments in a visual block and delete them.


A few people have said something like this, but I have found it more useful to not need to remember a different mapping for each language I'm editing. To this end, I have several different functions that set up commands based on which language I'm editing. Like so:

" Define functions
function! PoundComment()
  map - :s/^/# /<CR>
  map _ :s/^\s*# \=//<CR>
  set comments=:#
endfunction

function! SlashComment()
  map - :s/^/\/\/ /<CR>
  map _ :s/^\s*\/\/ \=//<CR>
endfunction

" And then later...
autocmd FileType perl call PoundComment()
autocmd FileType cgi call PoundComment()
autocmd FileType csh call PoundComment()
autocmd FileType sh call PoundComment()
autocmd FileType java call SlashComment()

You get the idea. Now, I can always comment a line/range of lines with - (hyphen), and uncomment it with _ (underscore).


These will handle the :nohl search highlighting case nicely (nothing's changed, it's automatic)

map \# :call Comment()<CR>
map \-# :call Uncomment()<CR>
map \--# :call UncommentBlock()<CR>

" Comments range (handles multiple file types)
function! Comment() range
  if &filetype == "c" || &filetype == "php" || &filetype == "css"
    execute ":" . a:firstline . "," . a:lastline . 's/^\(.*\)$/\/\* \1 \*\//'
  elseif &filetype == "html" || &filetype == "xml" || &filetype == "xslt" || &filetype == "xsd"
    execute ":" . a:firstline . "," . a:lastline . 's/^\(.*\)$/<!-- \1 -->/'
  else
    if &filetype == "java" || &filetype == "cpp" || &filetype == "cs"
      let commentString = "//"
    elseif &filetype == "vim"
      let commentString = '"'
    else
      let commentString = "#"
    endif
    execute ":" . a:firstline . "," . a:lastline . 's,^,' . commentString . ','
  endif
endfunction

" Uncomments range (handles multiple file types)
function! Uncomment() range
  if &filetype == "c" || &filetype == "php" || &filetype == "css" || &filetype == "html" || &filetype == "xml" || &filetype == "xslt" || &filetype == "xsd"
    " [[VimTip271]]
    execute ":" . a:firstline . "," . a:lastline . 's/^\([/(]\*\|<!--\) \(.*\) \(\*[/)]\|-->\)$/\2/'
  else
    if &filetype == "java" || &filetype == "cpp" || &filetype == "cs"
      let commentString = "//"
    elseif &filetype == "vim"
      let commentString = '"'
    else
      let commentString = "#"
    endif
    execute ":" . a:firstline . "," . a:lastline . 's,^' . commentString . ',,'
  endif
endfunction

" Uncomments from current line up to last line that's commented
function! UncommentBlock()
  if &filetype == "c" || &filetype == "php" || &filetype == "css" || &filetype == "html" || &filetype == "xml" || &filetype == "xslt" || &filetype == "xsd"
    echoerr "TODO: haven't implemented UncommentBlock; use Uncomment instead"
  else
    if &filetype == "java" || &filetype == "cpp" || &filetype == "cs"
      let commentString = '\/\/'
      let firstChar = '/'
    elseif &filetype == "vim"
      let commentString = '"'
      let firstChar = '"'
    else
      let commentString = '#'
      let firstChar = '#'
    endif
    if version < 600 && strlen( commentString ) > 1
      echoerr "TODO: haven't implemented multi-character comment block"
    else
      " TODO: doesn't handle case where the block ends at end of file
      execute ':.,/^\(\(' . commentString . '\)\@!\|[^' . firstChar . ']\|$\)/-1s/^' . commentString . "//"
    endif
  endif
endfunction

Based on the above and having 4 things in mind: (1) single keystroke (2) no highlighting (3) C language (4) being simple - my version became:

function! Komment()
  if getline(".") =~ '\/\*'
    let hls=@/
    s/^\/\*//
    s/*\/$//
    let @/=hls
  else
    let hls=@/
    s/^/\/*/
    s/$/*\//
    let @/=hls
  endif
endfunction
map k :call Komment()<CR>

So pressing k will comment out the current line if it is not already so and will uncomment it if it is commented already. Really handy :)


See script#955 I "improved" the original tip a little:

  • no "remove comment" key, but the comment-status of a line is toogled
  • no hlsearch-problem
  • commenting uses the original indentation, not the first column

I found the Nerd commenter script very valuable: Nerd Commenter


I like Enhanced Commentify. It has a lot of functionality and options.

If you want simplicity, ToggleCommentify is available.


To do this quickly with a visual selection you could just type shift-i and then the comment character(s) followed by Esc or ctrl-c which will insert the characters at the start of the selection at every line.


Advertisement