88

So I usually find myself copying text from one point to another while overwriting old text where the new is pasted:

blah1
newtext
blah2
wrong1
blah3
wrong2
blah4

Suppose I visual-mark newtext and yank it. Now I select wrong1 (which could be anything, not necessarily just a word) and paste the newtext. However, if I now do the same with wrong2 it will be replaced with wrong1 instead of newtext.

So how do I keep the text that is in the buffer from being swapped with the text that I am currently overwriting?

Edit 1

Although I quite like the reigister suggestions (I think I will start using registers more, now that I discovered the :dis command), I am going with a modification of jinfield's answer, because I do not use the swapping mode.

vnoremap p "0p
vnoremap P "0P
vnoremap y "0y
vnoremap d "0d

does the trick perfectly.

Edit 2

I was too fast; romainl's solution is precisely what I was looking for, without the hack in Edit 1.
Actually, vnoremap p "_dP is enough!
So, changing accepted answer.

2
  • 4
    Hey fyi, I've long used that vnoremap p "_dP map, and I've noticed that it doesn't work well for the last word/character in a line. I've gone back to the vnoremap p "0p, vnoremap P "0P, and set clipboard=unnamed (for OSX)
    – Kache
    Aug 1, 2014 at 6:40
  • 1
    vnoremap p "_dP removes white space on paste. Edit 1 works perfectly. Sep 12, 2019 at 8:18

13 Answers 13

57

I have these mappings in my .vimrc:

" delete without yanking
nnoremap <leader>d "_d
vnoremap <leader>d "_d

" replace currently selected text with default register
" without yanking it
vnoremap <leader>p "_dP

"_ is the "blackhole register", according to :help "_:

"When writing to this register, nothing happens. This can be used to delete text without affecting the normal registers. When reading from this register, nothing is returned. {not in Vi}"

6
  • 2
    I've used that vnoremap p "_dP map and noticed that it doesn't work well for the last word/character in a line. I've gone back to the vnoremap p "0p, vnoremap P "0P, and set clipboard=unnamed (for OSX)
    – Kache
    Aug 1, 2014 at 6:41
  • vnoremap p "_dP stop working for me in select mode, but vnoremap <leader>p "_dP does work
    – whitesiroi
    Nov 5, 2015 at 10:22
  • 1
    maybe <leader> is a placeholder I'm supposed to know to replace with something, but this only worked for me if I removed it. What does it mean here?
    – Hashbrown
    Nov 9, 2016 at 7:17
  • 1
    @Hashbrown, :help mapleader.
    – romainl
    Nov 9, 2016 at 7:22
  • Small caveat: the vnoremap <leader>p mapping doesn't work correctly on the last line of the buffer because as soon as you delete that last line of the buffer, you are now on the once-second-last line, and the P will paste above that line, instead of below.
    – Kal
    Oct 12, 2018 at 4:07
19

In addition to the standard buffer, you can yank text into named buffers, and then put from those named buffers. There are up to 26 named buffers you can use (one for each letter). Use double quotes and a letter to access a named buffer. Examples:

"dyy - Yank current line into buffer d.
"a7yy - Yank next seven lines into buffer a.
"dP - Put the contents of buffer d before cursor.
"ap - Put the contents of buffer a after cursor

Another cool thing, if you use a capital letter instead of lower case, i.e "Dyy the current line will be Appended to the buffer d instead of replacing it. More details in the O`Reilly book: http://docstore.mik.ua/orelly/unix/vi/ch04_03.htm

3
  • 3
    Very cool thing. I knew about buffers, but didn't connect them with this problem. It is still cumbersome to "a everything, but okay.
    – bitmask
    Aug 10, 2011 at 20:27
  • I'm a pretty big fan of "p so that paste becomes "pp, and use alternate buffers if I need more. But for the most part "pp is quick and easy for 90% of my re-paste needs.
    – Brettins
    Dec 1, 2020 at 21:19
  • 2
    I think you mean 'registers' instead of 'buffers'
    – mattb
    Jun 16, 2021 at 15:00
15

When using put in visual mode, the text you're replacing, wrong1, is overwritten by the contents of the 'unamed' register.

