如何注释掉在可视模式下选择的一组行?


35

如何在可视模式下选择多行注释掉?如何使它特定于语言?

例如,如果选择了前四行:

def foo(a,b):
    for each in (a,b):
        print each
    return a+b
print "2"

命令/宏的操作应导致以下结果(在python中):

#def foo(a,b):
#    for each in (a,b):
#        print each
#    return a+b
print "2"

Answers:


31

如果要特定语言的注释,则需要nerdcommenter之类的插件。

另外,尽管它不能回答您的实际问题,但是您可以使用内置的vim动作以及您对每种语言的注释字符的了解...

选项1:V块

  1. :1 Enter (转到第1行)
  2. Ctrl-V(垂直​​块模式)
  3. jjj (再下3行)
  4. Shift-I(在块之前进入插入模式)
  5. # (插入“#”)
  6. Esc (返回普通模式)

选项2:替代

:1,4s/^/#/

分解:

  1. : 遵循Ex命令
  2. 1,4 从1到4
  3. s 替代
  4. /替换命令的分隔符。
    (您也可以使用其他字符,例如:
  5. ^ 行的开头
  6. / 分隔器
  7. # python的注释字符
  8. / 最后的分隔符

选项#3:重复应用宏(

  1. :1 Enter (转到第1行)
  2. qa(开始在寄存器上记录a
  3. Shift-I(在行的开头进入插入模式
  4. # (在行首添加“#”)
  5. Esc (返回普通模式)
  6. q (停止录制)

  7. :2,4 normal @a(重新运行记录的宏以a在2到4之间的行上注册)

    要么

    您可以在可视化模式下选择行,然后单击:以自动填充Ex行:'<,'>(从可视化选择的开始到结束的范围),然后键入normal @a并单击Enter)。

现在,每当您要注释一些行时,只需重新运行记录的宏即可a在这些行上注册:

:9,22 normal @a (comment out lines 9-22)

1
方案4:插件
科迪投票

我不明白为什么您可以:9,22 normal I#按我的回答来对单个命令使用宏。
2015年

当可以使用gg时,为什么还要使用:1 <enter>?
Tanath

@Tanath从第一行开始注释是针对此示例的。如果作者想在第9至22行中发表评论,他们将无法使用gg。
bsmith89

@Ben normal在编写此答案之前,我对命令一无所知。你是对的; :9,22 normal I#也会工作。
bsmith89

26

使用可视块模式(CtrlV),选择各行的开头。然后按I#(这是一个大写字母I)在每个行上插入哈希字符。然后按Esc从插入模式返回到普通模式。


它对我不起作用。它仅在第一行中插入注释。
gon1332 2015年

你在推ctrl吗?因为ctrl+v与正义是不同的v
Cody Poll

@CodyPoll我知道。一切都好I。当我按时I#将仅放在第一行的前面。
gon1332 2015年

@CodyPoll好吧..我只是住院。Esc进行完上述步骤后,我没有按。
gon1332 2015年

据我所知@ gon1332,您需要按Esc末尾。
贡萨洛·里贝罗

18

如果您只需要快速解决当前所用语言的问题,并且已经在可视模式下选择了文本,则

:norm 0i#

做这份工作。(对于每一行,在正常模式下,请转到第一列并进行插入#。using :norm I#会将其插入到第一个非空白字符之前,该字符可能不是您想要的。)using :norm i#也将起作用,因为它:norm始于行,但是如果您不知道的话,它就不太明确也不太清楚。

当然,如果您打算经常执行此操作,则需要设置映射或寻找插件。


1
不需要为0,因为默认情况下,normal命令在行首使用光标执行。
nitishch

1
当然,可以在此命令前加上行号,%,标记。示例::1,5norm i#(或):'a,'bnorm i#(或):10%norm i#
SibiCoder

9

自动执行此操作将需要您在vimrc文件()中添加以下内容:

au FileType haskell,vhdl,ada let b:comment_leader = '-- '
au FileType vim let b:comment_leader = '" '
au FileType c,cpp,java let b:comment_leader = '// '
au FileType sh,make let b:comment_leader = '# '
au FileType tex let b:comment_leader = '% '
noremap <silent> ,c :<C-B>sil <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:noh<CR>
noremap <silent> ,u :<C-B>sil <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:noh<CR>

,c评论的区域,并,u取消注释的区域。这将手动设置不同语言的注释符号。

第二种选择是使用插件,例如tcommentvim-commentarycomments.vim。我自己用tcomment。请阅读他们页面上的使用和安装说明,因为我相信这超出了问题的主题。

我建议您使用一个插件(上面链接的一个或另一个),因为它比在vimrc文件中维护一段代码要容易得多。

编辑:随着问题的更改,我删除了手动方式,并且200_success回答了正确的方式。



注意:这仅支持逐行注释。例如,ANSI C无法识别//(仅/* */)。
wchargin

虽然我喜欢这种方法,但是有没有办法使它切换评论?
ideaman42

1
@ ideasman42您必须改用一个函数,并检查当前行是否以注释开头,然后根据该调用:s选择答案摘录中显示的任何命令。支票本身就像getline('.') =~ "^" . escape(b:comment_leader, '\/')。如果确实没有评论,请发表评论。这未经测试,仅应作为示例。
tokoyami


5

选择线后,只需键入

:norm I#

:会自动显示'<,'>在您的命令行中,该命令行是从选择开始到结束的范围;norm执行普通模式命令,并在该范围内起作用;I#是正常模式命令,该命令在行的开头插入“#”。


4

为此,我是TComment的忠实粉丝;我不仅可以执行文件类型特定的注释样式,甚至可以为支持块注释的语言指定逐行或逐行。

    gc{motion}   :: Toggle comments (for small comments within one line 
                    the &filetype_inline style will be used, if 
                    defined)
    gcc          :: Toggle comment for the current line

Explicit commenting/uncommenting:

    g<{motion}   :: Uncomment region
    g<c          :: Uncomment the current line
    g<b          :: Uncomment the current region as block

    g>{motion}   :: Comment region
    g>c          :: Comment the current line
    g>b          :: Comment the current region as block

In visual mode:

    gc           :: Toggle comments
    gC           :: Comment selected text

感谢您的回答!您可以扩大它吗?提供插件答案很好,但是现在,它只是插件的链接。至少应该对答案进行基本的描述,以及如何使用它。另请参阅此meta post
马丁·图尔诺伊

复制/粘贴键绑定似乎很愚蠢,但是您就可以了。我已经描述了它的作用。
Collin Grady

3

我发现vim注释插件是迄今为止最简单的方法。选择一系列线,然后点击gc。它将为您打开的文件类型使用适当的注释字符。甚至可能没有任何视觉选择就可以用gcu或取消注释相邻的注释行 gcgc


2

假设您想在行首添加前缀到5行,则可以使用Search和replace

:.,+5s/^/prefix_/g

或行尾:

:.,+5s/$/suffix_/g

或使用可视模式(Ctrl+ v)选择垂直文本块,然后进入插入模式(I)并键入一些内容,然后按Esc确认并将更改应用到其他行。

有关:


2

这个答案是这里1)显示正确的代码粘贴到.vimrc获得vim 7.4+以进行块注释/取消注释,同时在可视模式下以1快捷方式保持缩进级别,并2)对其进行解释。

这是代码:

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>

怎么运行的:

  • let b:commentChar='//':这将在vim中创建一个变量。在b这里指的是范围,在这种情况下被包含到缓冲区,这意味着当前打开的文件。您的注释字符是字符串,需要用引号引起来,引号不是切换注释时要替换的内容的一部分。

  • autocmd BufNewFile,BufReadPost *...:自动命令会在其他情况下触发,在这种情况下,这些命令是在新文件或读取文件以某个扩展名结尾时触发的。触发后,执行以下命令,这使我们可以更改commentChar取决于文件的类型。还有其他方法可以做到,但是对于新手(如我)来说,它们更加令人困惑。

  • function! Docomment():函数以开头function和结尾声明endfunction。功能必须以大写字母开头。所述!,该函数将覆盖定义为任何先前的功能,确保Docomment()与此版本的Docomment()。没有!,我会遇到错误,但这可能是因为我正在通过vim命令行定义新函数。

  • execute '''<,''>s/^\s*/&'.escape(b:commentChar, '\/').' /e':执行调用命令。在这种情况下,我们正在执行substitute,它可以采用一个范围(默认为当前行),例如%整个缓冲区或'<,'>突出显示的部分。^\s*是正则表达式,以匹配行的开头,后跟任意数量的空格,然后将其附加到(由于&)。在.这里用于字符串连接,因为escape()不能用引号包裹。escape()允许您commentChar在参数前面加上来转义与参数(在本例中为\/)匹配的字符\。此后,我们再次连接substitute字符串的末尾,该字符串的末尾e旗。该标志使我们能够静默失败,这意味着,如果在给定的行上找不到匹配项,我们将不会大喊大叫。总体而言,此行使我们可以在第一个文本之前放置一个注释字符,后跟一个空格,这意味着我们保持缩进级别。

  • execute '''<,''>s/\v(^\s*)'.escape(b:commentChar, '\/').'\v\s*/\1/e':这类似于我们的最后一个长指令。对于我们来说\v,这是唯一的,它可以确保我们不必逃脱我们的(),并且1指的是我们与我们组成的小组()。基本上,我们匹配的行以任意数量的空格开头,然后匹配注释字符,其后跟随任意数量的空格,并且我们仅保留第一组空白。同样,e如果我们在该行上没有注释字符,请让我们静默失败。

  • let l:line=getpos("'<")[1]:这会像设置注释字符一样设置变量,但是l引用的是本地范围(此函数的本地范围)。getpos()在这种情况下,它获取突出显示的开始位置,并且[1]我们只关心行号,而不关心列号之类的其他方式。

  • if match(getline(l:line), '^\s*'.b:commentChar)>-1:您知道如何if运作。match()检查第一件事是否包含第二件事,因此我们抓住开始突出显示的行,并检查它是否以空格开头,后跟注释字符。如果没有找到匹配项match()-1则返回true的索引。由于if评估所有非零数字为真,因此我们必须比较输出以查看其是否大于-1。vim如果为false,则in的比较返回0,如果为true,则返回1,这是if希望正确评估的结果。

  • vnoremap <silent> <C-r> :<C-u>call Comment()<cr><cr>vnoremap表示在可视模式下映射以下命令,但不要以递归方式映射(表示不要更改可能以其他方式使用的任何其他命令)。基本上,如果您是vim新手,请务必使用noremap以确保您不会破坏事物。<silent>意思是“我不想你的话,只是你的行动”,并告诉它不要在命令行上打印任何内容。<C-r>是我们要映射的东西,在这种情况下为ctrl + r(请注意,通过此映射,您仍可以在正常模式下将Cr正常用于“重做”)。C-u有点令人困惑,但是基本上可以确保您不会丢失视觉突出显示(根据此处的答案它使您的命令从我们想要的开始)。,它只是告诉vim执行我们命名的功能,并指点击'<,'>call<cr>enter按钮。我们必须打一下它才能实际调用该函数(否则,我们只需call function()在命令行上键入,然后我们必须再次打它才能使我们的替代项一直执行(不确定原因,但无论如何)。

无论如何,希望这会有所帮助。这将使用vV或突出显示的内容C-v,检查第一行是否已注释,如果是,请尝试取消注释所有突出显示的行,如果没有注释,请在每行中添加额外一层注释字符。这是我想要的行为;我不仅希望它能切换块中的每一行是否都被注释,因此在对这个问题问了 多个问题之后,它对我来说是完美的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.