Remap yank (y), Change (c) and Delete (d) - vim

I've been trying to extend the functionality of these three commands (y,c,d), so I'd have the content both in the system clipboard ("+ register) and in the normal registers.
This is what I've come up with so far:
nnoremap Y y$v$"+y$
nnoremap yy 0y$0v$"+y$
vnoremap y ygv"+y
" ------
nnoremap C y$v$"+c
nnoremap cc 0y$0v$"+c
vnoremap c ygv"+c
" ------
nnoremap D y$v$"+d$
nnoremap dd 0y$0v$"+d$
vnoremap d ygv"+d
" ------
And I have a few issues with this mapping:
For some odd reason, I have to resource my .vimrc before I can actually use these new bindings.
Trying to use only nmap, vmap or even map just didn't seem to work at all.
When I try to yank (for example) multiple lines (e.g. 2yy), it yanks the text to the "regular" register ("0) as expected, but yanks only the first line into the system clipboard.
I could'nt find a way to create this behaviour to y,c and d in normal-mode.
Can anyone help solve these issues?
I thought of creating a function, perhaps, so I could call it multiple times, but my vim script-fu is novice at best.

If you can do "+y, all these mappings are useless. Just add set clipboard+=unnamedplus to your ~/.vimrc.

Related

Vim mapping to surround word with a character

In my vimrc file I have the following entry to surround a word with '+':
:nnoremap <silent> q+ wbi+<ESC>ea+<Esc>
I would like to change this to surround a word with any other symbol, for example a quote, but I would not like to use another plugin or to insert one more map. I would like something like this in my vimrc:
:nnoremap <silent> qX wbiX<ESC>eaX<Esc>
where X would be the character to put around the word.
How can this be done in VIM?
Thanks
The general idea:
:nnoremap q :let c=nr2char(getchar())\|:exec "normal wbi".c."\eea".c."\e"<CR>
But I don't think that q is a good choice, especially for your example with quotes, because q starts the extremely handy recording feature from vim. The normal q command also does expect a second character (the recording target register) and it actually can be ". This will be quite confusing for you when you're on another computer or if someone else is using your vim. You should preprend a leader for q. With a leader:
:nnoremap <leader>q :let c=nr2char(getchar())\|:exec "normal wbi".c."\eea".c."\e"<CR>
The default leader value is \, but this can be changed. Note that it had to be changed before defining the mapping. You can see the currently configured leader with let mapleader (which prints an error message if there's no leader configured). With these statements,
:let mapleader=','
:nnoremap <leader>q :let c=nr2char(getchar())\|:exec "normal wbi".c."\eea".c."\e"<CR>
you can type ,q+ to surround your word with + and ,q" in order to surround your word with ".
By the way: Your mapping doesn't work if your cursor is on the last character on line. Change the mapping to:
:nnoremap <leader>q :let c=nr2char(getchar())\|:exec "normal viwo\ei".c."\eea".c."\e"<CR>
Use Tim Pope's surround.vim plugin!
To surround a word with + signs:
ysiw+

How do I swap two lines in vim?

I have this:
pick 887b66f add 222 Dziewiecsil to flowers new title
pick dc331cb new name of beginning commit
And I want to get this:
pick dc331cb new name of beginning commit
pick 887b66f add 222 Dziewiecsil to flowers new title
Is there a way to do this in a quick way using keyboard shortcuts?
To swap the current line with the next one, type ddp while in command mode.
dd - delete line (actually called cut in other editors) and save it in register
p - paste line from register
dd deletes the current line, then you can paste the removed line using p. There's another way though using m. With m you can move lines around i.e.
:m 1 will move the current line after line 1
:m 0 will move the current line to top
:m $ will move the current line to bottom
In your example, place the cursor in the first line and type :m $
More info: http://vim.wikia.com/wiki/Moving_lines_up_or_down
Example:
1. one
> 2. two
with :m-2, switch (current line - 2)
> 2. two
1. one
with :m+1 switch (current line + 1)
1. one
> 2. two
You could map this if you want.
Despite the fact that question is quite old and marked as answered, I'd like to extend the answer by saying that you can use normal mode commands, which were provided by Sven Marnach with nnoremap like so:
:nnoremap <C-Up> <Up>ddp<Up>
:nnoremap <C-Down> ddp
This will allow you to move lines with Ctrl + Up and Ctrl + Down within your file. However this will overwrite #" register, which stores your last copied string/word/letter/etc. So by adding "(reg) before dd and p commands we can fix this:
:nnoremap <C-Up> <Up>"add"ap<Up>
:nnoremap <C-Down> "add"ap
Here we add "a before delete and paste commands to store our line in #a register, so your default copy register will not be overwritten. However it may overwrite contents of #a register (who knows, but you may use it for something important in your use-case, but this step bit paranoid, you can skip it if you want to), let's fix that too:
:nnoremap <silent><C-Up> :let save_a=#a<Cr><Up>"add"ap<Up>:let #a=save_a<Cr>
:nnoremap <silent><C-Down> :let save_a=#a<Cr>"add"ap:let #a=save_a<Cr>
(<silent> needed to prevent echoing our commands to message-line at the bottom.)
Now we have two mappings which allow us to move lines within the file with keyboard shortcuts. You can redefine buttons, I use Alt + j/k, which would be <A-j> and <A-k> for those commands. However not all terminal emulators support Alt key mappings AFAIK.
The mappings proposed in the Vim wikia page are actually the best way to map key combinations that emulate the way Sublime and other editors implement this feature.
This includes an indentation action after the move, which is also great (that's the double equal == signs, in case you don't want that).
It also supports visual and insert modes, so that you can move lines while editing or with fulblocks.
nnoremap <A-j> :m .+1<CR>==
nnoremap <A-k> :m .-2<CR>==
inoremap <A-j> <Esc>:m .+1<CR>==gi
inoremap <A-k> <Esc>:m .-2<CR>==gi
vnoremap <A-j> :m '>+1<CR>gv=gv
vnoremap <A-k> :m '<-2<CR>gv=gv
I personally mapped them to <D-J> and <D-K> on my Mac, instead of <A- which maps to the Alt key. That way I use Cmd + Shift + j/k, which feels more natural to my fingertips.

vim toggling buffer overwrite behavior when deleting

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

Add support for `ci|` and `da|` (seleting text inside pipes)

I often use commands like ci( and di{ when editing source code.
Parameters in Ruby blocks are contained inside pipe characters, like |a, b|
Is it possible to extend this behavior to add support for |, so that commands like ci|, da| and yi| work properly?
I have the following in my vimrc (I have added the va| and vi| commands for completeness):
nnoremap di\| T\|d,
nnoremap da\| F\|d,
nnoremap ci\| T\|c,
nnoremap ca\| F\|c,
nnoremap yi\| T\|y,
nnoremap ya\| F\|y,
nnoremap vi\| T\|v,
nnoremap va\| F\|v,
The , operator repeats the previous F,f,T or t but in the opposite direction. A very useful key!
These mappings can be easily modified to support other delimiters; I use the $ versions all the time when editing LaTeX.
This is what I would use:
vnoremap <silent> a<bar> :<c-u>silent! normal! vF<bar>of<bar><cr>
vnoremap <silent> i<bar> :<c-u>silent! normal! vT<bar>ot<bar><cr>
onoremap <silent> a<bar> :normal va<bar><cr>
onoremap <silent> i<bar> :normal vi<bar><cr>
Basically setup a operator pending mode (that is the onoremap) which will call the appropriate visual mode mapping. The visual mode mappings will search backwards to find a | with F| then go to the other side of the visual selection via the o command, then search forwards with f| to select the other end of the piped area. The inner mappings are the same but instead of using the F and f commands you use T and t.
Sadly these mappings do not work correctly with the ., redo command as they inherently rely visual mode mappings which means the . command will execute the same command again but only for an area that take up the same amount of space.

vim: how to select pasted block

Is there a vim command to directly select a block of text which has just been pasted?
ps. I know about gv to reselect a block after exiting visual mode. It doesn't apply to this case.
If you want to select it just after paste (before you change anything else), use
nnoremap <expr> gV "`[".getregtype(v:register)[0]."`]"
. [ and ] marks point to start and end of the last change, v:register is set to the last register used (which is register used for the paste command unless you, for example, yank something), [0] selects only first byte of register type (it is required because for blockwise register it returns <C-v>{width}) and register type is one byte which is just the same as the keystroke you should use in normal mode to invoke visual mode.
I saw this solution somewhere on SO, you may want to search for it in order to get some alternatives.
In my case I have this map:
:nnoremap gp `[v`]
After more research I think the better solution is:
" https://vim.fandom.com/wiki/Selecting_your_pasted_text
nnoremap <expr> gp '`[' . strpart(getregtype(), 0, 1) . '`]'
I have had the following maps in my vimrc forever:
nnoremap <leader>p `[V`]
nnoremap <leader>[ `[V`]<
nnoremap <leader>] `[V`]>
They do the following:
visually select the recently pasted block
de-indent the recently pasted block
indent the recently pasted block
I probably use the indent ones even more than the selection one.

Resources