vim - Ignore timoutlen for certain key-mapping macros - vim

I have made two simple nmaps in Vim that moves the cursor 10 times right and left by pressing Alt-z and Alt-Z
nmap <ESC>z 10l
nmap <ESC>Z 10h
It works perfect.
Now I would like to add two similar nmaps that delete 10 times
nmap d<ESC>z 10x
nmap d<ESC>Z 10X
This works almost fine, and follows the vim logic of operation followed by cursor move.
Unfortunately it times out if I don't press Altz or AltZ quickly after d.
I find this odd, because I have not mapped anything to just d. And if I press w or W after d, I can wait as long as I want.
I know there's a special thing about built-in maps such ad dw and dW.
To my question: Is there a way I can get my own nmaps to accept long delays between the key-presses?

You're right that by default, 'timeoutlen' (default 1 s) applies to mapped keys. I find this useful, but if you really want your started mappings to wait indefinitely, you have to define the map on the first key only, then query and handle the remaining keys yourself.
function! MapOrDefault()
let c = nr2char(getchar())
return c == "\<A-z>" ? '10x' : 'd' . c
endfunction
nnoremap <expr> d MapOrDefault()
Note: Though it's technically equivalent, I'd recommend to prefer the Vim key-notation (<A-z>) instead of the cryptic <Esc>z.
Note: You should use :noremap; it makes the mapping immune to remapping and recursion.

Related

5>> indents the next 4 lines including the current one. I want 5>> to indent the current line 5 times (vim, neovim specifically)

I thought about
noremap 5>> >>4.
But naturally, it's not a good idea to just do this for every different number
Maybe there's some way to do input (say, 5) and then that input repeats in the command. I don't know how to do that either
This is one way to do it:
nnoremap <key> <Cmd>call repeat('>>', v:count1)->feedkeys()<CR>
nnoremap <otherkey> <Cmd>call repeat('<<', v:count1)->feedkeys()<CR>
Press <key> to shift one time, 5<key> to shift five times, etc. It is up to you to choose <key> and <otherkey>. You could use >> and << but that would override their default functionality, which I don't think is a good idea.
:help <Cmd> makes it easy to execute built-in commands and functions from a mapping without unnecessary mode switching.
:help repeat() outputs the given string repeated the given time.
:help feedkeys() is a low-level way to simulate keystrokes.
:help v:count1 is the count you gave to the mapping.
Very personal mappings are the gateway drug to vimscript.

Vim: record macro entering insert mode via mapping

I'm trying to have remap i/c/o/a and friends to always record a macro. But the keypress that actually brings me into insert mode is not recorded (explained below).
For example, if we have:
nnoremap i qzi
inoremap <esc> <esc>q
Ideally, this means that when I enter insert mode (i), I also start recording to register z. And when I leave (<esc>), we stop recording. So register z should have the entire sequence needed to repeat what I did in insert mode.
But then if I try to type in insert mode (e.g., ihelloworld<esc>) and then check :registers a, I get
---Registers---
"z helloworld^[
Notice how the i command is not included -- only what i typed in insert mode is.
Why is this happening? What can I do to get around it?
I also noticed that if I just typed qzihelloworld<esc> normally, the z register DOES contain ihelloworld<esc> (with the i).
For those interested, I'm doing trying to do this because certain operations break 'repeat last command (the period key .)'. For example, if you use <c-o> or move around while insert-mode, . will only repeat the last segment of your insert mode. In particular, I'm trying to find a workaround for an autoclosing plugin
As #Carpetsmoker explains why it doesn't get recorded:
From :help q
The 'q' command is disabled while executing a register, and it doesn't work inside a mapping and :normal.
So that explains why it doesn't work ...
A solution:
As the command (e.g., i) in the mapping is not recorded, a solution is to prepend it to the recorded buffer when leaving insert mode.
To distinguish between i, a, o, etc, you can store the command in a variable, and then prepend that to the recorded macro:
nnoremap i :let b:mode="i"<cr>qzi
nnoremap a :let b:mode="a"<cr>qza
nnoremap o :let b:mode="o"<cr>qzo
inoremap <Esc> <Esc>q :let #z=":normal! ".b:mode.#z<CR>
Simply prepending i (etc.) to the macro doesn't work (for me) since it triggers the mapping for i. Calling it with normal! fixes that.
Another option is to modify the macro to enter insert using startinsert (combined with suitable movements to emulate i, a, o, etc.) by prepending the macro like this
:let #z=":startinsert^M".#z
caveat: Your rationale for wanting this:
For example, if you use <c-o> or move around while insert-mode, . will only repeat the last segment of your insert mode.
(my emphasis) needs the warning that "moving around" (with arrow keys) does not work (at least in my quick tests) without additional hacking, as the arrow keys send an <esc> ... sequence, triggering the end of the recording mapping for <esc>. c-o will work though, so you can move around (slowly) with e.g. <c-o> h (for moving left).
Another problem is that replaying it as a macro does not work (with this solution at least, perhaps due to the use of normal!) if you have newlines, etc., in the recorded macro. A solution to that is to execute the buffer instead of replaying it as a macro. That is, :exe #z instead of #z for instance with the mapping
nnoremap <space>z :execute #z<cr>

