我在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

假设我想取消注释第一个定义中的所有行。。。端部。在Vim中有什么有效的方法可以做到这一点?

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


当前回答

我使用NERD评论员脚本。它允许您在代码中轻松注释、取消注释或切换注释。

如评论中所述:

对于任何对用法感到困惑的人,默认的前导是“\”,所以10\cc将注释十行,10\cu将取消注释这十行

其他回答

我喜欢/*…*/(安西评论),所以这是我给你的诀窍。当然,您可以调整它以适应不同的情况。


注释/**/

选择文本(转到开始,开始视觉块,用}跳转):

<c-V>}

键入要在选择中应用的命令

:norm i/* <c-v><esc>$a */

命令将显示为::'<,'>norm i/*^[$a*/

详见(i*)。


取消注释/**/

选择文本(如前所述,或其他您喜欢的方式):

<c-V>}

键入要在选择中应用的命令

:norm :s-\s*/\*\s*-<c-v><enter>$bbld$

命令将如下::“<,”>norm:s-\s*/\*\s*-^M$bbld$

详见(ii*)。


后果

效果是逐行注释:

Comment block
Comment block
Comment block

变为(反之亦然):

/* Comment block */
/* Comment block */
/* Comment block */

最好将其保存为.vimrc中的一些map或@reg,因为它需要大量输入。如果您喜欢单个/*和*/而不是整个块,请使用:

用单个/**/整个块注释

将其保存在一个寄存器中,记录,例如,qc,然后在段落开头注释:

v}di/*  */<esc>hhhp

不要再忘记q,完成记录。

详见(iii*)。


从块中取消注释单个/**/

将其保存在寄存器中,例如@u。将光标放在块内的任意位置,然后:

?/\*<enter>xx/\*/<enter>xx

通过完成q命令保存寄存器。

详见(iv*)。


后果

效果是多行的单个注释:

Comment block
Comment block
Comment block

变为(反之亦然):

/* Comment block
Comment block
Comment block */

解释

(i*)它通过使用norm来工作,norm在每个选定的行中重复应用相同的命令。命令只需插入一个/*,找到该行的结尾,然后插入*/

:norm i/* <c-v><esc>$a */

(ii*)它还使用规范在每一行重复搜索/替换。搜索空格/*空格,然后不替换。之后,找到行的结尾,返回两个单词,右对齐一个字母,删除到末尾。

:norm :s-\s*/\*\s*-<c-v><enter>$bbld$

(iii*)通过v}选择段落,删除它,打开和关闭插入注释,移动到其中间并粘贴删除的块。

v}di/*  */<esc>hhhp

(iv*)中间的任何位置,向后查找a/*,删除它;查找转发的*/,将其删除。

?/\*<enter>xx/\*/<enter>xx

如果您无法安装插件,但仍希望注释字符遵循现有缩进级别,则此答案非常有用。

