tags:

views:

216

answers:

5

Hello.

Is there any way to search a string in a C/C++ source file while skipping commented lines?

A: 

http://www.linux.com/learn/tutorials/8255-vim-tips-the-basics-of-search-and-replace

lajoo
I'm pretty sure Nicola knows how to do a basic search. What he is asking is doing a search **avoiding** C/C++ style comments. You don't answer his question at all.
ereOn
Precisely. Ignoring commented lines seems to be a tricky requirement to meet.
Nicola Bonelli
@Nicola Bonelli: tricky and **interesting**. That's why I upvoted your question and I'm looking into it right now ;) (Might be usefull to me too)
ereOn
A: 

Here is how I would proceed:

  1. Delete all C/C++ comments (using the replace command %s)
  2. Proceed to the search using regular search command /
  3. Set a mark at the position using m a (to set the mark "a")
  4. Undo the deletion of the comments using u
  5. Jump to the mark "a" using `a
  6. Eventually deleting the mark using delm a (it would be overwritten in the case you don't delete it, so no big deal)

Of course you can do that in one big operation/function. I do not master Vim scripting good enough to give an example of that though.

I admit my solution is a bit "lazy" (and you can probably do it way better) but that's all I came to.

Hope it helps.

ereOn
I hope there's a more suitable way, but better that nothing... :-)
Nicola Bonelli
Pretty dangerous if you ask me.
Ken Bloom
@Ken Bloom: A life without danger must be pretty boring no ?! ;)
ereOn
+3  A: 

This pattern searches for a string that is not preceded by the two C++ commenting conventions. I've also excluded '*' as the first non-whitespace character, as that's a common convention for multi-line comments.

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

Only the first and fourth foo are matched.

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

Putting \v at the beginning of the pattern eliminates a bunch of backslashes:

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

You can bind a hotkey to this pattern by putting this in your .vimrc

"ctrl+s to search uncommented code
noremap <c-s> <c-o>/\v((\/\*\|\/\/\|^\s*\*[^/]).*)@<!
bukzor
Clever, nice and simple.
ereOn
It even works with `hlsearch`
Ken Bloom
+1  A: 

Not sure if this is helpful but when you type :syn it has all the formatting that is used in your file type. Maybe you can refer to that somehow. You could say something like:

map n betterN

function betterN{
  n keystroke
  while currentLine matches comment class
    do another n keystroke
}
sixtyfootersdude
+5  A: 

This is an intriguing question.

I think @sixtyfootersdude has the right idea -- let Vim's syntax highlighting tell you what's a comment and what's not, and then search for matches within the non-comments.

Let's start with a function that mimics Vim's built-in search() routine, but also provides a "skip" parameter to let it ignore some matches:

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

Here are a couple of functions that build on SearchWithSkip() to implement syntax-sensitive searches:

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

Here are commands that make the syntax-sensitive search functions easier to use:

command! -nargs=+ -complete=command SearchOutside call SearchOutside(<f-args>)
command! -nargs=+ -complete=command SearchInside  call SearchInside(<f-args>)

That was a long way to go, but now we can do stuff like this:

:SearchInside String hello

That searches for hello, but only within text that Vim considers a string.

And (finally!) this searches for double everywhere except comments:

:SearchOutside Comment double

To repeat a search, use the @: macro to execute the same command repeatedly, like pressing n to repeat a search.

(Thanks for asking this question, by the way. Now that I've built these routines, I expect to use them a lot.)

Bill Odom
Thank your for your *great* answer. I expect to use them a lot as well :-)
Nicola Bonelli