我在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(-#)。


当前回答

你可以使用tpope的vim评论(https://github.com/tpope/vim-commentary)您可以按如下方式使用它:

按进入视觉模式

'v'

然后按

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

现在,您所要做的选择就是输入键:

'gc'

这将注释掉所有选择,以取消注释repead键:

'gc'

其他回答

注释一行(适用于所有语言):

noremap<silent>,//:调用CommentLine()<CR>

我们可以用许多行来调用它,在视觉模式下也可以。Like:要注释四行,请使用4,//,要取消注释,请使用5,/。

要取消注释A行(适用于所有语言):

noremap<silent>,/:调用UnCommentLine()<CR>

若要添加新符号[comment],请添加一个列表并在函数中添加一些行。如果您想添加一种语言,该语言的注释符号已经在其中一个列表中定义,只需在相应的列表中添加您的语言名称即可(要获得正确的名称:在vim中打开文件并使用:set ft获取您的语言的正确名称)。

CommentLine()的定义

作用注释行()让slash_ft_list=['c','cpp','java','scala','systemverilog','verilog'、'verilog_systemverilog']让hash_ft_list=[sh','ruby','python','csh','sconf','fstab','perl']让perct_ft_list=['tex']让mail_ft_list=['mail']让quote_ft_list=['vim']如果(索引(slash_ft_list,&ft)!=-1):规范I//elseif(索引(hash_ft_list,&ft)!=-1):规范I#elseif(索引(perct_ft_list,&ft)!=-1):标准I%elseif(索引(mail_ft_list,&ft)!=-1):规范I>elseif(索引(quote_ft_list,&ft)!=-1):规范I“结束符端函数

UnCommentLine()的定义

作用取消注释行()让slash_ft_list=['c','cpp','java','scala','systemverilog','verilog'、'verilog_systemverilog']让hash_ft_list=[sh','ruby','python','csh','sconf','fstab','perl']让perct_ft_list=['tex']让mail_ft_list=['mail']让quote_ft_list=['vim']如果(索引(slash_ft_list,&ft)!=-1):标准^2xelseif(索引(hash_ft_list,&ft)!=-1):标准^xelseif(索引(perct_ft_list,&ft)!=-1):标准^xelseif(索引(mail_ft_list,&ft)!=-1):标准^xelseif(索引(quote_ft_list,&ft)!=-1):标准^x结束符端函数

这个简单的片段来自my.vimrc:

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

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

它适用于//-注释,但您可以很容易地将其改编为其他角色。您可以按照jqno的建议使用autocmd设置引线。

这是一种非常简单和有效的方式,自然地处理范围和视觉模式。

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

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

我标记第一行和最后一行(ma和mb),然后做:‘a,‘bs/^#//

首先,我要感谢@mike的回答,因为我使用的是它的修改版本。我想发布我的版本,以防有人感兴趣。我的主要功能区别在于,它将始终在射程的每一行上执行相同的动作。如果所选范围包含任何未注释的行,则每一行都会添加注释引线。这样,如果在未注释的代码块中有人类可读的注释,则它们不会变为未注释。然后,当您取消注释块时,人类可读的文本将保持注释状态,因为它有两个注释引线。完成后,它还会恢复光标位置。

ToggleComment函数:

function! ToggleComment() range
    "Ensure we know the comment leader.
    if !exists('b:comment_leader')
        echo "Unknown comment leader."
        return
    endif
    "Save the initial cursor position, to restore later.
    let l:inipos = getpos('.')
    "Make a list of all of the line numbers in the range which are already commented.
    let l:commented_lines = []
    for i in range(a:firstline, a:lastline)
        if getline(i) =~ '^\s*' . b:comment_leader
            let l:commented_lines = add(l:commented_lines, i)
        endif
    endfor
    "If every line in the range is commented, set the action to uncomment.
    "  Otherwise, set it to comment.
    let l:i1 = index(l:commented_lines, a:firstline)
    let l:i2 = index(l:commented_lines, a:lastline)
    if l:i1 >= 0 && l:i2 >= 0 && (l:i2 - l:i1) == (a:lastline - a:firstline)
        let l:action = "uncomment"
    else
        let l:action = "comment"
    endif
    "Loop through the range, commenting or uncommenting based on l:action.
    for i in range(a:firstline, a:lastline)
        "Move to line i.
        exec "exe " . i
        "Perform the action.
        if l:action == "comment"
            exec 'normal! 0i' . b:comment_leader
        else
            execute 'silent s,' . b:comment_leader . ',,'
        endif
    endfor
    "Restore the initial position.
    call setpos('.', l:inipos)
endfunction

键映射(注意,我将注释键更改为“k”):

noremap <Leader>k :call ToggleComment()<CR>

最后,我将autocmds放在下面的“if”语句和augroup中,但它们保持不变。它们大多只是供参考。

if has("autocmd")
    augroup autocmds
        autocmd!
        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 = '"'
    augroup END
endif

编辑:我从原始答案更新了ToggleComment函数。最重要的是,我修改了正则表达式。在许多情况下,如果字符串中有另一个注释引线实例,它将无法正常工作。我相信它可能必须在前面或后面加一个空格,但我记不起来了。不管怎样,下面是Python的错误示例。

print("Hello, World!") # Says hello to the world.

我已经解决了这个问题,并稍微简化了正则表达式。一个副作用是它不再在评论标题后面添加空格,但这并不困扰我。我在一些变量中添加了一个局部声明,我在最初的答案中忘记了这些变量。