Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vim toggling buffer overwrite behavior when deleting

Tags:

vim

vi

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
like image 797
Magnus Avatar asked Dec 11 '22 22:12

Magnus


2 Answers

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)

like image 58
Magnus Avatar answered Dec 28 '22 23:12

Magnus


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.

like image 36
Peter Rincker Avatar answered Dec 28 '22 23:12

Peter Rincker