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
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 thevnoremap p "0p
,vnoremap P "0P
, andset clipboard=unnamed
(for OSX)vnoremap p "_dP
removes white space on paste. Edit 1 works perfectly.