在Vim中,有什么快速的方法可以注释/取消注释行呢?

1542

我在vi中打开了一个Ruby代码文件,其中有用#注释掉的行:

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

假设我想取消注释第一个 def ... end 段落中的所有行,有没有在 Vim 中高效完成的方法?

总的来说,我正在寻找一种简单流畅的方法来注释和取消注释代码行。这里涉及到 Ruby 代码,但也可能是 JavaScript (//) 或 Haml (-#)。


50
应该将被接受的答案更改为其中一个详细说明如何在不使用插件的情况下添加/取消注释块的答案。当前被接受的答案基本上只是一个指向第三方插件的链接。 - faintsignal
最高评分的答案没有提到任何插件,@rationalis的评论是误导性的,请您纠正或删除它,谢谢。 - bonobo
接受的答案应该是Magnus的回答 - user3313834
一个理想的解决方案是,一个热键可以切换注释“关闭”和“打开”,无论是“光标所在行”还是“选择的多行”。其他任何操作都太麻烦了。 - Lonnie Best
55个回答

3245

对于大多数任务,我大部分时间都使用块选择

将光标放在第一个#字符上,按下CtrlV(或对于gVim,按下CtrlQ),然后向下滚动到最后一个被注释的行并按下x,这将垂直删除所有#字符。

要注释一段文本几乎相同:

  1. 首先,转到要注释的第一行,按下CtrlV。 这将使编辑器处于VISUAL BLOCK模式。
  2. 然后使用箭头键选择到最后一行
  3. 现在按下ShiftI,这将使编辑器进入INSERT模式,然后按下#。 这将在第一行添加一个哈希。
  4. 然后按下Esc(等待一秒钟),它将在所有其他选定的行中插入一个#字符。

对于默认情况下随debian / ubuntu一起提供的精简版vim,在第三步中键入: s/^/#即可(可以使用:nohl移除每行第一个字符的任何剩余高亮显示)。

这里有两个小的屏幕录像供参考。

注释: Comment

取消注释: Uncomment


104
默认情况下是CTRL+V。GVim 的 Windows 版本使用 Ctrl+Q,因为 Ctrl+V 已经被用于粘贴。 - R. Martinho Fernandes
39
你如何使用双斜杠'//'来实现这个? - AustinT
88
您可以按两次 Esc 键来避免等待那一秒钟 ;) - Ahmed Hegazy
90
这对我没有用。Shift-I 进入了简单插入模式。 - Geremia
20
如果“shif-i”对你无效:你应该按下“ESC”键,以查看其他行中粘贴的文本。 - Yar
显示剩余47条评论

1063

在 vim 中注释掉一段代码:

  • 按下 Esc (退出编辑或其他模式)
  • 按下 ctrl+v (可视化块模式)
  • 使用 / 方向键选择想要注释的行(不会高亮所有内容-没关系!)
  • Shift+i (大写 I)
  • 插入想要的文本,例如 %
  • 按下 EscEsc

在 vim 中取消注释:

  • 按下 Esc (退出编辑或其他模式)
  • 按下 ctrl+v (可视化块模式)
  • 使用 / 方向键选中要取消注释的行。
  • 如果您想选择多个字符,请使用以下方法之一或结合使用:

    • 使用左/右箭头键选择更多的文本
    • 要选择文本块,请使用 shift + / 方向键
    • 您可以反复使用以下删除键,就像使用常规删除按钮一样

  • 按下 dx 删除字符,必要时重复操作。

74
评论的快捷键对我不起作用。按 Shift + i 会进入插入模式。这是取决于vim版本吗? - user3527975
8
为什么需要一秒钟? - Conor Patrick
63
我对这个答案唯一的问题是它告诉你要使用箭头键 - cozyconemotel
7
请按两次 Esc 键。 :) - Aaron
67
起初启用评论对我没有起作用,但是重新阅读后就正常了:
  1. 确保您使用的是 Ctrl-V,而不是 V 进行选择
  2. 插入时,它将只显示为您正在修改单个行
  3. 当您按下 Esc 结束时,所有插入才会发生。
- timurb
显示剩余22条评论

441
有时我会被限制在一个远程框中,我的插件和.vimrc都无法帮助我,或者有时NerdCommenter会出错(例如HTML中嵌入了JavaScript)。 在这些情况下,一个低技术的替代方法是内置的"norm"命令,它只是在指定范围内的每一行运行任意的vim命令。例如: 使用"#"进行注释:
1. visually select the text rows (using V as usual)
2. :norm i#

这会在每一行的开头插入"#"。请注意,当您输入:时,范围将被填充,因此它实际上看起来像:''<,'>norm i#

取消注释 #:

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

这将删除每行的第一个字符。如果我使用了2个字符的注释,例如//,那么我只需使用:norm xx来删除两个字符。

如果像原帖中的问题一样缩进了注释,则可以这样锚定您的删除:

:norm ^x
这意味着“跳到第一个非空格字符,然后删除一个字符”。请注意,与块选择不同,即使注释的缩进不均匀,此技术也有效!

