ChatGPT解决这个技术问题 Extra ChatGPT

What's a quick way to comment/uncomment lines in Vim?

I have a Ruby code file open in vi, there are lines commented out with #:

class Search < ActiveRecord::Migration
  def self.up
    # create_table :searches do |t|
    #   t.integer :user_id
    #   t.string :name
    #   t.string :all_of
    #   t.string :any_of
    #   t.string :none_of
    #   t.string :exact_phrase
    # 
    #   t.timestamps
    # end
  end

  def self.down
    # drop_table :searches
  end
end

Say I want to uncomment all the lines in the first def ... end section. What's an efficient way to do that in Vim?

In general, I'm looking for an easy and fluid way to comment and uncomment lines. Here I'm dealing with Ruby code, but it could be JavaScript (//) or Haml (-#).

The accepted answer should be changed to one of the answers that has detailed instructions on how to accomplish commenting/uncommenting blocks without using a plugin. The current accepted answer is basically just a link to a third-party plugin.
The best rated answer does not mention any plugins, @rationalis your comment is misleading, could you please either correct it or remove it, thank you.
Accpted Answer should be Magnus answer

E
Eric O Lebigot

For those tasks I use most of the time block selection.

Put your cursor on the first # character, press CtrlV (or CtrlQ for gVim), and go down until the last commented line and press x, that will delete all the # characters vertically.

For commenting a block of text is almost the same:

First, go to the first line you want to comment, press CtrlV. This will put the editor in the VISUAL BLOCK mode. Then using the arrow key and select until the last line Now press ShiftI, which will put the editor in INSERT mode and then press #. This will add a hash to the first line. Then press Esc (give it a second), and it will insert a # character on all other selected lines.

For the stripped-down version of vim shipped with debian/ubuntu by default, type : s/^/# in the third step instead (any remaining highlighting of the first character of each line can be removed with :nohl).

Here are two small screen recordings for visual reference.

https://i.stack.imgur.com/lu6aU.gif

https://i.stack.imgur.com/2Y7eH.gif


By default it's CTRL+V. The windows version of gvim uses Ctrl+Q because Ctrl+V is already used for paste.
How would you do this with '//'?
You can click Esc two times to not wait that second ;)
This didn't work for me. Shift-I went into simple insertion mode.
For people that shif-i didn't work for them: you should press ESC to see the pasted text in the other lines.
S
SO_fix_the_vote_sorting_bug

To comment out blocks in vim:

press Esc (to leave editing or other mode)

hit ctrl+v (visual block mode)

use the ↑/↓ arrow keys to select lines you want (it won't highlight everything - it's OK!)

Shift+i (capital I)

insert the text you want, e.g. %

press EscEsc

To uncomment blocks in vim:

press Esc (to leave editing or other mode)

hit ctrl+v (visual block mode)

use the ↑/↓ arrow keys to select the lines to uncomment. If you want to select multiple characters, use one or combine these methods: use the left/right arrow keys to select more text to select chunks of text use shift + ←/→ arrow key you can repeatedly push the delete keys below, like a regular delete button

use the left/right arrow keys to select more text

to select chunks of text use shift + ←/→ arrow key

you can repeatedly push the delete keys below, like a regular delete button

press d or x to delete characters, repeatedly if necessary


@amelia : The commenting shortcut doesn't work for me. Shift + i takes me to insert mode. Does it depend on vim version?
Why does it take a second?
The only issue I have with this answer is that it tells you to use arrow keys.
Hit Esc twice instead. :)
At first enabling comments didn't work for me but after reading that once again worked fine: 1. make sure you use Ctrl-V, not V for selection 2. when inserting it will appear as you are modifying only a single line 3. all inserts happen when you hit Esc in the end
M
Magnus

Sometimes I'm shelled into a remote box where my plugins and .vimrc cannot help me, or sometimes NerdCommenter gets it wrong (eg JavaScript embedded inside HTML).

In these cases a low-tech alternative is the built-in norm command, which just runs any arbitrary vim commands at each line in your specified range. For example:

Commenting with #:

1. visually select the text rows (using V as usual)
2. :norm i#

This inserts "#" at the start of each line. Note that when you type : the range will be filled in, so it will really look like :'<,'>norm i#

