2

For Emacs there is idedit. It Allows you to select some text, then hit one button, which automatically select all other occurrences of the selected text in the current buffer. Then you can edit the primary selection and the text in the secondary selections will be modified accordingly as well.

Is there something like that for Vim?

I know I can do what the answers describe here: Replace current selection and all identical occurrences in a file

But those are way to many key strokes. It only needs one keystroke (or 2, if you include the modifier key) in Emacs. And the answer by statox requires 8! Is there some way to put those 8 keystrokes into a function with VimScript or a plugin that does this?

2 Answers 2

3

You can apply Peter Rincker's solution in a loop:

fun! ReplaceAll()
    " Don't do anything if there isn't a search pattern.
    if @/ is# '' 
        return
    endif

    " Save view state and wrapscan setting.
    let l:save = winsaveview()
    let l:old_ws = &wrapscan

    try
        " Make sure search won't wrap around.
        set nowrapscan

        while 1
            " Go to next match and select.
            let l:pos = getpos('.')
            try
                normal! n
            catch
                " E385: search hit BOTTOM without match for: xzxxx
                " Also catch other errors.
                break
            endtry

            " Position unchanged; shoudn't happen AFAIK, but better
            " check otherwise you're going to end up in an infinite
            " loop!
            if getpos('.') == l:pos
                break
            endif

            " Apply last edit command.
            normal! .
        endwhile
    finally
        " Restore view.
        call winrestview(l:save)
        let &wrapscan = l:old_ws
    endtry
endfun

So the workflow with this is:

  1. Search for /foo.
  2. Apply change with c operator, e.g. to change foo to XXX use cwXXX.
  3. :call ReplaceAll()
  4. PROFIT!

You can map ReplaceAll() to a command or binding as well, of course.

4
  • Hmm... That is a little better yes, but still way more keystorkes and mental effort (retyping the word in the search command rather than just visually selecting it) than ideit :/ Commented Sep 3, 2018 at 23:36
  • You can just visually select it @lotolmencre; Peter's answer explains how. Commented Sep 3, 2018 at 23:38
  • Are you referring to the visual star bit? Commented Sep 3, 2018 at 23:50
  • @lotolmencre Yeah Commented Sep 4, 2018 at 0:00
0

I have a solution using operators and text objects, also working with visual-mode. I believe it uses less keystrokes and is more vim-like than the idedit solution, and becomes intuitive and quick to use.

function! GetTextObject(type, is_visual)
    let sel_save = &selection
    let &selection = "inclusive"
    let reg_save = @@
    if a:is_visual
      silent execute "normal! gvy"
    elseif a:type == 'line'
      silent execute "normal! '[V']y"
    else
      silent execute "normal! `[v`]y"
    endif
    let text = @@
    let &selection = sel_save
    let @@ = reg_save
    return text
endfunction

function! ReplaceOperator(type, ...)
    let text = GetTextObject(a:type, a:0)
    call feedkeys(":%s/".text."//g\<left>\<left>", "n")
endfunction
nnoremap gr :set opfunc=ReplaceOperator<cr>g@
vnoremap gr :<C-u>call ReplaceOperator(visualmode(), 1)<cr>

function! AppendReplaceOperator(type, ...)
    let text = GetTextObject(a:type, a:0)
    call feedkeys(":%s/".text."/".text."/g\<left>\<left>", "n")
endfunction
nnoremap gA :set opfunc=AppendReplaceOperator<cr>g@
vnoremap gA :<C-u>call AppendReplaceOperator(visualmode(), 1)<cr>

function! PrependReplaceOperator(type, ...)
    let text = GetTextObject(a:type, a:0)
    call feedkeys(":%s/".text."/".text."/g".repeat("\<left>", len(text) + 2), "n")
endfunction
nnoremap gI :set opfunc=PrependReplaceOperator<cr>g@
vnoremap gI :<C-u>call PrependReplaceOperator(visualmode(), 1)<cr>

Inserting this into your ~/.vimrc or ~/_vimrc or equivalent, this should give you three operators which work on text-objects in normal-mode, and selections in visual-mode. These operators are gr, gA, and gI standing respectively for "go replace", "go Append replace", and "go Insert before replace". These may be intuitive because many operators are under the prefix g, and r replaces characters, A appends and I inserts before.

Now, you can simply move the cursor over the word you want to replace over the whole buffer, and type in normal-mode:

griwThing<cr>

to replace all instances of the word with "Thing". The other operators work similarly but put you in a substitute command with the text being replaced already there, with the cursor placed after or before the text. So, for example, select your text in visual mode, for example the current Word until the end of the line:

viW$hgA_1<cr>

selects the text giving you a visual of what you will replace, then puts you in a substitute command letting you add, for example "_1" to the end of the replacement. You can also use this to easily remove characters from the end of each instance.

Explanation of the vimscript code: Operators are implemented in a strange way in vim, using a setting called operatorfunc or opfunc which is called whenever you type @g in normal-mode, passing in a type of the text-object and some other parameters. Then, vim will set the bracket marks referencing the start and end of the text-object. The GetTextObject function just simplifies this, pulled mostly directly from :help map-operator, so operators can be made easily.

Then, for each operator there is a function for opfunc to be set to, with the correct parameters, and they use feedkeys to open up the command-mode with the correct text to define a substitute command, with the cursor placed correctly. Then the normal-mode mapping sets the opfunc then activates it with g@, while the visual-mode mapping opens the command-mode and clears the auto-inserted visual range using <C-u>, then just calls it and tells it is being called from visual-mode.

One problem is that if there are any special characters (including newlines) or escape sequences recognized by the substitute command, then something may mess up, so I will expand on the answer soon to fix that.

(Also, it may not be neccessary to have gA and gI, if you don't mind using & in a substitute command to reference the replaced text, so griw&_1<cr> does the same as gAiw_1<cr>).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.