在C/C++代码行中使用Vim搜索

24

是否有一种方法可以在C / C ++源文件中搜索字符串,同时跳过注释的行?

4个回答

30

这是一个有趣的问题。

我认为@sixtyfootersdude的想法是正确的——让Vim的语法高亮告诉你什么是注释,什么不是注释,然后在非注释中搜索匹配项。

让我们从一个函数开始,该函数模仿Vim内置的search()例程,但还提供了一个“skip”参数来忽略一些匹配项:

function! SearchWithSkip(pattern, flags, stopline, timeout, skip)
"
" Returns true if a match is found for {pattern}, but ignores matches
" where {skip} evaluates to false. This allows you to do nifty things
" like, say, only matching outside comments, only on odd-numbered lines,
" or whatever else you like.
"
" Mimics the built-in search() function, but adds a {skip} expression
" like that available in searchpair() and searchpairpos().
" (See the Vim help on search() for details of the other parameters.)
" 
    " Note the current position, so that if there are no unskipped
    " matches, the cursor can be restored to this location.
    "
    let l:matchpos = getpos('.')

    " Loop as long as {pattern} continues to be found.
    "
    while search(a:pattern, a:flags, a:stopline, a:timeout) > 0

        " If {skip} is true, ignore this match and continue searching.
        "
        if eval(a:skip)
            continue
        endif

        " If we get here, {pattern} was found and {skip} is false,
        " so this is a match we don't want to ignore. Update the
        " match position and stop searching.
        " 
        let l:matchpos = getpos('.')
        break

    endwhile

    " Jump to the position of the unskipped match, or to the original
    " position if there wasn't one.
    "
    call setpos('.', l:matchpos)

endfunction

以下是构建在 SearchWithSkip() 基础上实现语法敏感搜索的一些功能:
function! SearchOutside(synName, pattern)
"
" Searches for the specified pattern, but skips matches that
" exist within the specified syntax region.
"
    call SearchWithSkip(a:pattern, '', '', '',
        \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "' . a:synName . '"' )

endfunction


function! SearchInside(synName, pattern)
"
" Searches for the specified pattern, but skips matches that don't
" exist within the specified syntax region.
"
    call SearchWithSkip(a:pattern, '', '', '',
        \ 'synIDattr(synID(line("."), col("."), 0), "name") !~? "' . a:synName . '"' )

endfunction

以下是使语法敏感搜索功能更易于使用的命令:
command! -nargs=+ -complete=command SearchOutside call SearchOutside(<f-args>)
command! -nargs=+ -complete=command SearchInside  call SearchInside(<f-args>)

经过漫长的探索,我们现在可以做出这样的事情:

:SearchInside String hello

这会在Vim认为是字符串的文本中搜索hello

最后,这会在除注释外的所有地方搜索double

:SearchOutside Comment double

为了重复搜索,请使用@:宏来重复执行相同的命令,就像按下n键重复搜索一样。
(顺便说一句,谢谢你提出这个问题。现在我已经建立了这些例程,我期望会经常使用它们。)

感谢您的非常好的回答。我也期待着经常使用它们 :-) - Nicola Bonelli
7
太好了!然而有一个小问题。如果你的搜索字符串在文件中被找到,但只出现在被跳过的地方,那么就会导致无限循环。你可以通过设置一个保护条件来解决这个问题。在循环之前添加代码:let l:guard = []。然后在循环内部添加代码:if l:guard == [] | let l:guard = getpos('.') | elseif l:guard == getpos('.') | break | endif - Kurt Hutchinson
2
这真的很棒。请注意,您可以使用“SearchOutside Comment\\|String double”来排除多个SynNames,是的,您需要四个反斜杠来转义管道符。 - Drasill
@KurtHutchinson 需要注意的是,当使用“c”标志进行搜索时,该解决方案无法正常工作,该标志表示接受在光标位置找到的匹配项。在这种情况下,它将在找到第一个匹配项后停止搜索。 - JelteF
解决方案是这样的:let l:flags = a:flags,将搜索调用更改为 l:flags,并在 if l:firstmatch == [] 中添加一行代码,如下所示:let l:flags = substitute(l:flags, 'c', '', '') - JelteF

7

这个模式搜索不以两种C++注释约定为前缀的字符串。我还排除了 '*' 作为第一个非空白字符,因为这是多行注释的常见约定。

/\(\(\/\*\|\/\/\|^\s*\*[^/]\).*\)\@<!foo 

只有第一个和第四个foo会被匹配。

foo
/* foo
* baz foo
*/ foo
// bar baz foo

在模式的开头加上 \v 可以消除许多反斜杠:

/\v((\/\*|\/\/|^\s*\*[^/]).*)@<!foo

你可以在你的 .vimrc 文件中添加以下内容,将快捷键绑定到该模式:

"ctrl+s to search uncommented code
noremap <c-s> <c-o>/\v((\/\*\|\/\/\|^\s*\*[^/]).*)@<!

无法工作,带有一些注释 /* */ 在行首没有星号的注释。 - Mathieu Westphal

1

不确定这是否有帮助,但当您键入:syn时,它具有文件类型中使用的所有格式。也许您可以从中参考一些内容。您可以说类似于:

map n betterN

function betterN{
  n keystroke
  while currentLine matches comment class
    do another n keystroke
}

0

这是我的处理方法:

  1. 删除所有C/C++注释(使用replace命令%s
  2. 使用regular search命令/进行搜索
  3. 使用m a设置标记位置(设置标记“a”)
  4. 使用u撤销注释的删除
  5. 使用``a`跳转到标记“a”
  6. 最后使用delm a删除标记(如果不删除,它会被覆盖,所以没关系)

当然,你也可以将其作为一个大型操作/函数来完成。我并不精通Vim脚本,无法给出示例。

我承认我的解决方案有点“懒惰”(而且你可能可以做得更好),但这就是我能想到的。

希望对你有所帮助。


希望有更合适的方法,但总比没有好... :-) - Nicola Bonelli
如果你问我,这相当危险。 - Ken Bloom
1
@Ken Bloom:没有危险的生活肯定很无聊,不是吗?! ;) - ereOn

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