Uncommenting #:

1. visually select the text as before (or type gv to re-select the previous selection)
2. :norm x

This deletes the first character of each line. If I had used a 2-char comment such as // then I'd simply do :norm xx to delete both chars.

If the comments are indented as in the OP's question, then you can anchor your deletion like this:

:norm ^x

which means "go to the first non-space character, then delete one character". Note that unlike block selection, this technique works even if the comments have uneven indentation!

Note: Since norm is literally just executing regular vim commands, you're not limited to comments, you could also do some complex editing to each line. If you need the escape character as part of your command sequence, type ctrl-v then hit the escape key (or even easier, just record a quick macro and then use norm to execute that macro on each line).

Note 2: You could of course also add a mapping if you find yourself using norm a lot. Eg putting the following line in ~/.vimrc lets you type ctrl-n instead of :norm after making your visual selection

vnoremap <C-n> :norm

Note 3: Bare-bones vim sometimes doesn't have the norm command compiled into it, so be sure to use the beefed up version, ie typically /usr/bin/vim, not /bin/vi

(Thanks to @Manbroski and @rakslice for improvements incorporated into this answer)


@Shyam The ctrl-v technique combined with special block-selection-only commands is what most of the other answers recommend; however I personally find the "norm" technique I've described to be easier because it doesn't introduce any new syntax besides the norm command itself, so I can reuse what I already know about vim.
For uncommenting an indented block, it is useful to say :norm ^x. This method in general has the advantage of working with region selections (e.g. vi{ will select inside curly braces). Such text object selectors do not work with Visual Block.
Ah, I just figured it out -- on centos 6 the /bin/vi is vim 7.2, but it's a different build than /usr/bin/vim, and it has features like this turned off.
This is by far the best answer. Especially when combined with vip to select a whole paragraph.
This is so awesome. I am an AVID macro user. I create macros (often times recursive) on the fly for keystrokes that I am going to have to repeat even once. I just think in macros now. I never knew about :normal. I'm going to use the heck out of it.
B
Bhargav Rao

I use the NERD Commenter script. It lets you easily comment, uncomment or toggle comments in your code.

As mentioned in the comments:

for anyone who is confused by the usage, default leader is "\" so 10\cc will comment ten lines and 10\cu will uncomment those ten lines


Interesting! I read the Doc and find there is a "sexy comment" -- just use "\cs". For Ruby, it will use =begin and =end to comment multiple lines instead of hash tags.
I think that it isn't the quickest way to do it with vim since it requires to install a plugin. Also the best answer has already received more votes but it hasn't been marked as solution.
Don't stop here. Most voted answers are below without the requirement of any plugins. stackoverflow.com/a/15588798/2117868 and stackoverflow.com/a/1676690/2117868
@whirmill I think "best" really depends on use case. Visual block mode is faster if I want to toggle comments once in my life. But if I don't mind installing a plugin and want to make as few keystrokes as possible to toggle comments and not have to differentiate operation between adding or removing comments - then this here might be the "best answer".
@whirmill Great point. Though I could argue that "quickest" might have those multiple interpretations as well. Though yours would be more common/likely. :)
B
Bruno Bronosky

I have the following in my .vimrc:

" Commenting blocks of code.
augroup commenting_blocks_of_code
  autocmd!
  autocmd FileType c,cpp,java,scala let b:comment_leader = '// '
  autocmd FileType sh,ruby,python   let b:comment_leader = '# '
  autocmd FileType conf,fstab       let b:comment_leader = '# '
  autocmd FileType tex              let b:comment_leader = '% '
  autocmd FileType mail             let b:comment_leader = '> '
  autocmd FileType vim              let b:comment_leader = '" '
augroup END
noremap <silent> ,cc :<C-B>silent <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:nohlsearch<CR>
noremap <silent> ,cu :<C-B>silent <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:nohlsearch<CR>

Now you can type ,cc to comment a line and ,cu to uncomment a line (works both in normal and visual mode).

