count of command get lost when do key mapping in vim - vim

I have a key mapping for p in my vimrc file as below:
noremap p <ESC>:set paste<CR>p:set nopaste<CR>
The purpose of this key mappinng is to make sure the content from outside of vim can be pasted in its original format in paste mode. And restore it to nopaste mode after paste complete.
But when I run the following commands:
yy
5p
p is only executed once other than 5 times.
It looks like the count is missed from the key mapping.
Any way to pass the count in the key mapping?
Or how to fix this problem?

The fisrt <ESC> drops the count so it is no available later. Instead of <ESC> we would ignore and save count with <c-u> and access it later with v:count1 variable like :<c-u>set paste <CR>... v:count1 ....
But there is another problem: count will be lost after the first <CR>, so we want to rewrite the mapping as a single command. Chaining of commands may be done with |, but in mappings we should write <BAR> instead of |.
Here is the final mapping:
:noremap p :<c-u>set paste <BAR> :exe "normal! " . v:count1 . "p" <BAR> :set nopaste<CR>
:exe "normal! " is a fancy way to execute a command from evaluated string.

Related

Vim: Using v:count1 as argument of a mapping

I have a mapping like below to move the cursor to the first column of input/update (the red-circled) area in a table. And I can type w or ww to get to the 2nd/3rd column if needed.
noremap <s-F8> 0f\|ew
It goes to the anchor | first, to the end of the green number, and moves to next word (can be a dot, a number or an expression) in the table as below.
I wonder if I can use the count given in front of a normal-mode command to get to the 2nd or 3rd column directly, with a command 2<s-F8> or 3<s-F8>.
The following code doesn't work, though the #= helps separate the count from the 0 command.
noremap <s-F8> #='0f\|ew'<cr>
I studied the answer of Karkat in the post Mapping with v:count in vim and made this mapping:
noremap <expr> <s-F8> '0f\|e' . v:count1 . 'W'
But it doesn't move if a count is given. (It does move to column 1 if there is no count.)
What would be the correct way to use v:count1 in such a mapping?
Use the :execute STRING command to include the v:count1 in the string:
noremap <silent> <s-F8> :<c-u>exe 'norm 0f\|e' . v:count1 . 'W'<cr>
Note: The CTRL-U is used to remove the range that Vim may insert.
For your <expr> version, you need an <esc> to help cancel the COUNT (something like the CTRL-U does):
noremap <silent> <expr> <s-F8> '<esc>0f\|e' . v:count1 . 'W'

How can I define a key mapping in vim and use the repeat number more than once?

I edit files with relative line numbers. Often I would like to copy a line from let's say 16 lines above to the current location.
In normal mode I would enter: 16kyy16jP
But when it is line 14, it is: 14kyy14jP
How can I define a key mapping/command to be able to enter something like 16LK or 14LK in normal mode to achieve the same result?
16kyy16jP
What a waste… You could use :help :t instead:
:-16t.
:-14t.
May be something like
nnoremap <silent> µ :<c-u>exe "normal! ".v:count1."kyy".v:count1."jP"<cr>
But, honestly, I'd use functions here as there is no need to move around that much:
nnoremap <silent> µ :<c-u>call append(line('.')-1, getline(line('.')-v:count1))<cr>
Note that the following also works thanks to :yank
nnoremap <silent> µ :<c-u>exe '-'.v:count1.'y'<cr>P
EDIT: I didn't know about :t, #romainl's answer (with #Kent's patch) makes more sense than mine. If you want a mapping it could be mode with:
nnoremap <silent> µ :<c-u>exe '-'.v:count1.'t-1'<cr>
" which isn't much different than the previous answer.
You can map a function call, which accepts input parameters.
function! YourMap(n)
exec 'normal! '.a:n.'-Y'.a:n.'+P'
endfunction
nnoremap <leader>c :call YourMap(input('lines:')) <CR>
You press <leader>c, then input the relative line numbers, the copy/paste should be done.
The <leader>c is the mapping key, you can change it into other key combinations.

Vim script: run an execute when mapping?

So I'm not sure how to go about running some some code in my mappings, like:
nnoremap <Return> :execute "normal! if 1 echo('one') endif"<cr>
Also tried it without the 'normal' - tried different combinations with separating the commands via '\' and '|' but nothing worked - keep getting variable not defined errors.
Any idea how?
EDIT:
So here's what I'm actually doing:
" Quickly toggle between insert/normal modes
nnoremap <A-e> i
inoremap <silent><A-e> <esc>:call GoRightIfNotBOL()<cr>
" Returns 1 if the cursor is at the beginning of a line "
function! IsBOL()
return col('.') == 1
endfu
function! GoRightIfNotBOL()
if !IsBOL()
execute "normal l"
endif
endfu
So instead of calling GoRightIfNotBOL I thought I could inline its code cause really, I can't think of another location where I would be using this function, and it's pretty small.
you are looking for <expr> mapping
read :h <expr> there you'll find examples.
If your codes were a bit long, put them in a function, and call that function in your mapping. It is more readable if you later want to do some change on it.
An example with inoremap <expr>:
inoremap <expr> <YourKeys> "<esc>".(col('.')>1?'l':'')

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.

Vim: how to create key mapping to :edit and jump to last line?

I mapped key F2 to refresh (:edit) currently opened file. I'm using this when watching a log file to update the screen when file has been updated outside (new lines added at the end of a log file).
nnoremap <silent> <F2> :edit<CR>
I would like to jump to the end of the file after it has been refreshed.
How do I create key mapping which does :edit and jump to end of the file (shortcut G) at the same time?
An idiomatic way to position the cursor in the just opened (or reopened) file
is to use the +-argument of the :edit command (see :help +cmd).
Although the general syntax allows to execute any command, there are special
cases for navigating to a certain line by a pattern matching text on that line
(+/ followed by the pattern), or by a line number (+ followed by the
number). If the number is omitted in the latter form, it is assumed to be the
last line of the file.
In such a way, to reload the current file positioning the cursor on the last
line, one can use the command
:edit +$
or
:edit + %
It is possible to shorten these commands by using :e instead of :edit and
leaving out an optional space before the +-argument.
:e+$
or
:e+ %
The corresponding mappings would take the form
:nnoremap <silent> <F2> :edit +$<CR>
and
:nnoremap <silent> <F2> :edit + %<CR>
Note that this +-argument syntax is also valid for opening a file from the
command line, so
$ vim + filename
works as well.
This is what I'd use:
nnoremap <silent><F2> :edit<bar>$<CR>
You can chain commands in a map by using <bar>. This mapping does what you want:
:nnoremap <silent> <F2> :edit <bar> :normal! G<enter>
It's important to use normal! instead of normal in mappings/scripts because the prior will not take user defined mappings into account. Even if there is a mapping for G in this case, vim will treat G as if it were not mapped at all.
You can use :normal to use the normal-mode G motion:
:nnoremap <silent> <F2> :edit<CR>:norm! G<CR>
Perhaps better would be to use the :$ command to go to the end of the file:
:nnoremap <silent> <F2> :edit<CR>:$<CR>
In Vim '|' can be used to separate commands, much like many flavors of Linux/Unix. For more information about the use of the bar check out :help bar
Example:
:edit | normal! G
If you wish to use this in a key mapping You may find that your ~/.vimrc doesn't like maps utilizing |, or \|. In order to make this work use the equivalent <bar> instead.

Resources