Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vim: overloaded mapping for multiple modes

Tags:

vim

I use mappings to normal mode commands that I'd also like to work in insert mode. This can be done by adding <C-o> to insert mode mapping:

nmap <C-Up> 10<Up>
imap <C-Up> <C-o>10<Up>

But this means repeating each mapping twice. To avoid repetition, I've tried to "overload" some other key, then use it for mode-specific part:

" F12 selects prefix suitable for current mode
nmap <F12> <Nop>
imap <F12> <C-o>
" single mapping relying on "overloaded" F12 
map <C-Up> <F12>10<Up>

For some reason, it doesn't work. F2 in insert mode just inserts <F2> as text. Any idea what's wrong and how to fix it? Bonus points if you can extend the solution to visual mode.

like image 938
rburny Avatar asked Dec 13 '12 00:12

rburny


4 Answers

Original


nnoremap <F2> :w<CR>
inoremap <F2> <Esc>:w<CR>a

map sometimes does not set it for all modes. I don't know the exact reason, so to be sure I like to explicitly set all mapping in my configuration file. I suggest that you do the same as there are cases where you can get something unexpected due to different modes. That's why it is important to consider every remapping that you do for each particular mode with care.

In addition, favor *noremap command instead of just *map everywhere you can as recursive mapping is a known source of errors, especially for beginners.

Lastly, I don't know what are you trying to achieve by binding writing of a file in visual mode. Are you aiming for partial buffer writing (it's when you selected something in visual mode, then hit this file-writing shortcut and only selected text is written)? Or do you want the whole file to be written when you are in visual mode, regardless of whether you selected anything or not when you hit the file-writing shortcut? Provide more information on that. Personally, in either case it is weird mapping for visual mode, as it is really not indented for that. It's rather better to keep such stuff in normal mode.

Update


As others have already given exhaustive answers on your question, I just thought that it would be helpful if add my 2 cents, but in slightly different direction. By looking on what you are trying to do, namely mapping navigation features involving arrow keys in insert mode, I can infer that you are very new to Vim. As you probably already know, the philosophy behind Vim is that you should never ever touch mouse during your work inside Vim - call it a kind of golden rule.

What I want to point out now, is what I call a silver rule, and it basically looks like this:

noremap <Up>    <Nop>
noremap <Down>  <Nop>
noremap <Left>  <Nop>
noremap <Right> <Nop>

inoremap <Up>    <Nop>
inoremap <Down>  <Nop>
inoremap <Left>  <Nop>
inoremap <Right> <Nop>

In other words, prevent yourself from using arrow keys (everywhere except command-line mode). Your fingers should always be only in the region of character keys. Vim is all about modes. Insert mode is not for navigation - it is intended for bursts of typing. When you work with code or just text (doesn't matter) you spend most of your time in normal mode - navigating - looking through the file, seeking where to land next in order to edit something, add something, i.e. to do your next input burst for which you switch to insert mode, and when you are finished you switch back to normal mode to look for some more meat - like a predator. :)

So what is it all about? I just want to head you to the right direction right from the beginning. This way you can become intermediate Vim user very quickly - just a few days. In order to get better feeling of all the aforementioned I suggest that you should definitely watch Vim Novice Video Tutorials by Derek Wyatt where he talks about all that stuff in more detail and shows it in action in the screencasts. There are also Intermediate and Advanced tutorials by him which you might also look when you are comfortable with the basics.

I wish you happy vimming! :)

like image 196
Alexander Shukaev Avatar answered Nov 18 '22 15:11

Alexander Shukaev


As ZyX has already pointed out, there is no single :map command for all modes, because it mostly doesn't make sense. If you really want to define a mapping for all modes, use both :map and :map!; see :help map-modes.

As you typically define mappings only once in your .vimrc, I would not worry too much about the little duplication, but if you do, you can use a wrapper function to avoid this:

function! MapBoth(keys, rhs)
    execute 'nmap' a:keys a:rhs
    execute 'imap' a:keys '<C-o>' . a:rhs
endfunction
call MapBoth('<C-Up>', '10<Up>')
like image 42
Ingo Karkat Avatar answered Nov 18 '22 15:11

Ingo Karkat


There are no commands to define mappings for all modes: :map maps for normal, operator-pending and visual modes (really visual and select at once) which is clearly stated in documentation. It does not make any sense to have same mapping for all modes, though unlike movement ones saving may be done in all modes with exactly the same rhs:

function s:Save()
    update
    return ''
endfunction
noremap  <expr> <F2> <SID>Save()
noremap! <expr> <F2> <SID>Save()

. noremap! is another multi-mode mapping command, it covers insert and command mode now. You can’t move the cursor from <SID>Save() function (textlock) thus this method is not applicable for cursor movement commands, but you can use variables in order not to repeat the same thing twice:

let s:tendownlhs='10j'
execute ' noremap <C-Down>      '.s:tendownlhs
execute 'inoremap <C-Down> <C-o>'.s:tendownlhs

. Now without command mode as this is tricky and likely useless.

like image 7
ZyX Avatar answered Nov 18 '22 16:11

ZyX


If it is okay for the mapping to end up in normal mode, you could combine a for loop with <C-\><C-n> mappings. <C-\><C-n> switches from any mode to normal mode.

For example, this allows switching panes with Alt-{h,j,k,l} from any mode:

for map_command in ['noremap', 'noremap!', 'tnoremap']
  execute map_command . ' <silent> <M-h> <C-\><C-n><C-w>h'
  execute map_command . ' <silent> <M-j> <C-\><C-n><C-w>j'
  execute map_command . ' <silent> <M-k> <C-\><C-n><C-w>k'
  execute map_command . ' <silent> <M-l> <C-\><C-n><C-w>l'
endfor
  • noremap maps in Normal, Visual, and Operator-pending mode
  • noremap! maps in Insert and Command mode
  • tnoremap maps in Neovim's Terminal mode
like image 3
Adriaan Zonnenberg Avatar answered Nov 18 '22 16:11

Adriaan Zonnenberg