" File: greplace.vim " Script to search and replace pattern across multiple files " Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) " Version: 1.0 Beta1 " Last Modified: March 3, 2007 " if exists("loaded_greplace") finish endif let loaded_greplace = 1 " Requires Vim 7.0 and above if v:version < 700 finish endif " Line continuation used here let s:cpo_save = &cpo set cpo&vim if &isfname =~ '[' let s:gRepl_bufname = '[Global\ Replace]' else let s:gRepl_bufname = '\[Global\ Replace\]' endif " Character to use to quote patterns if !exists("Greplace_Shell_Quote_Char") if has("win32") || has("win16") || has("win95") let Greplace_Shell_Quote_Char = '' else let Greplace_Shell_Quote_Char = "'" endif endif let s:save_qf_list = {} function! s:warn_msg(msg) echohl WarningMsg echomsg a:msg echohl None endfunction highlight GReplaceText term=reverse cterm=reverse gui=reverse function! s:gReplace() if empty(s:save_qf_list) return endif let change_all = v:cmdbang let changeset = {} " Parse the replace buffer contents and get a List of changed lines let lines = getbufline('%', 1, '$') for l in lines if l !~ '[^:]\+:\d\+:.*' continue endif let match_l = matchlist(l, '\([^:]\+\):\(\d\+\):\(.*\)') let fname = match_l[1] let lnum = match_l[2] let text = match_l[3] let key = fname . ':' . lnum if s:save_qf_list[key].text == text " This line is not changed continue endif let fname = s:save_qf_list[key].fname if !has_key(changeset, fname) let changeset[fname] = {} endif let changeset[fname][lnum] = text endfor if empty(changeset) " The replace buffer is not changed by the user call s:warn_msg('Error: No changes in the replace buffer') return endif " Make the changes made by the user to the buffers for f in keys(changeset) let f_l = changeset[f] if !filereadable(f) continue endif silent! exe 'hide edit ' . f let change_buf_all = 0 " Accept all the changes in this buffer for lnum in keys(f_l) exe lnum let cur_ltext = getline(lnum) let new_ltext = f_l[lnum] let s_idx =0 while cur_ltext[s_idx] == new_ltext[s_idx] let s_idx += 1 endwhile let e_idx1 = strlen(cur_ltext) - 1 let e_idx2 = strlen(new_ltext) - 1 while e_idx1 >= 0 && cur_ltext[e_idx1] == new_ltext[e_idx2] let e_idx1 -= 1 let e_idx2 -= 1 endwhile let e_idx1 += 2 if (s_idx + 1) == e_idx1 " If there is nothing to highlight, then highlight the " last character let e_idx1 += 1 endif let hl_pat = '/\%'.lnum.'l\%>'.s_idx.'v.*\%<'.e_idx1.'v/' exe '2match GReplaceText ' . hl_pat redraw! try let change_line = 0 if !change_all && !change_buf_all let new_text_frag = strpart(new_ltext, s_idx, \ e_idx2 - s_idx + 1) echo "Replace with '" . new_text_frag . "' (y/n/a/b/q)?" let ans = 'x' while ans !~? '[ynab]' let ans = nr2char(getchar()) if ans == 'q' || ans == "\" " Quit return endif endwhile if ans == 'a' " Accept all let change_all = 1 endif if ans == 'b' " Accept changes in the current buffer let change_buf_all = 1 endif if ans == 'y' " Yes let change_line = 1 endif endif if change_all || change_buf_all || change_line call setline(lnum, f_l[lnum]) endif finally 2match none endtry endfor endfor endfunction function! s:gReplace_show_matches() let qf = getqflist() if empty(qf) call s:warn_msg('Error: Quickfix list is empty') return endif let new_qf = {} " Populate the buffer with the current quickfix list let lines = [] for l in qf if l.valid && l.lnum > 0 && l.bufnr > 0 let fname = fnamemodify(bufname(l.bufnr), ':.') let buf_text = fname . ':' . l.lnum . ':' . l.text let k = fname . ':' . l.lnum let new_qf[k] = {} let new_qf[k].fname = fnamemodify(bufname(l.bufnr), ':p') let new_qf[k].text = l.text else let buf_text = l.text endif call add(lines, buf_text) endfor if empty(lines) " No valid matching lines return endif let w = bufwinnr(s:gRepl_bufname) if w == -1 " Create a new window silent! exe 'new ' . s:gRepl_bufname else exe w . 'wincmd w' " Discard the contents of the buffer %d _ endif call append(0, '#') call append(1, '# Modify the contents of this buffer and then') call append(2, '# use the ":Greplace" command to merge the changes.') call append(3, '#') call append(4, lines) call cursor(5, 1) setlocal buftype=nofile setlocal bufhidden=wipe setlocal nomodified command! -buffer -nargs=0 -bang Greplace call s:gReplace() let s:save_qf_list = new_qf endfunction " gSearch " Search for a pattern in a group of files using ':grep' function! s:gSearch(type, ...) let grep_opt = '-r' let pattern = '' let filenames = '' " Parse the arguments " grep command-line flags are specified using the "-flag" format " the next argument is assumed to be the pattern " and the next arguments are assumed to be filenames or file patterns let argcnt = 1 while argcnt <= a:0 if &grepprg =~ 'findstr' && a:{argcnt} =~ '^/' let grep_opt = grep_opt . ' ' . a:{argcnt} elseif a:{argcnt} =~ '^-' let grep_opt = grep_opt . ' ' . a:{argcnt} elseif pattern == '' let pattern = a:{argcnt} else let filenames = filenames . ' ' . a:{argcnt} endif let argcnt += 1 endwhile " If search pattern is not specified on command-line, ask for it if pattern == '' let pattern = input('Search pattern: ', expand('')) if pattern == '' return endif " Quote the supplied pattern let pattern = g:Greplace_Shell_Quote_Char . pattern . \ g:Greplace_Shell_Quote_Char endif if a:type == 'grep' if filenames == '' let filenames = input('Search in files: ', '*', 'file') endif elseif a:type == 'args' " Search in all the filenames in the argument list let arg_cnt = argc() if arg_cnt == 0 call s:warn_msg('Error: Argument list is empty') return endif let filenames = '' for i in range(0, arg_cnt - 1) let filenames .= ' ' . argv(i) endfor else " Get a list of all the buffer names let filenames = '' for i in range(1, bufnr('$')) let bname = bufname(i) if bufexists(i) && buflisted(i) && filereadable(bname) && \ getbufvar(i, '&buftype') == '' let filenames .= ' ' . bufname(i) endif endfor endif if filenames == '' call s:warn_msg('Error: No valid file names') return endif " Use ! after grep, so that Vim doesn't automatically jump to the " first match let grep_cmd = 'grep! ' . grep_opt . ' ' . pattern . ' ' . filenames " Run the grep and get the matches silent! exe grep_cmd call s:gReplace_show_matches() endfunction command! -nargs=0 Gqfopen call s:gReplace_show_matches() command! -nargs=* -complete=file Gsearch call s:gSearch('grep', ) command! -nargs=* Gargsearch call s:gSearch('args', ) command! -nargs=* Gbuffersearch call s:gSearch('buffer', ) " restore 'cpo' let &cpo = s:cpo_save unlet s:cpo_save