注意:由于 norm 实际上只是执行普通的vim命令,因此您不仅限于注释,您还可以对每行进行一些复杂的编辑。如果您需要将转义字符作为命令序列的一部分,请键入ctrl-v,然后按Esc键(或者更简单的方法是记录一个快速宏,然后使用norm在每行上执行该宏)。

注意2:当然,如果您经常使用 norm ,您也可以添加映射。例如,在~ / .vimrc中放置以下行让您在视觉选择后输入 ctrl-n 而不是:norm
vnoremap <C-n> :norm

注意3: 有时候基本版的vim可能没有编译进norm命令,所以请确保使用升级版,通常是/usr/bin/vim而不是/bin/vi。

(感谢@Manbroski和@rakslice提供的改进)


4
大多数其他答案建议使用ctrl-v技巧与仅限块选择的特殊命令结合使用,然而我个人认为我所描述的“规范”技巧更容易,因为它除了norm命令本身之外不会引入任何新的语法,所以我可以重复使用我已经掌握的vim知识。 - Magnus
3
要取消缩进块的注释,可以使用“:norm ^x”命令。这种方法通常适用于区域选择(例如,“vi{”将选择花括号内部)。这些“文本对象选择器”不能与“可视块”一起使用。 - ivan-k
1
啊,我刚刚弄明白了——在CentOS 6上,/bin/vi是Vim 7.2,但它与/usr/bin/vim不同,而且它已经关闭了这样的功能。 - rakslice
11
这绝对是最好的答案。特别是当与 vip 结合使用选择整个段落时。 - meh
3
太棒了!我是一个狂热的宏用户。我会实时创建宏(通常是递归的),来重复执行我需要做的按键操作。现在我脑子里都在想宏了。我以前从来不知道 :normal。我会大量使用它的。 - Bruno Bronosky
显示剩余6条评论

200

我使用NERD Commenter脚本。它可以让你轻松地在代码中注释、取消注释或切换注释。

正如在评论中提到的:

对于任何对用法感到困惑的人, 默认的leader键是"\",因此10\cc将会注释掉十行代码,10\cu将会取消注释这十行代码。


有趣!我读了文档,发现有一个“性感注释”——只需使用“\cs”。对于Ruby来说,它将使用=begin=end来注释多行,而不是使用井号。 - Hegwin
361
不要停在这里。下面是得票最高的答案,不需要使用任何插件: https://dev59.com/wHI-5IYBdhLWcg3wxrqQ#15588798 和 https://dev59.com/wHI-5IYBdhLWcg3wxrqQ#1676690 - kuttumiah
3
@whirmill,我认为“最好”取决于使用情况。如果我只需在一生中切换注释一次,则可视块模式更快。但是,如果我不介意安装插件,并希望尽可能少地键入按键来切换注释,并且不必区分添加或删除注释操作,则这里可能是“最佳答案”。 - Carolus
@Carolus 如果问题是以“什么是最好的方法”开始而不是“什么是快速的方法”,我会同意你的观点。 - whirmill
1
@whirmill 很好的观点。虽然我可以争辩“最快”的解释也可能有多种解释。不过你的解释更普遍/更有可能。 :) - Carolus
显示剩余3条评论

164

以下是我在.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>

现在你可以输入,cc来注释一行,,cu来取消注释一行(在正常模式和可视模式下均可使用)。

(这是我很多年前从某个网站上偷来的,所以我无法完全解释它的工作原理 :)。有一个评论解释了它的用法。)


我应该使用什么快捷键?我无法从vim代码本身确定! - gideon
9
我喜欢它 :)!谢谢!另外一件事,我觉得解释不太难。a)它重新映射一个命令(非递归见此),所以现在当你按下,cc时,:... 会被执行。b)现在这基本上是一个sed(s/what/towhat/where)命令,根据打开的文件类型将^(行首)更改为正确设置的注释字符。c)至于“silent thingies”,它们只是抑制命令的输出。d):nohlsearch停止了对sed搜索的高亮显示。 - ramrunner
14
请注意,这不是正确加载自动命令的方法。它们应该放在augroup内部,否则它们将被多次添加到vim中并导致很多减速。参见:http://learnvimscriptthehardway.stevelosh.com/chapters/14.html。我已经添加了我的回答到这个问题。 - user427390
2
我的模块默认为未列出的文件类型使用//,并使用<Leader>(默认为\)而不是“,”,并在任何缩进后添加/删除注释字符:https://gist.github.com/zonidjan/7fc11c2e9574c84383a87a8fcbe4b1ca - DimeCadmium
1
今天vim允许autocmd FileType * let b:comment_leader = (split(&commentstring, '%s')+['//'])[0]。在不同的文件类型下检查:set cms?。请注意,生成的字符串不包括尾随空格字符。此外,在没有+folding或旧版本中可能无法正常工作。不确定是否应该自行编辑答案,所以我将其留作评论。tpope/vim-commentary似乎也使用了'commentstring'。 (修复后重新发布) - user3125367
显示剩余9条评论

