Last active
January 9, 2024 09:54
-
-
Save AndrewRadev/d767364406d5b738bf69ebb06256f411 to your computer and use it in GitHub Desktop.
Substitute while skipping strings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
" 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