(I stole it from some website many years ago so I can't completely explain how it works anymore :). There is a comment where it is explained.)


what is the shortcut I should use? I can't quite make sure from the vim code itself!
in normal or visual mode, use ",cc" (3 character sequence) to comment the current line, and ",cu" to uncomment the current line.
i like it :)! thanks! On a side note i don't find it to hard to explain. a) it remaps a command (non recursively [see this ](stackoverflow.com/questions/3776117/…) so now when you press ,cc the :... thing gets executed. b) now this is basically a sed (s/what/towhat/where) command changing ^ (start of line) to the correctly set comment character based on the type of file you have opened c) as for the silent thingies they just suppress output from commands. d):nohlsearch stops it from highlighting the sed search
Note, this is not the correct way to load autocommands. They should be inside an augroup or else they will be added to vim multiple times and cause a lot of slow down. See: learnvimscriptthehardway.stevelosh.com/chapters/14.html. I've added my answer to this question.
My mod which defaults to // for unlisted filetypes and uses <Leader> (default \ ) instead of , and adds/removes the comment character after any indentation: gist.github.com/zonidjan/7fc11c2e9574c84383a87a8fcbe4b1ca
J
JonnyRaa

Specify which lines to comment in vim:

Reveal the line numbers:

:set number

then

:5,17s/^/#/     this will comment out line 5-17

or this:

:%s/^/#/        will comment out all lines in file

