How do I replace-paste yanked text in vim without yanking the deleted lines?

45

15

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.

bitmask

Posted 2011-08-10T19:39:39.033

Reputation: 705

3Hey 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 – 2014-08-01T06:40:33.047

1vnoremap p "_dP removes white space on paste. Edit 1 works perfectly. – Vaclav Kasal – 2019-09-12T08:18:44.620

Answers

23

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}"

romainl

Posted 2011-08-10T19:39:39.033

Reputation: 19 227

1I'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 – 2014-08-01T06:41:03.787

vnoremap p "_dP stop working for me in select mode, but vnoremap <leader>p "_dP does work – whitesiroi – 2015-11-05T10:22:56.963

1maybe <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 – 2016-11-09T07:17:32.207

1@Hashbrown, :help mapleader. – romainl – 2016-11-09T07:22:11.470

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 – 2018-10-12T04:07:28.717

14

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

Tradsud

Posted 2011-08-10T19:39:39.033

Reputation: 393

3Very cool thing. I knew about buffers, but didn't connect them with this problem. It is still cumbersome to "a everything, but okay. – bitmask – 2011-08-10T20:27:40.377

6

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

jinfield

Posted 2011-08-10T19:39:39.033

Reputation: 391

This solution seems most usable, until something clever comes up from the vim itself. – Yugal Jindle – 2013-11-13T04:14:01.877

4

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". 

Jerph

Posted 2011-08-10T19:39:39.033

Reputation: 197

3

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.

garyjohn

Posted 2011-08-10T19:39:39.033

Reputation: 29 085

1