Eliminating lag when remapping 'd' key in VIM

I use a Dvorak keyboard layout, and so I've made a few adjustments to the default VIM mappings-- one change I've made is to remap the right-side home row keys to
noremap d h
noremap h j
noremap t k
noremap n l
So that movement keys are conveniently positioned, as they would be for a QWERTY user. h, t, and n do their job fine and are very responsive. d, however, lags for a moment before moving left the way its supposed to. I think this is because there are key-sequence commands that start with d (like dd for delete line), so VIM is waiting to receive the second stroke in the sequence before executing the command for just a single 'd'. I've remapped dd:
noremap dd hh
But this isn't doing the trick. Yet commands for things like d3w or d$ (text objects after d) aren't working (after remapping d), so they couldn't be causing the issue. Anyone know how to get rid of the lag? BTW, even after disabling all plugins I have the same issues. I also have no other mappings with d in my vimrc.
AFAIK, d, c and their "operator pending" friends are not really mappings. This means that you can't :unmap d. The delay you experience can't really be avoided without side effects, I believe.
You might want to play with :h timeout and :h timeoutlen.
For what it's worth, the alternative layouts/Vim story is… complicated. And not settled at all.

Map 'f' to PageUp in vi

I can map 'jj' to
imap jj <Esc>
and I can even map letters to tab navigation
map tj :tabprevious<CR>
map tk :tabnext<CR>
But I can't map g to page up (even though spacebar acts as page down)
map <Space> <PageDown>
map g <PageUp>
According to this "When you try to map multiple key sequences, you won't be able to start them with lower or upper case letters ("Too dangerous to map that"), but the punctuation and control characters are fair game." Can anyone confirm this?
If so, how does one assign a function to an unmapped key like 'g'
This isn't answering your question, but I thought it may be helpful to the problem you are having with your RSI. It maps the spacebar to toggle between fast and slow move modes. Normally pressing j or k will scroll down one line. Pressing space will turn on fast move mode, where pressing j or k will scroll down/up 10 lines. Press space again to go back to normal. This will only work in vim, not just plain vi (most "vi" programs are just symlinks to vim anyway though).
It works in both normal and visual edit modes.
To use it, put this code somewhere in your ~/.vimrc file:
map <Space> :call ToggleFastMoveMode()<CR>
vmap <Space> :call ToggleFastMoveMode()<CR>gv
let g:fastMoveMode = 0
function! ToggleFastMoveMode()
let g:fastMoveMode = 1 - g:fastMoveMode
if (g:fastMoveMode == 0)
noremap j j
vnoremap j j
noremap k k
vnoremap k k
else
noremap j 10j
vnoremap j 10j
noremap k 10k
vnoremap k 10k
endif
endfunction
(Edit - original answer suggested native Ctrl-f and Ctrl-b, but answer was updated as the goal here is to avoid using Ctrl and Shift)
A few points to add
Leaving the issue of choosing the right character to you, assuming we chose X for now.
I can think of two reasons why map X <PageUp> isn't working for you.
Your version of vi may not support PageUp/PageDown. If this is the issue then try instead to map to vi's page jumping (B for back, accompanied by for forward) eg. map X <C-b>.
Another other option is that it doesn't work 'as expected'. In vi PageUp/PageDown act on the 'viewport' not the cursor. So if you'r looking at the top of the file, but the cursor is not at the top or won't do anything. PageDown won't 'work' if your cursor is two lines from the bottom either.
To address this you could combine the 'move viewport up' <C-b> and the 'move cursor to the top of viewport' H eg. map X <C-b>H (The opposite being map X <C-f>L). Or specifying the number of lines to jump yourself map X 30k (Op. map X 30j).
Then the issue of choosing the right character to overwrite. Vi has a lot of native commands, so many in fact that only a handful of characters don't do something natively.
So if your goal is to avoid RSI, then of course overwrite something. But make sure to overwrite something that isn't too useful for you personally.
Natively:
f searches for a given symbol on the line you are currnetly on (can be very useful, but not critical I guess)
g on it's own does nothing, but gg moves cursor to top of file. Choosing g may cause issus as vim (not the original vi) will interpret two quick keypresses as go to top of file instead of do two PageUp's.