Since you're just changing the 1st char of each line, you don't need the "g" at the end
Some vims on embedded boxes (like openwrt) don't have visual mode .. So this is freaking awesome :)
can you explain why :%s/^/#/g will comment out all lines ? I was wondering the percent sign %
And to uncomment these lines you can write :5,17s/^#/
Great! It really works nicely with block selection like: va{ or with var for ruby.
i
isomorphismes

Here is how I do it:

Go to first character on the first line you want to comment out. Hit Ctrl+q in GVIM or Ctrl+v in VIM, then go down to select first character on the lines to comment out. Then press c, and add the comment character.

Uncommenting works the same way, just type a space instead of the comment character.


c deletes the first character as well. CMS's answer has it right i.e. pressing I then typing out the comment character(s) and then Esc (this is on windows vim)
This works, except 'r' needs to be pressed at step three, not 'c'.
alternatively you can press ESC twice after pressing c and that should do the trick
All of these options are destructive of the first character on the line.
F
Flavio Wuensche

Toggle comments

If all you need is toggle comments I'd rather go with commentary.vim by tpope.

https://i.stack.imgur.com/NYiEh.gif

Installation

Pathogen:

cd ~/.vim/bundle
git clone git://github.com/tpope/vim-commentary.git

vim-plug:

Plug 'tpope/vim-commentary'

Vundle:

Plugin 'tpope/vim-commentary'

Further customization

Add this to your .vimrc file: noremap <leader>/ :Commentary<cr>

You can now toggle comments by pressing Leader+/, just like Sublime and Atom.


thanks! is it going to support css commenting inside an html sometime in the future?
But sometimes you don't want to bloat servers with plugins.
For me this was the best solution. I already had. the plugin installed but forgot to implement the keybind. Works like a charm now!
Nice, thanks! this is how I customized line comment with C-/ for both normal and insert mode: nnoremap <C-_> :Commentary<cr>j and inoremap <C-_> <Esc>:Commentary<cr>ji. I don't know the reason but vim recognises / as _, that's why I used C-_.
This is really useful.
i
ideasman42

I've come up with a simple addition to my .vimrc file which works pretty well and can be extended easily. You simply add a new filetype to the comment_map and its comment leader.

I added a mapping to normal and visual modes, but you can remap to anything you like. I prefer only to have a 'toggle' style function. One bears having multiple mappings etc.

let s:comment_map = { 
    \   "c": '\/\/',
    \   "cpp": '\/\/',
    \   "go": '\/\/',
    \   "java": '\/\/',
    \   "javascript": '\/\/',
    \   "lua": '--',
    \   "scala": '\/\/',
    \   "php": '\/\/',
    \   "python": '#',
    \   "ruby": '#',
    \   "rust": '\/\/',
    \   "sh": '#',
    \   "desktop": '#',
    \   "fstab": '#',
    \   "conf": '#',
    \   "profile": '#',
    \   "bashrc": '#',
    \   "bash_profile": '#',
    \   "mail": '>',
    \   "eml": '>',
    \   "bat": 'REM',
    \   "ahk": ';',
    \   "vim": '"',
    \   "tex": '%',
    \ }

function! ToggleComment()
    if has_key(s:comment_map, &filetype)
        let comment_leader = s:comment_map[&filetype]
        if getline('.') =~ "^\\s*" . comment_leader . " " 
            " Uncomment the line
            execute "silent s/^\\(\\s*\\)" . comment_leader . " /\\1/"
        else 
            if getline('.') =~ "^\\s*" . comment_leader
                " Uncomment the line
                execute "silent s/^\\(\\s*\\)" . comment_leader . "/\\1/"
            else
                " Comment the line
                execute "silent s/^\\(\\s*\\)/\\1" . comment_leader . " /"
            end
        end
    else
        echo "No comment leader found for filetype"
    end
endfunction


nnoremap <leader><Space> :call ToggleComment()<cr>
vnoremap <leader><Space> :call ToggleComment()<cr>

Note:

I don't use any callbacks or hooks into the file types/loading, because I find they slow down Vim's startup more than the .vimrc static function/map does but that's just my preference. I've also tried to keep it simple and performant. If you do use autocommands you need to be sure to put them in an autocommand group or else the callbacks get added to the filetype multiple times per-file loaded and cause a lot of performance degradation.


I'm completely new to vim, what button should I press to toggle the mapped function? What's that <leader><Space> declaration at the bottom?
You can replace with a key like <,>. Then you press ,SPACE and it will toggle the line's comment state. Leader is whatever your leader is, Vim's default is \, but you can set your own like "let mapleader = ','"
Great answer, one annoyance though, commenting blocks that already have some comments, will swap commented for un-commented lines. QtCreator for eg only removes comments if all non-empty lines have leading comments, else add a leading comment.
I have made a slightly different version using \zs and \ze regex trick, the code became a little smaller. you can see it here
This is the method that I use. If you're running it on an older version of vim, you might need to remove any trailing spaces in code lines to get it to work properly. I used <C-_> to match Control-/ to match most other line comment keybinds.
R
R. Martinho Fernandes

Use Control-V to select rectangles of text: go to the first # character, type Ctrl+V, move right once, and then down, up to the end of the comments. Now type x: you're deleting all the # characters followed by one space.


A
Andy

I use vim 7.4 and this works for me. Assuming we are commenting/uncommenting 3 lines.

To comment:

if the line has no tab/space at the beginning:
ctrl + V then jjj then shift + I (cappital i) then //then esc esc
if the line has tab/space at the beginning you still can do the above or swap for c:
ctrl + V then jjj then c then //then esc esc

To uncomment:

if the lines have no tab/space at the beginning:
ctrl + V then jjj then ll (lower cap L) then c

if the lines have tab/space at the beginning, then you space one over and esc
ctrl + V then jjj then ll (lower cap L) then c then space then esc


i
innaM

Here is a section of my .vimrc:

"insert and remove comments in visual and normal mode
vmap ,ic :s/^/#/g<CR>:let @/ = ""<CR>
map  ,ic :s/^/#/g<CR>:let @/ = ""<CR>
vmap ,rc :s/^#//g<CR>:let @/ = ""<CR>
map  ,rc :s/^#//g<CR>:let @/ = ""<CR>

In normal and in visual mode, this lets me press ,ic to insert comments and,rc to remove comments.


This is very helpful for a beginner how to learn writing own .vimrc .
map covers normal and visual modes, so you don't need the vmap lines
Better place is in after/ftplugin/ruby.vim.
also use <leader>ic and <leader>rc
M
Markus Dutschke

I combined Phil and jqno's answer and made untoggle comments with spaces:

autocmd FileType c,cpp,java,scala let b:comment_leader = '//'
autocmd FileType sh,ruby,python   let b:comment_leader = '#'
autocmd FileType conf,fstab       let b:comment_leader = '#'
autocmd FileType tex              let b:comment_leader = '%'
autocmd FileType mail             let b:comment_leader = '>'
autocmd FileType vim              let b:comment_leader = '"'
function! CommentToggle()
    execute ':silent! s/\([^ ]\)/' . escape(b:comment_leader,'\/') . ' \1/'
    execute ':silent! s/^\( *\)' . escape(b:comment_leader,'\/') . ' \?' . escape(b:comment_leader,'\/') . ' \?/\1/'
endfunction
map <F7> :call CommentToggle()<CR>

how it works:

Lets assume we work with #-comments.

The first command s/\([^ ]\)/# \1/ searches for the first non-space character [^ ] and replaces that with # +itself. The itself-replacement is done by the \(..\) in the search-pattern and \1 in the replacement-pattern.

The second command s/^\( *\)# \?# \?/\1/ searches for lines starting with a double comment ^\( *\)# \?# \? (accepting 0 or 1 spaces in between comments) and replaces those simply with the non-comment part \( *\) (meaning the same number of preceeding spaces).

For more details about vim patterns check this out.


Y
Yugandhar Chaudhari

Visual and Shift-I did not worked for me.

Simplest that worked without any plugins is

Select block - V then j or k or any relevant motion (Don't use arrow keys) :) Then hit : it prompts command to :'<,'> To Comment Using # - `s/^/#/` Using `//` - `s/^/\/\//` To Uncomment Using # - `s/^#//` Using `//` - `s/^\/\//`

Exaplanation -

'<,'> - Apply to visual block

s - substitute

^ - starts with

after / add character # in this case of \/\/ escaped for //

Update

I wrote a function to comment and uncomment current line with <Space><Space>

Works for next 10 lines for example 10<Space><Space>

Paste it to .vimrc

function CommentUncomment()
  let line = getline('.')
  if line[:1] == "//"
      norm ^2x
  else 
      norm I//
  endif
endfunction

nnoremap <Space><Space> :call CommentUncomment()<CR>

This is far the easiest way I found. In this case the full command to insert command would be '<,'>s/^/#/g. For me, it wouldn't work if not add /g at the end, which indicates the cursor to move at the end of the '<,'>` range
If you have number lines enabled, you can do this: :1,10 s/^/#/ to comment lines 1 to 10.
No need to escape the slashes, just use a different separator charakter: :s#^#//
u
uckelman

If you already know the line numbers, then n,ms/# // would work.


really that should probably be: n,ms/^\s.#// Because you might have leading white space and might not follow the hash with one
i
imagineerThat

With 30 answers ahead of me, I'll try to give an even easier solution: Insert a # at the beginning of the line. Then go down a line and press dot (.). To repeat, do j,.,j,., etc...To uncomment, remove a # (you can hit x over the #), and do the reverse using k,.,etc...


It's a very simple answer even beginner can understand and use. However, it works pretty slowly on big amounts of lines to comment. To work around that you can write I#<Esc>j to the buffer - say, c - and then do 10@c, or whatever number of lines suits you.
Pretty cumbersome key combination for such simple task :( I am commenting and uncommenting stuff a lot using cmd+/ and lack of that function built-in is the reason I am not using vim for serious work
K
K.Dᴀᴠɪs

How to uncomment the following three lines in vi:

#code code
#code
#code code code

Place the cursor over the upper left # symbol and press CtrlV. This puts you in visual block mode. Press the down arrow or J three times to select all three lines. Then press D. All the comments disappear. To undo, press U.

How to comment the following three lines in vi:

code code
code
code code code

Place the cursor over the upper left character, press CtrlV. This puts you in visual block mode. Press ↓ or J three times to select all three lines. Then press:

I//Esc

That's a capital I, //, and Escape.

When you press ESC, all the selected lines will get the comment symbol you specified.


if you miss the "upper left" hash, you can press o in order to move the cursor to the "other side" in visual mode.
I think this is best to use. No need any third parties, just use native vim
best answer, simple and without any third parties
R
Rick

I like to use the tcomment plugin: http://www.vim.org/scripts/script.php?script_id=1173

I have mapped gc and gcc to comment a line or a highlighted block of code. It detects the file type and works really well.


R
Rich

Yes, there are 33 (mostly repetitive) answers already to this question.

Here is another approach to how to comment lines out in Vim: motions. The basic idea is to comment or uncomment lines out using the same method as yanking a paragraph by typing yip or deleting 2 lines by typing dj.

This approach will let you do things like:

ccj to comment the next 2 lines out, and cuk to uncomment them;

cci{ to comment a block out, and cui{ to uncomment it;

ccip to comment a whole paragraph out, and cuip to uncomment it.

ccG to comment everything out down to the last line, and cugg to uncomment everything up to the first line.

All you need are 2 functions that operate over motions, and 2 mappings for each function. First, the mappings:

nnoremap <silent> cc  :set opfunc=CommentOut<cr>g@
vnoremap <silent> cc  :<c-u>call  CommentOut(visualmode(), 1)<cr>
nnoremap <silent> cu  :set opfunc=Uncomment<cr>g@
vnoremap <silent> cu  :<c-u>call  Uncomment(visualmode(), 1)<cr>

(See the manual about the g@ operator and the operatorfunc variable.)

And now the functions:

function! CommentOut(type, ...)
  if a:0
    silent exe "normal!  :'<,'>s/^/#/\<cr>`<"
  else
    silent exe "normal!  :'[,']s/^/#/\<cr>'["
  endif
endfunction

function! Uncomment(type, ...)
  if a:0
    silent exe "normal!  :'<,'>s/^\\(\\s*\\)#/\\1/\<cr>`<"
  else
    silent exe "normal!  :'[,']s/^\\(\\s*\\)#/\\1/\<cr>`["
  endif
endfunction

Modify the regular expressions above to suit your taste as to where the # should be:


"completely new [...] motions" seems a bit over the top: the t_comment and vim-commentary plugins, both of which predate this answer, allow you to comment out using motions.
Good stuff! Upvoted. (I also think I might start using this approach instead of the plugin I was using previously, so thanks for writing it!)
f
fugu

There is this life changing plugin by tpope called vim-commentary

https://github.com/tpope/vim-commentary

This plugin provides:

Sanity

Properly indented comments

Does not comment out empty/unnecessary lines

Usage:

Install via Vundle (or Pathogen I guess).

Highlight your text and press : which will show as :<,'>

Type Commentary here :<,'>Commentary and press Enter.

Boom. Your done bud.


vim-commentary (like all of tpope's plugins) has the bonus of being idiomatic vim. gc = "go comment", gcap = "go comment a paragraph", etc.
Could this have just been an edit of the Tim Pope answer by Jim Stewart?
S
SDGator

I mark the first and last lines (ma and mb), and then do :'a,'bs/^# //


p
pylang

A few regular Vim commands do not work with my setup on Windows. Ctrl + v and Ctrl + q are some of them. I later discovered the following methods worked to uncomment lines.

Given

Some indented comments

   # Practice in Vim
   # Practice in Vim
   # Practice in Vim
   # Practice in Vim
   # Practice in Vim
   # Practice in Vim
   # Practice in Vim

The following approaches remove the # symbol and preserve indents.

Approaches

Move the cursor to the first comment (arrows or h, j, k, l). Then apply one of the following techniques:

Visual Block Mode (faster)

Ctrl + Shift + v to enter visual block mode

js to choose the vertical lines.

l to include horizontal characters (optional)

x to delete the block

Search/Replace + Regex

Choose text with regular visual mode, i.e. Shift + v

Type :. You'll get this prompt '<,'>.

Type regex, e.g. s/#// substitutes the hash with nothing. (Optional: type s/# // to include the space).

Enter

:norm command

Choose text with regular visual mode, i.e. Shift + v

Type :. You'll get this prompt '<,'>.

Give a command. Type :norm ^x to remove the first non-whitespace character and the next character. (Optional: try :norm x if not indented or :norm ^xx to include the space).

Enter

g mode

Choose text with regular visual mode, i.e. Shift + v

Type :. You'll get this prompt '<,'>.

Give a command. Type g/#/norm! ^x. (Optional: type g/#/norm! ^xx to include the space).

Enter

Results

    Practice in Vim
    Practice in Vim
    Practice in Vim
    Practice in Vim
    Practice in Vim
    Practice in Vim
    Practice in Vim

See Also

Post on removing indented comments

Post on how to quickly comment w/Vim

ThePrimeagen's tutorial on g commands.

VimTrick's tutorial on Commenting code


q
qba

I use EnhancedCommentify. It comments everything I needed (programming languages, scripts, config files). I use it with visual-mode bindings. Simply select text you want to comment and press co/cc/cd.

vmap co :call EnhancedCommentify('','guess')<CR>
vmap cc :call EnhancedCommentify('','comment')<CR>
vmap cd :call EnhancedCommentify('','decomment')<CR> 

j
jeremysprofile

This answer is most useful if you are unable to install plugins but you still want your comment characters to follow existing indentation levels.

This answer is here to 1) show the correct code to paste into a .vimrc to get vim 7.4+ to do block commenting/uncommenting while keeping indentation level with 1 shortcut in visual mode and 2) to explain it. Here is the code:

let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.[ch]    let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.cpp    let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.py    let b:commentChar='#'
autocmd BufNewFile,BufReadPost *.*sh    let b:commentChar='#'
function! Docomment ()
  "make comments on all the lines we've grabbed
  execute '''<,''>s/^\s*/&'.escape(b:commentChar, '\/').' /e'
endfunction
function! Uncomment ()
  "uncomment on all our lines
  execute '''<,''>s/\v(^\s*)'.escape(b:commentChar, '\/').'\v\s*/\1/e'
endfunction
function! Comment ()
  "does the first line begin with a comment?
  let l:line=getpos("'<")[1]
  "if there's a match
  if match(getline(l:line), '^\s*'.b:commentChar)>-1
    call Uncomment()
  else
    call Docomment()
  endif
endfunction
vnoremap <silent> <C-r> :<C-u>call Comment()<cr><cr>

How it works:

let b:commentChar='//' : This creates a variable in vim. the b here refers to the scope, which in this case is contained to the buffer, meaning the currently opened file. Your comment characters are strings and need to be wrapped in quotes, the quotes are not part of what will be substituted in when toggling comments.

autocmd BufNewFile,BufReadPost *... : Autocommands trigger on different things, in this case, these are triggering when a new file or the read file ends with a certain extension. Once triggered, the execute the following command, which allows us to change the commentChar depending on filetype. There are other ways to do this, but they are more confusing to novices (like me).

function! Docomment() : Functions are declared by starting with function and ending with endfunction. Functions must start with a capital. the ! ensures that this function overwrites any previous functions defined as Docomment() with this version of Docomment(). Without the !, I had errors, but that might be because I was defining new functions through the vim command line.

execute '''<,''>s/^\s*/&'.escape(b:commentChar, '\/').' /e' : Execute calls a command. In this case, we are executing substitute, which can take a range (by default this is the current line) such as % for the whole buffer or '<,'> for the highlighted section. ^\s* is regex to match the start of a line followed by any amount of whitespace, which is then appended to (due to &). The . here is used for string concatenation, since escape() can't be wrapped in quotes. escape() allows you to escape character in commentChar that matches the arguments (in this case, \ and /) by prepending them with a \. After this, we concatenate again with the end of our substitute string, which has the e flag. This flag lets us fail silently, meaning that if we do not find a match on a given line, we won't yell about it. As a whole, this line lets us put a comment character followed by a space just before the first text, meaning we keep our indentation level.

execute '''<,''>s/\v(^\s*)'.escape(b:commentChar, '\/').'\v\s*/\1/e' : This is similar to our last huge long command. Unique to this one, we have \v, which makes sure that we don't have to escape our (), and 1, which refers to the group we made with our (). Basically, we're matching a line that starts with any amount of whitespace and then our comment character followed by any amount of whitespace, and we are only keeping the first set of whitespace. Again, e lets us fail silently if we don't have a comment character on that line.

let l:line=getpos("'<")[1] : this sets a variable much like we did with our comment character, but l refers to the local scope (local to this function). getpos() gets the position of, in this case, the start of our highlighting, and the [1] means we only care about the line number, not other things like the column number.

if match(getline(l:line), '^\s*'.b:commentChar)>-1 : you know how if works. match() checks if the first thing contains the second thing, so we grab the line that we started our highlighting on, and check if it starts with whitespace followed by our comment character. match() returns the index where this is true, and -1 if no matches were found. Since if evaluates all nonzero numbers to be true, we have to compare our output to see if it's greater than -1. Comparison in vim returns 0 if false and 1 if true, which is what if wants to see to evaluate correctly.

vnoremap :call Comment() : vnoremap means map the following command in visual mode, but don't map it recursively (meaning don't change any other commands that might use in other ways). Basically, if you're a vim novice, always use noremap to make sure you don't break things. means "I don't want your words, just your actions" and tells it not to print anything to the command line. is the thing we're mapping, which is ctrl+r in this case (note that you can still use C-r normally for "redo" in normal mode with this mapping). C-u is kinda confusing, but basically it makes sure you don't lose track of your visual highlighting (according to this answer it makes your command start with '<,'> which is what we want). call here just tells vim to execute the function we named, and refers to hitting the enter button. We have to hit it once to actually call the function (otherwise we've just typed call function() on the command line, and we have to hit it again to get our substitutes to go through all the way (not really sure why, but whatever).

Anyway, hopefully this helps. This will take anything highlighted with v, V, or C-v, check if the first line is commented, if yes, try to uncomment all highlighted lines, and if not, add an extra layer of comment characters to each line. This is my desired behavior; I did not just want it to toggle whether each line in the block was commented or not, so it works perfectly for me after asking multiple questions on the subject.


C
Community
"comment (cc) and uncomment (cu) code 
noremap   <silent> cc      :s,^\(\s*\)[^# \t]\@=,\1# ,e<CR>:nohls<CR>zvj
noremap   <silent> cu      :s,^\(\s*\)# \s\@!,\1,e<CR>:nohls<CR>zvj

You can comment/uncomment single or multiple lines with #. To do multiple lines, select the lines then type cc/cu shortcut, or type a number then cc/cu, e.g. 7cc will comment 7 lines from the cursor.

I got the orignal code from the person on What's the most elegant way of commenting / uncommenting blocks of ruby code in Vim? and made some small changes (changed shortcut keys, and added a space after the #).


J
Jim Stewart

I use Tim Pope's vim-commentary plugin.


K
Kingsley Ijomah

You can use vim-commentary by tpope (https://github.com/tpope/vim-commentary) you can use it as following:

Enter visual mode by pressing

'v'

Then press

'j' repeatedly or e.g 4j to select 4 row

Now all you have to do with the selection is enter keys:

'gc'

This will comment out all the selection, to uncomment repead keys:

'gc'

P
Phil

This simple snippet is from my .vimrc:

function! CommentToggle()
    execute ':silent! s/\([^ ]\)/\/\/ \1/'
    execute ':silent! s/^\( *\)\/\/ \/\/ /\1/'
endfunction

map <F7> :call CommentToggle()<CR>

It's for //-Comments, but you can adapt it easily for other characters. You could use autocmd to set a leader as jqno suggested.

This is a very simple and efficient way working with ranges and visual mode naturally.


This is beautiful and works for multiple selected lines by V as well
n
nrz

I use vim-multiple-cursors for this.

To select the region, go to the first character of the first or last line of the region to be commented out by pressing 0 (it's zero, not letter "o"). Then press V and select the region using J, K or up and down arrow keys. Then put a virtual cursor on each line of the selection by pressing CtrlN. Then press I to simultaneously edit each line of the selection.


It doesn't work here, when I press Ctrl + N it selects the line bellow.
@Gerep Do you have vim-multiple-cursors installed? If yes, do you have the latest version of vim-multiple-cursors? Did you move to the first character of the first or the last line to be commented out/uncommented by pressing 0? Did you then select the lines to be commented out/uncommented using Visual mode (by pressing V or Ctrl+V to enter Visual mode, then J or K to select all the lines you want to comment out/uncomment) before you pressed Ctrl+N?
Oh!!! I'm really sorry, I thought vim-multiple-cursors was a built in functionality, it is working now, thank you =)
Just for me works with: <C-v>\d<cr><C-n>i#<cr>. Thank you for advice.
b
bgran

Here's a basic one-liner based on the C-v followed by I method outlined above.

This command (:Comment) adds a chosen string to the beginning of any selected lines.

command! -range -nargs=1 Comment :execute "'<,'>normal! <C-v>0I" . <f-args> . "<Esc><Esc>"

Add this line to your .vimrc to create a command that accepts a single argument and places the argument at the beginning of every line in the current selection.

E.g. if the following text is selected:

1
2

and you run this: :Comment //, the result will be:

//1
//2