This is what i use (literally copied from my .vimrc) 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 {{{

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

function! SmartCut()
    execute 'normal! gv"+c'

    if visualmode() != "\<C-v>" " If not Visual-Block mode
        " Trim the last \r\n | \n | \r character in the '+' buffer
        " NOTE: This messes up Visual-Block pasting.
        let @+ = substitute(@+,'\(\r\?\n\|\r\)$','','g')
    endif

    if exists("s:debug_smart_cut") && s:debug_smart_cut
        echomsg "SmartCut '+' buffer: " . @+
    endif
endfunction

function! SmartCopy()
    execute 'normal! gv"+y'

    if visualmode() != "\<C-v>" " If not Visual-Block mode
        " Trim the last \r\n | \n | \r character in the '+' buffer
        " NOTE: This messes up Visual-Block pasting.
        let @+ = substitute(@+,'\(\r\?\n\|\r\)$','','g')
    endif

    if exists("s:debug_smart_copy") && s:debug_smart_copy
        echomsg "SmartCopy '+' buffer: " . @+
    endif
endfunction

" Delete to black hole register before pasting. This function is a smarter version of "_d"+P or "_dp to handle special cases better.
" SOURCE: http://stackoverflow.com/questions/12625722/vim-toggling-buffer-overwrite-behavior-when-deleting
function! SmartPaste()
    let mode = 'gv'

    let delete = '"_d'

    let reg = '"+'

    " 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 = len(getline(selectionEndLine))
    let nextLineLength = len(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 cmd = 'P'

        if exists("s:debug_smart_paste") && 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 cmd = 'P'

        if exists("s:debug_smart_paste") && 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) and next line is not blank and not the last line
    elseif selectionEndColumn > selectionEndLineLength && nextLineLength > 0 && selectionEndLine != lastLineOfBuffer
        let cmd = 'P'

        if exists("s:debug_smart_paste") && s:debug_smart_paste
            echomsg "SmartPaste special case #3"
        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 next line is the last line
    elseif selectionEndColumn > selectionEndLineLength && selectionEndLine == lastLineOfBuffer
        " SOURCE:  http://vim.wikia.com/wiki/Quickly_adding_and_deleting_empty_lines

        " Fixes bug where if the last line is fully selected (Shift + V) and a paste occurs, that the paste appears to insert after the first character of the line above it because the delete operation [which occurs before the paste]
        " is causing the caret to go up a line, and then 'p' cmd causes the paste to occur after the caret, thereby pasting after the first letter of that line.
        " However this but does not occur if there's a blank line underneath the selected line, prior to deleting it, as the cursor goes down after the delete in that situation.
        call append(selectionEndLine, "")

        let cmd = 'p'

        if exists("s:debug_smart_paste") && s:debug_smart_paste
            echomsg "SmartPaste special case #4"
        endif

    else
        let cmd = 'p'

        if exists("s:debug_smart_paste") && s:debug_smart_paste
            echomsg "SmartPaste default case"
        endif
    endif

    if exists("s:debug_smart_paste") && s:debug_smart_paste
        echomsg "SmartPaste debug info:"
        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 "    cmd: " . cmd
        echo [getpos("'<")[1:2], getpos("'>")[1:2]]
        echo "visualmode(): " . visualmode()
        echo "mode(): " . mode()
    endif

    if visualmode() != "\<C-v>" " If not Visual-Block mode
        " Trim the last \r\n | \n | \r character in the '+' buffer
        " NOTE: This messes up Visual-Block pasting.
        let @+ = substitute(@+,'\(\r\?\n\|\r\)$','','g')
    endif

    try
        execute 'normal! ' . mode . delete . reg . cmd
    catch /E353:\ Nothing\ in\ register\ +/
    endtry

    " Move caret one position to right
    call cursor(0, col(".") + 1)
endfunction

" p or P delete to black hole register before pasting
" NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
vnoremap <silent> p :<C-u>call SmartPaste()<CR>
vnoremap <silent> P :<C-u>call SmartPaste()<CR>

" MiddleMouse delete to black hole register before pasting
nnoremap <MiddleMouse> "+p " Changes default behavior from 'P' mode to 'p' mode for normal mode middle-mouse pasting
" NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
vnoremap <silent> <MiddleMouse> :<C-u>call SmartPaste()<CR>
inoremap <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
noremap <2-MiddleMouse> <Nop>
inoremap <2-MiddleMouse> <Nop>
noremap <3-MiddleMouse> <Nop>
inoremap <3-MiddleMouse> <Nop>
noremap <4-MiddleMouse> <Nop>
inoremap <4-MiddleMouse> <Nop>

if os != "mac" " NOTE: MacVim provides Command+C|X|V|A|S and undo/redo support and also can Command+C|V to the command line by default
    " 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>+
    " TODO: Is their a select-all for the command line???

    " Cut, copy, and paste support for visual and insert mode (not for normal 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>
    inoremap <C-v> <C-r><C-o>+

    " Select-all support for normal, visual, and insert mode
    " http://vim.wikia.com/wiki/Using_standard_editor_shortcuts_in_Vim
    nnoremap <C-a> ggVG
    vnoremap <C-a> ggVG
    inoremap <C-a> <Esc>ggVG

    " Save file support for normal, visual, and insert mode
    " SOURCE: http://vim.wikia.com/wiki/Map_Ctrl-S_to_save_current_or_new_files
    " If the current buffer has never been saved, it will have no name,
    " call the file browser to save it, otherwise just save it.
    command -nargs=0 -bar Update if &modified |
                                \    if empty(bufname('%')) |
                                \        browse confirm write |
                                \    else |
                                \        confirm write |
                                \    endif |
                                \endif
    nnoremap <silent> <C-s> :update<CR>
    " NOTE: <C-u> removes the '<,'> visual-selection from the command line. See :h c_CTRL-u
    vnoremap <silent> <C-s> :<C-u>update<CR>V
    " NOTE: <C-o> executes a normal-mode command without leaving insert mode. See :help ins-special-special
    "inoremap <silent> <C-s> <C-o>:update<CR>
    "
    " <C-o> doesn't seem to work while also using the "Open the OmniCompletion menu as you type" code while the menu is visible.
    " Doing "call feedkeys("\<C-x>\<C-o>", "n")" to perform omni completion seems to be the issue.
    " However doing "call feedkeys("\<C-x>\<C-i>", "n")" to perform keywork completion seems to work without issue.
    "
    " Workaround will exit insert mode to execute the command and then enter insert mode.
    inoremap <silent> <C-s> <Esc>:update<CR>I

    " Undo and redo support 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

    function! Find()
        let wordUnderCursor = expand('<cword>')
        if len(wordUnderCursor) > 0
            execute 'promptfind ' . wordUnderCursor
        else
            execute 'promptfind'
        endif
    endfunction

    function! Replace()
        let wordUnderCursor = expand('<cword>')
        if len(wordUnderCursor) > 0
            execute 'promptrepl ' . wordUnderCursor
        else
            execute 'promptrepl'
        endif
    endfunction

    " Find and Find/Replace support for normal, visual, and insert mode
    nnoremap <C-f> :call Find()<CR>
    nnoremap <C-h> :call Replace()<CR>

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

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

" }}} Windows common keyboard shortcuts and pasting behavior

FocusedWolf

Posted 2011-08-10T19:39:39.033

Reputation: 111

1

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

Sebastian Blask

Posted 2011-08-10T19:39:39.033

Reputation: 123

Perfect! This is the first of these options that worked exactly as expected for me. Thanks! – Jamis Charles – 2019-04-15T16:54:34.467

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.

Ingo Karkat

Posted 2011-08-10T19:39:39.033

Reputation: 19 513

-1

tl;dr - vnoremap p "_c*

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

" Paste on new line
nnoremap ,p op
nnoremap ,P Op

kawerte

Posted 2011-08-10T19:39:39.033

Reputation: 1