155

指定在 vim 中要注释的行:

显示行号:

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

或者这个:

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

13
由于您只需更改每行的第一个字符,因此您不需要在末尾使用“g”。 - Magnus
1
一些嵌入式盒子上的Vim(如OpenWrt)没有可视模式..所以这太棒了:) - kim0
25
为了取消这些行的注释,您可以写:5、17s/^# / - Anoroah
@Bin % 在搜索和替换字符串中表示全局搜索(而不是特定行)。 - Matthew Woo
这是我认为的正确答案,谢谢。 - peterretief
显示剩余6条评论

66

切换评论

如果您只需要切换评论,我建议使用commentary.vim,作者为tpope

在此输入图片描述

安装

Pathogen:

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

vim-plug:

Plug 'tpope/vim-commentary'

Vundle:

Plugin 'tpope/vim-commentary'

进一步个性化定制

请将以下内容添加到您的 .vimrc 文件中:noremap <leader>/ :Commentary<cr>

现在,您可以使用 Leader+/ 的方式像 Sublime 和 Atom 一样切换注释。


谢谢!未来会支持在HTML内部使用CSS注释吗? - Dan Oak
但有时您不想通过插件使服务器变得臃肿。 - Dzintars
对我来说,这是最好的解决方案。我已经安装了插件,但忘记实现按键绑定。现在运行得非常好! - jgnovak-dev
3
谢谢!这是我如何为普通模式和插入模式自定义行注释的方法:nnoremap <C-_> :Commentary<cr>jinoremap <C-_> <Esc>:Commentary<cr>ji。我不知道原因,但vim将/识别为_,所以我使用了C-_ - Gerard Bosch
这真的很有用。 - Sibaprasad Maiti

65

这是我的做法:

  1. 进入你要注释掉的第一行的第一个字符位置。

  2. 在GVIM中按下Ctrl+q或在VIM中按下Ctrl+v,然后向下选择要注释掉的行的第一个字符。

  3. 接着按下c,并添加注释字符。

取消注释的方法相同,只需输入一个空格即可。


52
c 命令也会删除第一个字符。CMS 的回答是正确的,即在 Windows Vim 上按下 I 键,然后输入注释字符,最后按下 Esc 键。 - Samaursa
9
除了在第三步需要按下 'c' 而不是 'r' 外,这个方法是有效的。 - David Baucum
或者您可以在按下 c 后连续按两次 ESC 来完成操作。 - nihiser
所有这些选项都会破坏行首的第一个字符。 - Josiah

47

我想到了一个简单的方法,可以在我的 .vimrc 文件中添加一些内容,这个方法非常有效,并且可以轻松地进行扩展。你只需要将新的文件类型添加到 comment_map 中,并设置它的注释标识符。

我已经为 normal 和 visual 模式添加了映射,但你可以将其重新映射到任何你喜欢的键位上。我更喜欢使用“切换”样式的函数,而不是多个映射等复杂的方式。

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>

注意:
我没有使用任何回调或挂钩到文件类型/加载,因为我发现它们会比.vimrc静态函数/映射慢下Vim的启动,但这只是我的个人偏好。我还试图保持简单和高效。如果您使用自动命令,您需要确保将它们放在自动命令组中,否则回调会被添加到每个文件类型多次加载,并导致大量性能降低。

1
我完全不懂vim,应该按哪个按钮来切换映射函数?底部的<leader><Space>声明是什么意思? - Jens Kohl
2
你可以将<leader>替换为一个像<,>这样的按键。然后,按下,SPACE,它将切换行的注释状态。Leader就是你设置的leader,Vim的默认leader是\,但你可以设置自己的,比如"let mapleader = ','"。 - user427390
很好的答案,不过有一个烦恼,对于已经有一些注释的代码块进行注释时,它会交换被注释和未被注释的行。例如,QtCreator只有在所有非空行都具有引导注释时才会删除注释,否则会添加一个引导注释。 - ideasman42
1
我使用\zs\ze正则表达式技巧制作了一个稍微不同的版本,代码变得更小了一点。你可以在这里看到它。 - SergioAraujo
这是我使用的方法。如果您在较旧版本的vim上运行它,可能需要删除代码行中的任何尾随空格才能使其正常工作。我使用<C-_>来匹配Control-/以匹配大多数其他行注释绑定键。 - Marthinwurer
使用各种编辑器已经有很长一段时间了,一直在寻找一个好的解决方案,因为我要转换到 NeoVim。这个完美!我还结合了这个解决方案和这个 Reddit 帖子:https://www.reddit.com/r/neovim/comments/zfimqo/is_it_possible_to_use_initvim_and_initlua_together/这样我就不必用 Lua 重新编写了。 - undefined

26
使用 Control-V 选择文本矩形:移动到第一个 # 字符,按下 Ctrl+V,然后向右移动一次,再向下移动,一直到注释的结尾。 现在输入 x:您正在删除所有后面跟着一个空格的 # 字符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接