What is the difference between the remap, noremap, nnoremap and vnoremap mapping commands in Vim?

What is the difference between the remap, noremap, nnoremap and vnoremap mapping commands in Vim?
remap is an option that makes mappings work recursively. By default it is on and I'd recommend you leave it that way. The rest are mapping commands, described below:
:map and :noremap are recursive and non-recursive versions of the various mapping commands. For example, if we run:
:map j gg (moves cursor to first line)
:map Q j (moves cursor to first line)
:noremap W j (moves cursor down one line)
Then:
j will be mapped to gg.
Q will also be mapped to gg, because j will be expanded for the recursive mapping.
W will be mapped to j (and not to gg) because j will not be expanded for the non-recursive mapping.
Now remember that Vim is a modal editor. It has a normal mode, visual mode and other modes.
For each of these sets of mappings, there is a mapping that works in normal, visual, select and operator modes (:map and :noremap), one that works in normal mode (:nmap and :nnoremap), one in visual mode (:vmap and :vnoremap) and so on.
For more guidance on this, see:
:help :map
:help :noremap
:help recursive_mapping
:help :map-modes
I think the Vim documentation should've explained the meaning behind the naming of these commands. Just telling you what they do doesn't help you remember the names.
map is the "root" of all recursive mapping commands. The root form applies to "normal", "visual+select", and "operator-pending" modes. (I'm using the term "root" as in linguistics.)
noremap is the "root" of all non-recursive mapping commands. The root form applies to the same modes as map. (Think of the nore prefix to mean "non-recursive".)
(Note that there are also the ! modes like map! that apply to insert & command-line.)
See below for what "recursive" means in this context.
Prepending a mode letter like n modify the modes the mapping works in. It can choose a subset of the list of applicable modes (e.g. only "visual"), or choose other modes that map wouldn't apply to (e.g. "insert").
Use help map-modes will show you a few tables that explain how to control which modes the mapping applies to.
Mode letters:
n: normal only
v: visual and select
o: operator-pending
x: visual only
s: select only
i: insert
c: command-line
l: insert, command-line, regexp-search (and others. Collectively called "Lang-Arg" pseudo-mode)
"Recursive" means that the mapping is expanded to a result, then the result is expanded to another result, and so on.
The expansion stops when one of these is true:
the result is no longer mapped to anything else.
a non-recursive mapping has been applied (i.e. the "noremap" [or one of its ilk] is the final expansion).
At that point, Vim's default "meaning" of the final result is applied/executed.
"Non-recursive" means the mapping is only expanded once, and that result is applied/executed.
Example:
nmap K H
nnoremap H G
nnoremap G gg
The above causes K to expand to H, then H to expand to G and stop. It stops because of the nnoremap, which expands and stops immediately. The meaning of G will be executed (i.e. "jump to last line"). At most one non-recursive mapping will ever be applied in an expansion chain (it would be the last expansion to happen).
The mapping of G to gg only applies if you press G, but not if you press K. This mapping doesn't affect pressing K regardless of whether G was mapped recursively or not, since it's line 2 that causes the expansion of K to stop, so line 3 wouldn't be used.
I will explain mapping commands simply.
First, we have two general mapping commands:
map - works recursively in normal, visual, select and operator pending modes.
map! - works recursively in insert and command-line modes.
The non-recursive variations of these commands are:
noremap - works non-recursively in normal, visual, select and operator pending modes.
noremap! - works non-recursively in insert and command-line modes.
You can think of it as no[remap] {lhs} {rhs} which means to map the key sequence {lhs} to {rhs}, but do not re-map any commands in {rhs} to avoid nested and recursive mappings.
Then, we have mode-specific commands:
nmap - works recursively in normal mode.
imap - works recursively in insert mode.
vmap - works recursively in visual and select modes.
xmap - works recursively in visual mode.
smap - works recursively in select mode.
cmap - works recursively in command-line mode.
omap - works recursively in operator pending mode.
And their non-recursive variations:
nnoremap - works non-recursively in normal mode.
inoremap - works non-recursively in insert mode.
vnoremap - works non-recursively in visual and select modes.
xnoremap - works non-recursively in visual mode.
snoremap - works non-recursively in select mode.
cnoremap - works non-recursively in command-line mode.
onoremap - works non-recursively in operator pending mode.
Finally, remap is a boolean option that allows for mappings to work recursively. It is worth mentioning that you should always keep this option at the default on.
One difference is that:
:map does nvo == normal + (visual + select) + operator pending
:map! does ic == insert + command-line mode
as stated on help map-modes tables.
So: map does not map to all modes.
To map to all modes you need both :map and :map!.
In Vim, the mapping commands remap, noremap, nnoremap and vnoremap are used to define key mappings that allow you to execute a series of Vim commands by pressing a single key or key combination. Here's a brief overview of the differences between each of these mapping commands:
remap: This command allows you to remap an existing mapping to a new mapping. For example, if you want to remap the "jk" sequence to escape in insert mode, you can use the following command:
:remap jk <Esc>
This will replace the "jk" sequence with the "Esc" key.
noremap: This command creates a non-recursive mapping. This means that any mappings that are defined in the new mapping will not be expanded. For example, if you have the following mappings:
:map a b
:map b c
And you use the following command:
:noremap a b
This will create a non-recursive mapping for "a" that directly maps to "b" without expanding any other mappings.
nnoremap: This command creates a non-recursive mapping for normal mode only. This means that the mapping will only apply in normal mode, and any mappings that are defined in the new mapping will not be expanded. For example, if you have the following mappings:
:map a b
:map b c
And you use the following command:
:nnoremap a b
This will create a non-recursive mapping for "a" that directly maps to "b" without expanding any other mappings, but it will only apply in normal mode.
vnoremap: This command creates a non-recursive mapping for visual mode only. This means that the mapping will only apply in visual mode, and any mappings that are defined in the new mapping will not be expanded. For example, if you have the following mappings:
:map a b
:map b c
And you use the following command:
:vnoremap a b
This will create a non-recursive mapping for "a" that directly maps to "b" without expanding any other mappings, but it will only apply in visual mode.
It's important to note that when creating mappings, especially recursive mappings, it's possible to create unintended consequences or "mapping conflicts" that can cause issues when trying to use Vim. Careful attention to the mappings being created, especially when modifying the behavior of commonly used keys, is recommended to avoid unwanted side effects.
In general, noremap, nnoremap, and vnoremap are considered safer to use than remap, as they don't create recursive mappings and thus are less likely to cause unintended side effects. However, there may be situations where a recursive mapping is necessary, in which case remap can be used to overwrite an existing mapping with a new one.
It's also worth noting that there are other mapping commands in Vim, such as imap for insert mode mappings and map! for command-line mappings. Each of these commands behaves similarly to the mapping commands described above, but applies to a specific mode in Vim.

Resources