This actually works by 'putting' the register after the selection and then deleting the selection. The problem is that this deletion is now stored in the unnamed register and will be used for the next put action.

The solution, according to :h v_p, is to yank into a named register, such as "0y, then paste using "0p as many time as you need. It may be helpful to map <leader>y and <leader>p to use a named register, if this is something you do frequently.

:map <leader>y "0y
:map <leader>p "0p

for more help see:

:help v_p
:help map
1
  • This solution seems most usable, until something clever comes up from the vim itself. Nov 13, 2013 at 4:14
15

Use P to paste without yanking the deleted text.

:help v_P

With P the unnamed register is not changed (and neither the selection or clipboard), you can repeat the same change.

This behavior was introduced in v8.2.4242 (2022-01-28) and refined in v8.2.4881 (2022-05-06).

Or if your muscle memory is too strong:

xnoremap p P
2
  • 2
    Better 30 years late than never! This should be the accepted answer. Apr 11, 2023 at 16:02
  • 1
    Yup, this is exactly what I needed. I've wasted an aggregate of a day of my life with "0p. Aug 21, 2023 at 22:55
7

Pasting from "0 register is important to know, but you often want to replace many times. If you make it a repeatable action, you can use the . operator, as alluded to by garyjohn. It's explained on the vim wiki:

yiw     yank inner word (copy word under cursor, say "first". Same as above).
...     Move the cursor to another word (say "second").
ciw<C-r>0   select "second", then replace it with "first" If you are at the start of the word then cw<C-r>0 is sufficient.
...     Move the cursor to another word (say "third").
.   select "third", then replace it with "first". 
5

When you yank the text into the unnamed register*, a copy is also put into register 0. Each time you replace selected text, you can just paste from the 0 register. See

:help registers

In addition, if you are replacing a number of words with the same word, you can just move to the start of the word to be replaced and type .. That will repeat the last editing operation. See

:help single-repeat

* The storage locations that you yank into and put from are called registers. A buffer is the thing that you edit, usually a copy of a file from disk.

3

I use clipboard=unamedplus so I found that a lot of the solutions here weren't working because the paste was putting the overwritten text into "+ instead of "". I additionally found that "_dP had trouble at the end of lines.

However I found that using c worked.

set clipboard=unnamedplus " Use system clipboard.

" Don't put pasted-over content into the clipboard.
vnoremap p "_c<C-r><C-o>+<Esc>

This basically changes the text, and uses an insert mode paste (<C-r><C-o>+) of the unnamed register. Since the insert mode cursor can be after the last character of a line this has no trouble at the end of lines.

Possible tweaks:

  • If you don't want to override the regular p command just use <Leader>p.
  • If you use a different clibboard register replace + with " or *.
2

