" gs will propegate a change made to a over a text object. This is done " by the plugin taking note of the word under the cursor before a text change is " made and what the word was changed to after the text was changed, and gs will " create a substitute command based on that and apply it to a text object. " " This is extremely useful and ergonomic for renaming variables, types or " otherwise in a text body. noremap gs :set operatorfunc=do_subst_enterg@ xnoremap gs :call v_do_subst_enter() " gS is like gs, except it: " " * doesn't feed a newline at the end " * doesn't wrap the string to substitue in word boundaries. noremap gS :set operatorfunc=do_subst_no_enterg@ xnoremap gS :call v_do_subst_no_enter() " Hotkey for replacing the most recently searched matches. noremap :%s// let s:current_word_1 = '' let s:current_word = '' let s:old_word = '' let s:new_word = '' function! s:save_cur_word() abort let s:current_word_1 = s:current_word let s:current_word = expand("") endfunction function! s:save_new_word_from_insert() abort let l:new_word = expand("") " Don't replace the last useful substitution with foo -> foo. if l:new_word ==# s:current_word return endif " InsertLeave happens before CursorMoved. let s:old_word = s:current_word let s:new_word = l:new_word endfunction function! s:save_new_word() abort " For a normal-mode change CursorMoved fires first, so current_word now " describes the post-change cursor position. current_word_1 is therefore " the word from immediately before the change. let s:old_word = s:current_word_1 let s:new_word = expand("") endfunction augroup FieldMarshalSubstitute au! autocmd CursorMoved * call s:save_cur_word() autocmd TextChanged * call s:save_new_word() autocmd InsertLeave * call s:save_new_word_from_insert() augroup END function! s:do_subst_enter(...) abort call s:do_subst_priv("'[", "']", v:true) endfunction function! s:do_subst_no_enter(...) abort call s:do_subst_priv("'[", "']", v:false) endfunction function! s:v_do_subst_enter(...) abort call s:do_subst_priv("'<", "'>", v:true) endfunction function! s:v_do_subst_no_enter(...) abort call s:do_subst_priv("'<", "'>", v:false) endfunction function! s:do_subst_priv(m0, m1, do_enter) abort let [_, lnum0, _, _] = getpos(a:m0) let [_, lnum1, _, _] = getpos(a:m1) " Need to call feedkeys() because @. may contain special characters like " backspace. call feedkeys( \ printf( \ ":%s %d,%d s/\\V%s%s%s/%s/g%s", \ a:do_enter ? "silent!" : "", \ lnum0, \ lnum1, \ a:do_enter ? "\\<" : "", \ escape(s:old_word, '/\'), \ a:do_enter ? "\\>" : "", \ escape(s:new_word, '/\'), \ a:do_enter ? "\n" : "")) endfunction