这里的答案是: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。有其他方法可以做到这一点,但对新手(像我)来说,它们更容易混淆。作用Docomment():函数以函数开头,以endfunction结尾。函数必须以大写开头。这个确保此函数使用此版本的Doccomment()覆盖以前定义为Doccomment)的任何函数。没有!,我有错误,但这可能是因为我通过vim命令行定义了新函数。执行“”<,“”>s/^\s*/&'.escape(b:commentChar,'\/')。“/e':Execute调用命令。在这种情况下,我们正在执行替换,它可以取一个范围(默认情况下,这是当前行),例如整个缓冲区的%或突出显示部分的“<,”>^\s*是正则表达式,以匹配一行的开头,后跟任意数量的空格,然后将其附加到(由于&)。这个这里用于字符串连接,因为escape()不能用引号括起来。escape()允许您转义commentChar中与参数(在本例中,\和/)匹配的字符,方法是在它们前面加上\。之后,我们再次用替换字符串的结尾连接,该字符串带有e标志。这个标志让我们无声地失败,这意味着如果我们在给定的行上找不到匹配项,我们就不会对它大喊大叫。作为一个整体,这一行允许我们在第一个文本之前放置一个注释字符,后跟一个空格,这意味我们保持缩进级别。执行“”<,“”>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()返回索引,如果为true,则返回-1。因为if计算所有非零数字为真,所以我们必须比较输出,看看它是否大于-1。vim中的比较如果为false,则返回0,如果为true,则返回1,这就是if希望看到的正确评估结果。vnoremap<silent><C-r>:<C-u>调用Comment()<cr><cr>:vnoremap意味着在视觉模式下映射以下命令,但不要递归映射(意味着不要更改可能以其他方式使用的任何其他命令)。基本上,如果你是一个vim新手,请始终使用noremap来确保你不会破坏东西<silent>的意思是“我不需要你的言语,只需要你的行动”,并且告诉它不要向命令行打印任何内容<C-r>是我们正在映射的对象,在这种情况下是ctrl+r(注意,在正常模式下,使用此映射,您仍然可以正常使用C-r进行“重做”)。C-u有点让人困惑,但基本上它可以确保你不会忘记视觉高亮显示(根据这个答案,它会让你的命令以“<,”>开头,这就是我们想要的)。这里的调用只是告诉vim执行我们命名的函数,<cr>是指点击enter按钮。我们必须点击它一次才能真正调用该函数(否则我们只是在命令行上键入了call function(),我们必须再次点击它以让替代品一直执行(不确定为什么,但无论如何)。

无论如何,希望这能有所帮助。这将接受用v、v或C-v突出显示的任何内容,检查第一行是否有注释,如果有,尝试取消注释所有突出显示的行,如果没有,则为每行添加一层注释字符。这是我想要的行为;我不只是想让它切换块中的每一行是否有评论,所以在问了多个关于这个主题的问题后,它非常适合我。

我的.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来取消注释一行(在正常模式和视觉模式下都有效)。

(多年前我从某个网站上偷了它,所以我无法完全解释它是如何工作的:)。有一条注释对其进行了解释。)

我将Phil和jqno的答案结合起来,并用空格进行了不加修饰的评论:

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>

工作原理:

假设我们使用#comments。

第一个命令s/\([^]\)/#\1/搜索第一个非空格字符[^],并将其替换为#+本身。自身替换由搜索模式中的\(..\)和替换模式中的\1完成。

第二个命令s/^\(*\)#\?#\/\1/搜索以双引号^\(*\)#\?#\?开头的行?(在注释之间接受0或1个空格),并简单地用非注释部分\(*\)替换这些空格(表示前面空格的数量相同)。

有关vim模式的更多详细信息,请查看。

从这里的答案中的想法开始,我开始了自己的评论功能。它可以打开和关闭评论。它可以处理//打印(“蓝色”)等事情//这个东西是蓝色的,只是切换第一条评论。此外,它在第一个非空白处添加注释和一个空格,而不是在行的开头。另外,在注释和缩进行时,它不会不必要地复制空白,而是使用缩放(:h\zs表示帮助)来避免这种额外的工作。希望它能帮助一些极简主义者。欢迎提出建议。

" these lines are needed for ToggleComment()
autocmd FileType c,cpp,java      let b:comment_leader = '//'
autocmd FileType arduino         let b:comment_leader = '//'
autocmd FileType sh,ruby,python  let b:comment_leader = '#'
autocmd FileType zsh             let b:comment_leader = '#'
autocmd FileType conf,fstab      let b:comment_leader = '#'
autocmd FileType matlab,tex      let b:comment_leader = '%'
autocmd FileType vim             let b:comment_leader = '"'

" l:pos   --> cursor position
" l:space --> how many spaces we will use b:comment_leader + ' '

function! ToggleComment()
    if exists('b:comment_leader')
        let l:pos = col('.')
        let l:space = ( &ft =~ '\v(c|cpp|java|arduino)' ? '3' : '2' )
        if getline('.') =~ '\v(\s*|\t*)' .b:comment_leader
            let l:space -= ( getline('.') =~ '\v.*\zs' . b:comment_leader . '(\s+|\t+)@!' ?  1 : 0 )
            execute 'silent s,\v^(\s*|\t*)\zs' .b:comment_leader.'[ ]?,,g'
            let l:pos -= l:space
        else
            exec 'normal! 0i' .b:comment_leader .' '
            let l:pos += l:space
        endif
        call cursor(line("."), l:pos)
    else
        echo 'no comment leader found for filetype'
    end
endfunction

nnoremap <Leader>t :call ToggleComment()<CR>
inoremap <Leader>t <C-o>:call ToggleComment()<CR>
xnoremap <Leader>t :'<,'>call ToggleComment()<CR>