As something like vnoremap p "_dP(I also tried x or c) has issues with line beginning and end, I ended up doing this: vnoremap p :<C-U>let @p = @+<CR>gvp:let @+ = @p<CR>which I found simpler than the existing plugins (which also did not work with set clipboard=unnamedplus out of the box). So what it does is:

  • switch to command mode
  • remove range (C-U)
  • backup + register(due to unnamedplus, alternatives are " and * depending on your configuration) to p
  • recover selection and paste
  • switch to command mode again
  • recover register
1
  • Perfect! This is the first of these options that worked exactly as expected for me. Thanks! Apr 15, 2019 at 16:54
2

Select the text and paste by P(uppercase).

Example:

viwP

See h: v_P for more infomation.

1
  • 1
    it looks like this just works with Vim 9.0. Aug 10, 2022 at 3:11
1

I need this so often, I wrote a plugin for that: ReplaceWithRegister.

This plugin offers a two-in-one gr command that replaces text covered by a {motion}, entire line(s) or the current selection with the contents of a register; the old text is deleted into the black-hole register, i.e. it's gone.

1

The following might work in many cases: c+CtrlR+0+Esc.
You might need to do

:set paste

before to inhibit the autoindent.

1

This is what i use for Windows-style Control + X cut/Control + C copy/Control + V paste/Control + S save/Control + F find/Control + H replace behavior.

The SmartPaste() function should contain what you seek, i.e. a way to paste over highlighted text without simultaneously yanking what was selected.

" Windows common keyboard shortcuts and pasting behavior {{{

" Recommended vimrc settings:
"     Set clipboard like this:
"         NOTE: Use ^= to prepend a value to a comma-separated list. See :h set^=
"         set clipboard^=unnamedplus            " Works on Linux but has issues yanking on Windows.
"         set clipboard^=unnamed,unnamedplus    " Works on Linux and Windows (and probably MacVim which needed the unnamed option the last time i used it).
"
"     Set mouse like this:
"         set mouse=a    " Ensures mouse binds work in all modes.
"
"     Disable autoselect:
"         NOTE: This forces MiddleMouse clicks in other programs to paste the contents of Vim's "+ unnamedplus register instead of the "* unnamed selection register. See :h go-a
"         set guioptions-=a
"         set guioptions-=A

" Functions {{{

" Uncomment to enable debugging.
" Check debug output with :messages
" let s:debug_smart_cut = 1
" let s:debug_smart_copy = 1
" let s:debug_smart_paste = 1

function! SmartCut() " {{{
    if !has('clipboard')
        echoerr 'Clipboard support is required to use this function.'
        return
    endif

    let clipboard_register = '+'

    " Restore the visual selection with 'gv' and delete the selection into the clipboard.
    " NOTE: 'd' (delete) is better than 'c' (copy) because it moves the caret to the right for us.
    execute 'normal! gv"' . clipboard_register . 'd'

    " Enter insert mode.
    startinsert

    " Gets the value if it exists, else returns 0.
    if get(s:, 'debug_smart_cut')
        echomsg "SmartCut"
        echomsg "    clipboard contents: " . getreg(clipboard_register)
    endif
endfunction " }}}

function! SmartCopy() " {{{
    if !has('clipboard')
        echoerr 'Clipboard support is required to use this function.'
        return
    endif

    let clipboard_register = '+'

    " Restore the visual selection with 'gv' and yank the selection into the clipboard.
    execute 'normal! gv"' . clipboard_register . 'y'

    " Gets the value if it exists, else returns 0.
    if get(s:, 'debug_smart_copy')
        echomsg "SmartCopy"
        echomsg "    clipboard contents: " . getreg(clipboard_register)
    endif
endfunction " }}}

function! SmartPaste() " {{{
    if !has('clipboard')
        echoerr 'Clipboard support is required to use this function.'
        return
    endif

    if !&modifiable
        return
    endif

    " See :help '> for more information. Hint: if you select some text and press ':' you will see :'<,'>
    " SOURCE: http://superuser.com/questions/723621/how-can-i-check-if-the-cursor-is-at-the-end-of-a-line
    " SOURCE: http://stackoverflow.com/questions/7262536/vim-count-lines-in-selected-range
    " SOURCE: https://git.zug.fr/config/vim/blob/master/init.vim
    " SOURCE: https://git.zug.fr/config/vim/blob/master/after/plugin/zzzmappings.vim
    let currentColumn = col(".")
    let currentLine = line(".")
    let lastVisibleLetterColumn = col("$") - 1
    let lastLineOfBuffer = line("$")
    let selectionEndLine = line("'>")
    let selectionEndLineLength = strchars(getline(selectionEndLine))
    let nextLineLength = strchars(getline(currentLine + 1))
    let selectionStartColumn = col("'<")
    let selectionEndColumn = col("'>")

    " If selection does not include or go beyond the last visible character of the line (by also selecting the invisible EOL character).
    if selectionEndColumn < selectionEndLineLength
        let pee = 'P'

        " Gets the value if it exists, else returns 0.
        if get(s:, 'debug_smart_paste')
            echomsg "SmartPaste special case #1"
        endif

    " If attempting to paste on a blank last line.
    elseif selectionEndLineLength == 0 && selectionEndLine == lastLineOfBuffer
        let pee = 'P'

        " Gets the value if it exists, else returns 0.
        if get(s:, 'debug_smart_paste')
            echomsg "SmartPaste special case #2"
        endif

    " If selection ends after the last visible character of the line (by also selecting the invisible EOL character),
    " or the line is visually selected (Shift + V),
    " and the next line is not blank,
    " and selection does not end on the last line.
    elseif selectionEndColumn > selectionEndLineLength && nextLineLength > 0 && selectionEndLine != lastLineOfBuffer
        let pee = 'P'

        " Gets the value if it exists, else returns 0.
        if get(s:, 'debug_smart_paste')
            echomsg "SmartPaste special case #3"
        endif

    else
        let pee = 'p'

        " Gets the value if it exists, else returns 0.
        if get(s:, 'debug_smart_paste')
            echomsg "SmartPaste default case"
        endif
    endif

    let clipboard_register = '+'

    " Gets the value if it exists, else returns 0.
    if get(s:, 'debug_smart_paste')
        echomsg "SmartPaste"
        echomsg "    clipboard contents: " . getreg(clipboard_register)
        echomsg "    currentColumn: " . currentColumn
        echomsg "    currentLine: " . currentLine
        echomsg "    lastVisibleLetterColumn: " . lastVisibleLetterColumn
        echomsg "    lastLineOfBuffer: " . lastLineOfBuffer
        echomsg "    selectionEndLine: " . selectionEndLine
        echomsg "    selectionEndLineLength: " . selectionEndLineLength
        echomsg "    nextLineLength: " . nextLineLength
        echomsg "    selectionStartColumn: " . selectionStartColumn
        echomsg "    selectionEndColumn: " . selectionEndColumn
        echomsg "    pee: " . pee
        echomsg "    selection bounds" . string([getpos("'<")[1:2], getpos("'>")[1:2]])
        echomsg "    visualmode(): " . visualmode()
        echomsg "    mode(): " . mode()
        echomsg "    getpos('.'): " . string(getpos('.'))
    endif

    try
        " Enter paste mode.
        " NOTE: This prevents InsertCharPre autocommands from running repeatedly for each character in the pasted string.
        set paste

        " Backup clipboard.
        let clipboard_contents = getreg(clipboard_register)
        let clipboard_type = getregtype(clipboard_register)

        " If clipboard type is linewise.
        if clipboard_type[0] == 'V'
            " Change clipboard register to characterwise mode.
            call setreg(clipboard_register, clipboard_contents, 'v')
        endif

        " Restore the visual selection with 'gv' and paste the clipboard contents there.
        execute 'normal! gv"' . clipboard_register . pee

        " Restore clipboard.
        call setreg(clipboard_register, clipboard_contents, clipboard_type)
    catch /E353:\ Nothing\ in\ register/
    finally
        " Exit paste mode.
        set nopaste
    endtry
endfunction " }}}

function! SmartFind() " {{{
    let wordUnderCursor = expand('<cword>')
    if wordUnderCursor == ''
        execute 'promptfind'
    else
        execute 'promptfind' wordUnderCursor
    endif
endfunction " }}}

function! SmartReplace() " {{{
    let wordUnderCursor = expand('<cword>')
    if wordUnderCursor == ''
        execute 'promptrepl'
    else
        execute 'promptrepl' wordUnderCursor
    endif
endfunction " }}}

function! SmartSaveFile() " {{{
    " SOURCE: http://vim.wikia.com/wiki/Map_Ctrl-S_to_save_current_or_new_files

    " If file is saved.
    if !&modified
        return
    endif

    try
        " If the current buffer has no name.
        if empty(bufname('%'))
            " Display a save dialog so the user can specify a file name.
            browse confirm write
        else
            " Force a write even if a file exists with that name.
            confirm write
        endif
    catch /E212:\ Can't\ open\ file\ for\ writing/
        if has("win32") || has("win64")
            echomsg "ERROR: You lack write permission for this directory or the file name is not valid."
        else
            " NOTE: The [browse confirm write] call above will establish the filename in the case of an empty buffer.
            try
                " A hack that uses tee to output the buffer to the % filename with sudo permissions.
                " SOURCE: https://stackoverflow.com/questions/2600783/how-does-the-vim-write-with-sudo-trick-work
                execute("w !sudo tee %")

                if v:shell_error
                    throw 'Failed to save file.'
                else
                    " Reload the file, otherwise vim will detect the file changed and ask you for permission to reload the file.
                    silent edit!

                    " Clears the command line and removes 'Press ENTER or type command to continue' prompt.
                    " SOURCE: https://vim.fandom.com/wiki/Avoiding_the_%22Hit_ENTER_to_continue%22_prompts
                    redraw!
                endif
            catch /.*/
                echomsg 'SmartSaveFile() Unhandled Exception: ' . v:exception
            endtry
        endif
    endtry
endfunction " }}}

function! SmartCopyMessageHistory() " {{{
    redir @+
    silent execute(':messages')
    redir END
endfunction " }}}

" }}} Functions

" Binds {{{

" Paste for MiddleMouse in normal, visual, and insert mode {{{

" NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
nnoremap <silent> <MiddleMouse> <C-v>:<C-u>call SmartPaste()<CR>

" NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
vnoremap <silent> <MiddleMouse> :<C-u>call SmartPaste()<CR>

" Characterwise paste for insert mode.
" SOURCE: https://vim.fandom.com/wiki/Pasting_registers
" NOTE: <C-o> executes a normal-mode command without leaving insert mode. See :help ins-special-special
" NOTE: <C-r>+ inserts the contents of the unnamedplus register during insert mode. See :h i_CTRL-R
" NOTE: <C-r>+ doesn't work for Ctrl-v yanked and pasted columns of text. It forces characterwise selections to be linewise pasted. <C-r><C-o>+ works though.
" [bad, triggers InsertCharPre for each pasted letter] inoremap <silent> <MiddleMouse> <C-r><C-r>+
" [works] inoremap <silent> <MiddleMouse> <C-o>"+<MiddleMouse>
" [works, but no need for paste mode i think because it doesn't trigger InsertCharPre events for each pasted letter] inoremap <silent> <MiddleMouse> <C-o>:set paste<CR><C-r><C-o>+<C-o>:set nopaste<CR>
inoremap <silent> <MiddleMouse> <C-r><C-o>+

" Disable weird multi-click things you can do with middle mouse button.
" SOURCE: http://vim.wikia.com/wiki/Mouse_wheel_for_scroll_only_-_disable_middle_button_paste
nnoremap <2-MiddleMouse> <Nop>
inoremap <2-MiddleMouse> <Nop>
vnoremap <2-MiddleMouse> <Nop>
nnoremap <3-MiddleMouse> <Nop>
inoremap <3-MiddleMouse> <Nop>
vnoremap <3-MiddleMouse> <Nop>
nnoremap <4-MiddleMouse> <Nop>
inoremap <4-MiddleMouse> <Nop>
vnoremap <4-MiddleMouse> <Nop>

" }}} Paste for MiddleMouse in normal, visual, and insert mode

" If OS is not mac.
if system('uname') !~ 'Darwin' " NOTE: MacVim provides Command+C|X|V|A|S and undo/redo functionality and also can Command+C|V to the command line by default.
    " Copy, paste, and select-all functionality for commandline {{{

    " SOURCE: https://opensource.apple.com/source/vim/vim-62.41.2/runtime/macmap.vim.auto.html
    " NOTE: Only copy and paste are possible in the command line from what i can tell.
    "       Their is no undo for text typed in the command line and you cannot paste text onto a selection of text to replace it.
    cnoremap <C-c> <C-y>
    cnoremap <C-v> <C-r>+

    " Everyone seems to bind this. No way to select-all, but we can move the caret all the way to the left of the command line. Yay.
    cnoremap <C-A> <Home>

    " Copy message history to the clipboard.
    " NOTE: This is a workaround for not being able to select all.
    nnoremap <silent> <Leader>m :<C-u>call SmartCopyMessageHistory()<CR>

    " }}} Copy, paste, select-all functionality for commandline

    " Cut, copy, paste functionality for visual and insert mode {{{

    " Cut, copy, and paste functionality for visual mode.
    " SOURCE: http://superuser.com/questions/10588/how-to-make-cut-copy-paste-in-gvim-on-ubuntu-work-with-ctrlx-ctrlc-ctrlv
    " NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
    vnoremap <silent> <C-x> :<C-u>call SmartCut()<CR>
    vnoremap <silent> <C-c> :<C-u>call SmartCopy()<CR>
    vnoremap <silent> <C-v> :<C-u>call SmartPaste()<CR>

    " Characterwise paste for insert mode.
    " SOURCE: https://vim.fandom.com/wiki/Pasting_registers
    " NOTE: <C-o> executes a normal-mode command without leaving insert mode. See :help ins-special-special
    " NOTE: <C-r>+ inserts the contents of the unnamedplus register during insert mode. See :h i_CTRL-R
    " NOTE: <C-r>+ doesn't work for Ctrl-v yanked and pasted columns of text. It forces characterwise selections to be linewise pasted. <C-r><C-o>+ works though.
    " [works, but no need for paste mode i think because it doesn't trigger InsertCharPre events for each pasted letter] inoremap <silent> <C-v> <C-o>:set paste<CR><C-r><C-o>+<C-o>:set nopaste<CR>
    inoremap <silent> <C-v> <C-r><C-o>+

    " }}} Cut, copy, paste functionality for visual and insert mode

    " Select-all functionality for normal, visual, and insert mode {{{

    " SOURCE: http://vim.wikia.com/wiki/Using_standard_editor_shortcuts_in_Vim
    nnoremap <C-a> ggVG
    vnoremap <C-a> ggVG
    inoremap <C-a> <Esc>ggVG

    " }}} Select-all functionality for normal, visual, and insert mode

    " Save functionality for normal, visual, and insert mode {{{

    nnoremap <silent> <C-s> :call SmartSaveFile()<CR>

    " NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
    vnoremap <silent> <C-s> :<C-u>call SmartSaveFile()<CR>gv

    " NOTE: <C-o> executes a normal-mode command without leaving insert mode. See :help ins-special-special
    inoremap <silent> <C-s> <C-o>:call SmartSaveFile()<CR>

    " }}} Save functionality for normal, visual, and insert mode

    " Undo and redo functionality for normal, visual, and insert mode {{{

    nnoremap <C-z> <Esc>u
    nnoremap <C-y> <Esc><C-r>

    " NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
    vnoremap <C-z> :<C-u>uV
    vnoremap <C-y> :<C-u><C-r>V

    inoremap <C-z> <Esc>uI
    inoremap <C-y> <Esc><C-r>I

    " Disable Vim normal-mode undo/redo keys.
    " Having a single key for undo is an accident waiting to happen, especially when 'u' and 'U' are also used as visual-mode character case toggles.
    " SOURCE: https://stackoverflow.com/questions/57714401/vi-vim-remap-or-unmap-built-in-command-the-u-key-in-visual-mode
    nnoremap u <NOP>
    nnoremap <C-r> <NOP>

    " }}} Undo and redo functionality for normal, visual, and insert mode

    " Find and Replace functionality for normal, visual, and insert mode {{{

    nnoremap <silent> <C-f> :call SmartFind()<CR>
    nnoremap <silent> <C-h> :call SmartReplace()<CR>

    " NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
    vnoremap <silent> <C-f> :<C-u>call SmartFind()<CR>
    vnoremap <silent> <C-h> :<C-u>call SmartReplace()<CR>

    " NOTE: <C-o> executes a normal-mode command without leaving insert mode. See :help ins-special-special
    inoremap <silent> <C-f> <C-o>:call SmartFind()<CR>
    inoremap <silent> <C-h> <C-o>:call SmartReplace()<CR>

    " }}} Find and Replace functionality for normal, visual, and insert mode
endif

" }}} Binds

" }}} Windows common keyboard shortcuts and pasting behavior
-1
vnoremap p "_c<C-r>*<Esc>

Here is a list of my full mappings:

" Fix register copy/pasting  
   nnoremap DD "*dd  
   nnoremap D "*d  
   vnoremap D "*d  
   nnoremap d "_d  
   nnoremap dd "_dd  
   vnoremap d "_d  
   nnoremap s "_s  
   vnoremap s "_s  
   nnoremap c "_c  
   vnoremap c "_c  
   nnoremap x "_x  
   vnoremap x "_x  
   vnoremap p "_c<C-r>*<Esc>  

" Paste on new line  
    nnoremap ,p o<Esc>p  
    nnoremap ,P O<Esc>p  

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .