Key binding in VIM - vim

How can I bind key and its options?
Example:
dw - deletes one word. d3w - deletes three words. I want to use eo to delete one word, and e3o to delete three words. I can bind d to e, but the problem is I cannot bind option/movement key (e.g w to o in this example).
I tried:
nnoremap e d|xnoremap e d
nnoremap eo dw|xnoremap eo dw
or
nnoremap o w|xnoremap o w
nnoremap e d|xnoremap e d

What you're looking for is "operator pending mappings". You can remap the movement keys to specific actions.
In your case what you need to do is remap d to e
:nnoremap e d
And then remap the "w" movement to "o"
:onoremap o w
With this, both eo and e3o work.
Check this guide for a detailed description https://learnvimscriptthehardway.stevelosh.com/chapters/15.html
BTW: not sure if it's the best practice to remap VIM keybindings for basic actions such as deleting/inserting etc. as it can possibly mess up other bindings - unless some very specific usecase, maybe consider learning the default ones.

Related

Vim shortcuts to select and copy the current line without the next line

I want to map the current behaviour of V to vv and have V copy the whole line except for the newline at the end. Same for Y.
You need these two lines:
nnoremap vv V
nnoremap V 0vg_

How to repeat a simple nonrmap in vim?

I am trying to write slightly enhanced version of VIM’s mark function, which uses the combination of m and any capital letter to mark a file.
What I want to implement is, instead of using only a single letter, set a mark allowing the combination of two letters, so that if I have files named, test_views, test_models, test_forms, I could use tv, tm, and tf, then don’t have to bother what to map to which file.
What I come up with is so far is:
nnoremap <some-prefix>tv :let tv = expand("%")<cr>
nnoremap <leader>tv :execute 'edit' tv<cr>
but couldn’t figure out to write this more effectively. (I shouldn’t repeat this 26 * 26 times, should I?).
Creating function should be one way, but even if I could manage to finish writing this, I don’t think the usability would meet what I expect.
nnoremap , :call StoreFile(k_map)
fun! StoreFile(k_map)
let k_map = expand("%”)
endfunl
Any suggestion would be appreciated.
You could create all those mappings with a couple of :help :for loop:
let alphabet = split('abcdefghijklmnopqrstuvwxyz', '\zs')
for first_key in alphabet
for second_key in alphabet
let pair = first_key . second_key
execute "nnoremap <Space>" . pair . " :<C-u>let " . pair . " = expand('%')<CR>"
execute "nnoremap <leader>" . pair . " :execute 'edit '" . pair . "<cr>"
endfor
endfor

Vim copy and paste line with a search and replace

Say I've written code that references the x dimension. What is the best way to get vim to duplicate a line of code replacing all references to x to y and to z (best being the most clear method).
Input:
length_x = X_vec.dot(X_vec)**.5
Desired Output:
length_x = X_vec.dot(X_vec)**.5
length_y = Y_vec.dot(Y_vec)**.5
length_z = Z_vec.dot(Z_vec)**.5
Here's my best so far.
function SwitchXtoYZ()
:normal yy
:normal p
:normal! V
:s/X/Y/ge
:normal! V
:s/x/y/ge
:normal p
:normal! V
:s/X/Z/ge
:normal! V
:s/x/z/ge
endfunction
command XtoYZ exec SwitchXtoYZ() | :normal `.
It works, but I feel this is not very vim-y. Bonus points if the cursor returns to where it was before the command XtoYZ was issued (it currently goes the beginning of the second inserted line).
You don't need a function to do that, a macro would be fine for your requirement. Also you can define a macro in your vimrc too, if you like, so that you can have it everytime you open vim.
here is the macro:
qqv<Esc>Y2p:s/x/y/gi<Enter>n:s//z/gi<Enter>`<q
so it was recorded and saved in register q, you can #q to replay it.
explain it a little:
qq " start recording into q
v<esc> " enter visual mode and exit. to let `< work
Y2p " yank current line and paste twice below
:s/x/y/gi<Enter> " x->y sub, case insensitive
n " go to next x (here we could use j too)
:s//z/gi<Enter> " do another sub x->z
`< " back to the old cursor position
q " end recording
if you want to X->Y and x->y, just remove the i flag and add two more :s
The : at the beginning of each line is optional, as are the :normal! V lines.
You are leveraging the Normal commands that you know, which is a good way to start, but IMHO you get cleaner code if you use more Command-mode (ex) commands and functions. I would do something like this:
function! SwitchXtoYZ()
let save_cursor = getpos(".")
copy .
s/X/Y/ge
s/x/y/ge
-copy .
s/X/Z/ge
s/x/z/ge
call setpos('.', save_cursor)
endfun
command! XtoYZ call SwitchXtoYZ()
:help function-list
:help getpos()
:help :call
:help :exec

Jump to the end of a long list of repeated pattern

I have a big file with a lot of lines that share the same pattern, something like this:
dbn.py:206 ... (some other text) <-- I am here
dbn.py:206 ... (some other text)
...
(something I don't know) <-- I want to jump here
Is there a quick way in Vim to jump to the place where the succession of dbp.py:206 ends?
/^\(dbn.py\)\#!
Matches first line which does not start with the text inside the escaped parentheses.
If you want quick access to this you could add a vmap which yanks the visually selected text and inserts it in the right spot (but first escaping it with escape(var, '/').
Try this vmap: vmap <leader>n "hy<Esc>/^\(<C-R>=escape(#h,'/')<CR>\)\#!<CR>
Press n when visually selecting the text you wish to skip and you should be placed on the next first line which does not begin with the selection.
I just write a function to select identical lines:
nnoremap vii :call SelectIdenticalLines()<CR>
fun! SelectIdenticalLines()
let s = getline('.')
let n = line('.')
let i = n
let j = n
while getline(i)==s && i>0
let i-=1
endwhile
while getline(j)==s && j<=line('$')
let j+=1
endwhile
call cursor(i+1, 0)
norm V
call cursor(j-1, 0)
endfun
type vii to select identical lines (feel free to change the key-binding)
type zf to fold them.
type za to toggle folding
It's handy when you want to squeeze several empty line.
It acts like C-x C-o in emacs.
One option is to go to the bottom of the file and search backwards for the last line you want, then go down one:
G ?^dbn\.py:206?+1

How to use abbreviations in Vim with arguments?

In Vim, you can make it so each time you write "FF" on insert mode changes to some code by using:
:iab FF for ( int i = 0 ; i < n ; i++ )
But is there any way to use this with arguments? Something like C's #defines, so if I write
FF(e, 10)
It becomes:
for ( int e = 0 ; e < 10 ; e++ )
Take a look at SnipMate (a vim plugin). You won't get arguments, but upon expansion of an abbreviation, it allows you to tab through modifiable areas. In the for example, you'll be brought to the i first, can edit it to be e, and it will change it to e in all areas of the for declaration. Then simply tab to the next area you'd like to change.
From the docs:
snipMate.vim aims to be an unobtrusive, concise vim script that implements some of TextMate's snippets features in Vim. A snippet is a piece of often-typed text that you can insert into your document using a trigger word followed by a .
For instance, in a C file using the default installation of snipMate.vim, if you type "for" in insert mode, it will expand a typical for loop in C:
for (i = 0; i < count; i++) {
}
To go to the next item in the loop, simply over to it; if there is repeated code, such as the "i" variable in this example, you can simply start typing once it's highlighted and all the matches specified in the snippet will be updated.
The following is a nice helpful change to remap tab and s-tab to c-d and c-a, in case you don't want to lose the functionality of tab (in ~/.vim/after/plugin/snipMate.vim):
"""ino <silent> <tab> <c-r>=TriggerSnippet()<cr>
"""snor <silent> <tab> <esc>i<right><c-r>=TriggerSnippet()<cr>
"""ino <silent> <s-tab> <c-r>=BackwardsSnippet()<cr>
"""snor <silent> <s-tab> <esc>i<right><c-r>=BackwardsSnippet()<cr>
"""ino <silent> <c-r><tab> <c-r>=ShowAvailableSnips()<cr>
ino <silent> <c-d> <c-r>=TriggerSnippet()<cr>
snor <silent> <c-d> <esc>i<right><c-r>=TriggerSnippet()<cr>
ino <silent> <c-a> <c-r>=BackwardsSnippet()<cr>
snor <silent> <c-a> <esc>i<right><c-r>=BackwardsSnippet()<cr>
ino <silent> <c-r><tab> <c-r>=ShowAvailableSnips()<cr>
You can include function definitions in abbreviations, but they cannot take arguments. This is an example from the vimdocs:
func Eatchar(pat)
let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c
endfunc
iabbr <silent> if if ()<Left><C-R>=Eatchar('\s')<CR>
I guess you could maybe parse the abbreviation expression in the function, but I'm not sure if you can also include characters like parenthesis in the abbreviation. Maybe something here will give you an idea.
Edit: You can always do something like this:
:iab for() for(int i = 0; i < ; i++)<C-o>T<
Which lacks the argument autocompletion of course but lets you start typing it immediately.
It worked for me:
iab FF <c-o>:FF
com -nargs=* FF call s:FF(<f-args>)
fu s:FF(i, n)
let t = "for (int a = 0; a < b; ++a) {\e"
let t1 = substitute(t, 'a', a:i, 'g')
exe 'normal! A'.substitute(t1, 'b', a:x, 'g')
exe "normal o\<space>\<BS>\e"
endf
at insert mode FF e 10<cr> will be for (int e = 0; e < 10; ++e) {<cr>.
mu-template support interactive templates. With it, you can either ask something to the user, or reuse any variable, apply computation on it if you which (detecting that i is already use in the current scope is doable), and use the result in the text you will expand.

Resources