5

As a poor-mans refactoring, I'd like to do a search and replace, but omit strings that are in double quotes (C/C++ quoting rules). For example

cout << "Some variable " << variable << endl;

replacing variable -> m_variable

cout << "Some variable " << m_variable << endl;

So I'd like the equivalent of

:%s/variable/m_variable/g

but ignoring text in quotes. Is there a way to do this in vim?

jdm
  • 151
  • 1

1 Answers1

5

It can be done by testing the syntax name behind the variable to replace:

:%s/variable/\=(match(map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")'), '\cstring')==-1 ? 'NEw' : submatch(0))/g

But nobody will want to use that. Instead, we'd better define a new command.

:command! -bang -nargs=1 -range SubstituteUnlesString 
    \ <line1>,<line2>call s:SubstituteUnlessString("<bang>", <f-args>)

" Function: s:SubstituteUnlessString(bang, repl_arg) {{{3
function! s:SubstituteUnlessString(bang, repl_arg) range abort
  let do_loop = a:bang != "!"
  let sep = a:repl_arg[0]
  let fields = split(a:repl_arg, sep)
  let cleansed_fields = map(copy(fields), 'substitute(v:val, "\\\\[<>]", "", "g")')
  " build the action to execute
  if fields[1] =~ '^\\='
    let replacement = matchstr(fields[1], '^\\=\zs.*')
  elseif fields[1] =~ '&\|\\\d'
    let replacement = "'".substitute(fields[1], '&\|\\\(\d\)', '\=string(".submatch(".(submatch(0)=="&"?"0":submatch(1)).").")', 'g') ."'"
  else
    let replacement = string(fields[1])
  endif
  let action = '\=(match(map(synstack(line("."), col(".")), "synIDattr(v:val, \"name\")"), "\\cstring")==-1 ? '.replacement.' : submatch(0))'
  let cmd = a:firstline . ',' . a:lastline . 's'
    \. sep . fields[0]
    \. sep . action
        \. sep.(len(fields)>=3 ? fields[2] : '')
  " echomsg cmd
  exe cmd
endfunction

That you would be able to use this way:

:%SubstituteUnlessString/variable/m_variable/g
" or
:1,3SubstituteUnlessString#variable#m_&#g

Yeah. It's a bit overkill. The c flag from :substitute is more than enough.

Luc Hermitte
  • 17,351
  • 1
  • 33
  • 49