Skip to content

Instantly share code, notes, and snippets.

@AndrewRadev
Last active January 9, 2024 09:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AndrewRadev/d767364406d5b738bf69ebb06256f411 to your computer and use it in GitHub Desktop.
Save AndrewRadev/d767364406d5b738bf69ebb06256f411 to your computer and use it in GitHub Desktop.
Substitute while skipping strings
" Install by saving as `~/.vim/plugin/substitute_code.vim`
" Use like the regular `:s` command:
"
" %Scode/foo/bar/g
"
" It ignores comments, but not strings. Tweak implementation as desired.
command! -nargs=1 -range Scode call s:Scode(<q-args>, <line1>, <line2>)
" Input should be something like /<pattern>/<replacement>/<flags>
function! s:Scode(input, line1, line2) abort
if len(a:input) == 0
return
endif
" Extract the pattern so we can search for it ourselves
let delimiter = a:input[0]
let pattern = matchstr(a:input, '^' .. delimiter .. '\zs.\{-}[^\\]\ze' .. delimiter)
" We prepare a skip pattern that will skip anything that is NOT a comment.
" We'll find the coordinates and ignore them in the real substitution:
let skip_pattern = '\%(Comment\)'
let skip_expression = "synIDattr(synID(line('.'),col('.'),1),'name') !~ '".skip_pattern."'"
let saved_view = winsaveview()
try
let ignored_positions = []
" start at the last char in the file and wrap for the
" first search to find match at start of file
normal G$
let flags = "w"
" default values:
let stopline = 0
let timeout = 0
while search(pattern, flags, stopline, timeout, skip_expression) > 0
call add(ignored_positions, [line('.'), col('.')])
let flags = "W"
endwhile
" compose a pattern that will avoid matching at string/comment positions:
let ignore_pattern = join(map(ignored_positions, { _, pos -> '\%(\%'..pos[0]..'l\%'..pos[1]..'c\)\@!' }), '')
" insert pattern after initial delimiter:
let range = a:line1 .. ',' .. a:line2
let substitute_command =
\ range .. 's' ..
\ delimiter ..
\ escape(ignore_pattern, delimiter) ..
\ strpart(a:input, 1)
exe substitute_command
finally
call winrestview(saved_view)
endtry
endfunction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment