5

Vim is great, but like many people I get really annoyed when I want to copy, delete, then paste -- the yank buffer gets overwritten by the delete action.

Now I know there are 101 work-arounds and mappings, some of which are enumerated in posts like this one: Any way to delete in vim without overwriting your last yank?

But all of these solutions have drawbacks -- even I were a buffer-guru (which I'm not). For instance, excess keystrokes -- whereas I normally xxxx to quickly delete 4 characters (just one keystroke cuz I hold it down and wait for autorepeat), it is not practical for me to now switch to ,x,x,x,x or whatever mapping I have to use a different buffer.

What would really be ideal is simply a mode toggle, whereby you can toggle on and off the side-effect behavior of the D, d, X, and x keys so that they alternately do or do not also write their text to a buffer. That way I can simply enter the "no side-effect" mode and delete away to heart's content, then paste when I'm ready. And re-enable side-effects if desired.

Does anyone know a way to do this?

[UPDATE: SOLUTION] OK I got it: I wrote a function that toggles a "no side-effects" mode... works perfectly! See my accepted correct answer below

[UPDATE #2] My solution still works great and I use it all the time when I'm doing a lot of deleting and pasting. But meanwhile I also found a lighter way to meet the specific use-case of copy, paste, delete for simple cases where the text to delete is contiguous.

After yanking text normally, I then visually highlight the text to delete using the v command, and then simply paste over it with the p command. That achieves the desired effect without any special mapping.

Only problem with this workflow is that if I wanted to paste again, the original paste buffer is overwritten by the act of pasting over the highlighted text, but this behavior is easily changed with the following mapping in .vimrc:

vnoremap p "_dp
vnoremap P "_dP
Community
  • 1
  • 1
Magnus
  • 10,736
  • 5
  • 44
  • 57
  • I think those solutions you linked are intended to be put in `vimrc`. does something like `nnoremap dd "_dd` in your `vimrc` work for you? – none Sep 27 '12 at 16:20
  • Not really, because I already use dd to delete the whole line. Of course all my mappings go in vimrc, but this solution still doesn't address all the other d's, like dw, dl, 2dw, d$... not to mention all the x's too. And I have to type something special *with every keystroke* that I want to use it for. A full-blown mode would be so much better... – Magnus Sep 27 '12 at 16:44
  • I don't understand how a global toggle would be simpler than yanking to a named buffer when you need to. – Prince Goulash Sep 27 '12 at 17:49
  • yanking to a named buffer is an ok 2nd choice, but it requires forethought -- I have to know in advance that I'll want to reuse the text I just yanked or deleted. Also, if there's a lot of copy/pasting going on, it adds up to a lot of extra keystrokes. – Magnus Sep 29 '12 at 12:25
  • Why would you `xxxx` instead of `4x`? – Resigned June 2023 Sep 24 '16 at 17:45
  • 1
    @RadonRosborough because then I'd have to stop and count, whereas xxxx can be done heuristically - just keep hitting x til the undesired text is gone – Magnus Feb 02 '18 at 16:10

2 Answers2

10

OK, I got it -- this script in .vimrc lets me effectively toggle a "no buffer side-effects" mode whereby the d and x keys no longer overwrite the buffer when "no buffer side-effects" mode is activated.

Add this in .vimrc

function! ToggleSideEffects()
    if mapcheck("dd", "n") == ""
        noremap dd "_dd
        noremap D "_D
        noremap d "_d
        noremap X "_X
        noremap x "_x
        echo 'side effects off'
    else
        unmap dd
        unmap D
        unmap d
        unmap X
        unmap x
        echo 'side effects on'
    endif
endfunction
nnoremap ,, :call ToggleSideEffects()<CR>

Then to toggle in and out of this mode use the key combination ,, (two commas)

Magnus
  • 10,736
  • 5
  • 44
  • 57
5

I think trying to "turn-off" the side effects for every delete/change command would be overly difficult if not impossible. The basic ways to handle this situation:

  • Use the black hole ("_) register with your delete or change commands. e.g. "_dd
  • Use the "0 register which contains the most recent yank with your paste commands. e.g. "0p
  • Yanking the text to a named register. e.g. "ayy then later doing "ap

I personally lean toward the "0p approach as this is fits with how my mind works.

Now seeing you asked for no such work-arounds I have provided some functions that alter the paste commands to toggle between my so called paste_copy and nopaste_copy mode. nopaste_copy being Vim's default behavior. Put the following in your ~/.vimrc:

function! PasteCopy(cmd, mode)
  let reg = ""
  if exists('g:paste_copy') && g:paste_copy == 1 && v:register == '"'
    let reg = '"0'
  elseif v:register != '"'
    let reg = '"' . v:register
  endif
  let mode = ''
  if a:mode == 'v'
    let mode = 'gv'
  endif
  exe "norm! " . mode . reg . a:cmd
endfunction

command! -bar -nargs=0 TogglePasteCopy let g:paste_copy = exists('g:paste_copy') && g:paste_copy == 1 ? 0 : 1<bar>echo g:paste_copy ? '  paste_copy' : 'nopaste_copy'

nnoremap <silent> p :call PasteCopy('p', 'n')<cr>
nnoremap <silent> P :call PasteCopy('P', 'n')<cr>
nnoremap <silent> ]p :call PasteCopy(']p', 'n')<cr>
nnoremap <silent> [p :call PasteCopy('[p', 'n')<cr>
nnoremap <silent> ]P :call PasteCopy(']P', 'n')<cr>
nnoremap <silent> [P :call PasteCopy('[P', 'n')<cr>
vnoremap <silent> p :<c-u>call PasteCopy('p', 'v')<cr>
vnoremap <silent> P :<c-u>call PasteCopy('P', 'v')<cr>

You can toggle your paste_copy mode via :TogglePasteCopy. You may prefer a mapping like so

nnoremap <leader>tp :TogglePasteCopy<cr>

As a closing piece of advice I would highly suggest using "0p or using a named register over this approach as they are native to vim and there is one less "mode" to worry about.

Peter Rincker
  • 43,539
  • 9
  • 74
  • 101
  • Thanks for the above code, will test it out! I find the "0p approach inadequate because it only works when I've yanked the text, not when I've deleted it, which is often the case. And I hate using two different workflows for such similar actions. I guess a named buffer is the best vim can offer here. Is it maybe possible to statefully change vim's default buffer? That could maybe satisfy some of the mode-like characteristics I'm seeking. – Magnus Sep 28 '12 at 15:13
  • There is no way to change vim's default register that I am aware of outside of `'clipboard'`, which is no use in this situation. I believe the named register is probably the best approach for your workflow. My suggestion is try it out for a few weeks. Then think about ways to simplify your workflow. Maybe all you need is a quick mapping to the back hole register or some other named register. – Peter Rincker Sep 28 '12 at 15:36