How do I configure matchit.vim to use <tab> instead of %? - vim

I'm a big fan of the matchit.vim plugin, but I prefer to jump between matching delimiters with the <tab> key. However, it seems that matchit is hard-coded to activate when pressing the % key.
My first thought would be that I would simply put this line in my .vimrc, and change '%' to '<tab>', thus binding the Match_wrapper call to the tab key:
nnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'n') <CR>
However, this doesn't seem to work; I'm guessing it has got something to do with the <SID> (which as far as I understand is an ID unique to the script?) or the fact that Match_wrapper is script-local. (I'm pretty new to Vimscript)
Thus far I've managed to get by mapping <tab> to % with 'nmap', but it's a pretty fragile hack.
Anyway, any help would be greatly appreciated! :)

Well, if you know that % will always be remapped, then using
map <Tab> %
is safe (absence of n in front is intentional: % is defined in all modes covered by :map). But what you can always do is to replace <SID> with <SNR>{N}_ where {N} is the number of the matchit script in the outputs of :scriptnames. In a newer vim you can also use maparg('%', 'n', 0, 1), it will output a dictionary that among other values contains lhs and sid. In this case code may look like this:
for s:mode in ['n', 'v', 'o']
let s:map=maparg('%', s:mode, 0, 1)
execute s:mode.'noremap <Tab> '.substitute(s:map.lhs, '<SID>', '<SNR>'.s:map.sid.'_', 'g')
endfor
In this case
for s:mode in ['n', 'v', 'o']
execute s:mode.'noremap <Tab> '.maparg('%', s:mode)
endfor
is also acceptable as “old” (without fourth argument) behavior of maparg is to expand <SID>.

Here's what I did:
" <C-I> and <TAB> are the same thing.
" So, I changed <C-I> to <C-O><C-I> and <C-O> to <C-O><C-O> to match.
" I didn't want to lose the <C-I> jump functionality.
noremap <C-O><C-O> <C-O>
noremap <C-O><C-I> <C-I>
" This is what the plugin sets on %. I just set it on <TAB>
onoremap <TAB> :<C-U>call <SNR>41_Match_wrapper('',1,'o')<CR>
nnoremap <TAB> :<C-U>call <SNR>41_Match_wrapper('',1,'n')<CR>
vnoremap <TAB> :<C-U>call <SNR>41_Match_wrapper('',1,'v')<CR>m'gv``

Or, just in case, you can also use these mapping (tested with Vim 8.0):
nnoremap <silent> <Tab> :normal %<CR>
xnoremap <silent> <Tab> :normal %<CR>m`gv``

Related

vim mapping problem in vimrc file but can not find the mapping that I set

I am using vim-markdown-toc plugin(successfully installed) and want to remapping some hotkey to specific function. I export this code autocmd Filetype markdown noremapb <silent> <C-x> :GenTocMarked to my .vimrc file. But when I type :verbose imap <C-x>, it shows can not find the mapping.
Can anyone tell me what's the problem about this?
and also I also want to ask how to map one hotkey to multiple function?
autocmd Filetype markdown noremapb <silent> <C-x> :GenTocMarked
has two obvious errors:
noremapb should be noremap, without the b:
noremap <silent> <C-x> :GenTocMarked
There should be a <CR> at the end:
noremap <silent> <C-x> :GenTocMarked<CR>
The right-hand-side of a mapping is a macro: since you press <CR> to execute the command :GenTocMarked, it should be present in the RHS.
Then comes the diagnostic mistake: the :map command, and its non-recursive buddy :noremap create mappings for normal, visual, and operator-pending modes, but :imap prints out insert mode mappings so you can't really expect it to find a mapping created with :map.
Then comes the semantic mistake: the re in noremap is part of nore (short for non-recursive), not of remap. <C-x> is not a mapping so you are not "remapping" anything.
Then comes the scoping mistake: :noremap creates mappings for three modes, which is something you probably don't want. You should be more specific:
" normal mode mapping
nnoremap <silent> <C-x> :GenTocMarked<CR>
and, finally, the autocommand abuse mistake: there already is a built-in mechanism for sourcing filetype-specific config so there is no need to reinvent the wheel in your vimrc:
" in after/ftplugin/markdown.vim
nnoremap <buffer> <silent> <C-x> :GenTocMarked<CR>

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.

Move current line (up/down) to one position in Vim

Sometimes I want to swap current line with line up or below in vim. I can do it with commands :m+1 or :m-1. However it is too wordy. Is there shorter way doing the same?
give this a try:
ddp and ddkP
if it gives what you want. ;)
Both Tim Pope's unimpaired.vim - Pairs of handy bracket mappings and my own LineJuggler plugin provide (among others; my plugin has a focus on line moves and copies, whereas Tim's has a mixture of useful stuff) [e and ]e mappings to move the current line / selection above or below. These don't clobber the default register, as ddp et al. would do.
Give mappings a chance:
nnoremap <leader>k :move-2<CR>==
nnoremap <leader>j :move+<CR>==
xnoremap <leader>k :move-2<CR>gv=gv
xnoremap <leader>j :move'>+<CR>gv=gv
Vim has the :move command that allows you to move one line.
For instance, :m +1 will move the current line down.
I have these mappings in my .vimrc :
" move the lines of visual mode up or down
" JK to move and keep a correct indentation (with =)
" <up><down> to move keeping the correct indentation
vnoremap <silent> J :m '>+1<cr>gv=gv
vnoremap <silent> <down> :m '>+1<cr>gv
vnoremap <silent> K :m '<-2<cr>gv=gv
vnoremap <silent> <up> :m '<-2<cr>gv
With these lines, if you select a bunch of lines in visual mode, and then press <up> or <down> arrows, the lines will be moved up or down (and you will stay in the same visual selection thanks to the gv at the end).
J and K are almost the same, but they keep and autoindentation, using the = operator (gv= autoindents the last visual selection).
For sure, i encourage you to do modify the keys that are mapped to your own preferences. These are just mine. Also, copy-pasting without understanding is probably a bad idea. If you understand that mapping, you could check help pages for :m, gv and =.

how can i intuitively move cursor in vim?(not by line)

if some lines are too long, it will be forced to be newlined.
for example, normally a long line will looks like this
1 first line
2 this is the long second line of the file
3 third line.
but, if the window of a vim are too narrow, it will looks like this
1 first line
2 this is the long
second line of the file
3 third line
the problem arise from this.
let's assume the vim cursor are located at before 't' in 'third line'. if i type 'k', cursor will move to before 's' in 'second line of the file'. after that, if i type 'k' again, cursor will move to 'f' in 'first line'!, not 't' in 'this is the long'. what i want is that the cursor move to 't' in 'this is the long', it is more intuitive process for me. how can set my vim to works like this?
In Vim, the gj and gk commands move by line on the screen rather than by line in the file. This sounds like it probably matches your description.
You can modify your keys like this:
:map j gj
:map k gk
No, if some lines are too long and you have set wrap on they will be shown on "two lines", so to say, but there won't be a newline character between them. If you turn off wrap with set nowrap you'll see the effect.
Normally, k and j move you up and down. If you want to navigate wrapped lines use gk or gj, or just as some like it, map it to for example, the cursor keys.
nmap <up> gk
nmap <down> gj
To move in vim in a natural way is possible.
What I did was, and I suggest you, to modify (or create) your "~/.vimrc" and add these two lines:
map <C-Up> g<Up>
map <C-Down> g<Down>
This will map you control-up and control-down to the movements commands (this is coherent with control-right and control-left to move around long lines)
If you add these other two lines, you can use the same command to move in insertmode:
imap <C-Up> <C-[> g<Up> i
imap <C-Down> <C-[> g<Down> i
(VIM is great !)
Greg Ruo
This answer is derived from #mario-rossi 's answer (Kudo to him), with minor midification.
I use the normal UP and DOWN arrow key, rather than CTRL+up and CTRL+down. And somehow I need to remove one excess space in the INSERT mode mapping to avoid an off-by-one behavior.
Put the following into your ~/.vimrc:
" When a long line is wrapped, the "gk" and "gj" allow you to move up and down
" a visual line, while normal "k" and "j" move a physical line.
" The following settings map "gk" and "gj" to cursor <up> and <down>.
map <up> gk
map <down> gj
" And the following lines enables same <up> and <down> behavior in INSERT mode
imap <up> <C-[> <up>i
imap <down> <C-[> <down>i
Took this from vim.fandom.com:
There are several cases to remap up and down movements. First, you probably should remap both k/j and / arrows. Second, you should coose vim modes that requires remap. Basic modes are Normal (nmap), Insert (as after i command, imap) and Select (as after v command, vmap). To remap all three:
nnoremap j gj
nnoremap k gk
vnoremap j gj
vnoremap k gk
nnoremap <Down> gj
nnoremap <Up> gk
vnoremap <Down> gj
vnoremap <Up> gk
inoremap <Down> <C-o>gj
inoremap <Up> <C-o>gk
There's also one significant Operator mode (as in dj or, to say, y4k), remapping operator (omap) breaks vim experience/habits dramatically and not recommended.
Personally I prefer to remap Insert mode only, to keep my editorial habits intact.

Vim: search and highlight but do not jump

The super star (*) key in Vim will search for the word under the cursor and jump forward to the next match. The user can jump to the next matches with the n key. If hlsearch is enabled, it will also highlight the matches.
I want to be able to press * and get the highlighted matches and be able to navigate the matches using the n key. However, I do not want Vim to jump to the next match when * is pressed, it should remain on the current word. Is there a way to do this?
I would map:
nnoremap * *``
Works exactly like you want, except that it adds a jump in the jump list. To prevent that you need:
nnoremap * :keepjumps normal! mi*`i<CR>
I found this works pretty well, there's no blink and it doesn't need an intermediate register.
nnoremap <silent> * :let #/= '\<' . expand('<cword>') . '\>' <bar> set hls <cr>
Or if you want the g* behavior:
nnoremap <silent> g* :let #/=expand('<cword>') <bar> set hls <cr>
The best solution:
don't add a jump to the jump list
the behavior of the star key is not be changed
so, try the plugin:
http://www.vim.org/scripts/script.php?script_id=4335
Much better than:
" a jump adds to the jump list
nnoremap * *``
" I got a dead loop on macvim
nnoremap * :keepjumps normal *``<cr>
" the behavior is changed
nnoremap <silent> <Leader>* :let #/='\<<C-R>=expand("<cword>")<CR>\>'<CR>:set hls<CR>
I haven't seen this one yet:
nmap <silent> * "syiw<Esc>: let #/ = #s<CR>
It's very short and does not involve jumping around which can result in blinking.
Explanation: copy the word under cursor to s register and then set the search register (/) to the content of s register. The search register is not writeable directly, that's why the let is necessary and hence the silent to keep vim's command line clean.
I have the following in my .vimrc, which I think works better than the other alternatives:
" Put word under cursor into search register and highlight
nnoremap <silent> <Leader>* :let #/='\<<C-R>=expand("<cword>")<CR>\>'<CR>:set hls<CR>
vnoremap <silent> <Leader>* :<C-U>
\let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
\gvy:let #/=substitute(
\escape(#", '/\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR>
\gV:call setreg('"', old_reg, old_regtype)<CR>:set hls<CR>
If you want to keep the current view and add the search to the history, try this [not so efficient] solution:
noremap * msHmt`s*`tzt`s
It is using the marks s (save) and t (top).
A simple solution came to my mind: put map * *# in .vimrc file (it will blink though).
Many answers here outline rather simple mappings that work well for common cases, but may have side effects (like flickering by jumping back and forth) or lack robustness (may break if some regexp characters are defined as keyword characters).
If you're looking for a robust implementation and don't mind installing a plugin, you can choose from a plethora of alternatives, many of which also offer additional search-related improvements:
My SearchHighlighting plugin changes the * command, extends it to visual selections, and offers optional auto-searching of the word under the cursor.
star search changes the behavior of * to not jump to the next match, and includes the visual search from the next plugin
vim-visual-star-search provides searching of the visual selection
visualstar.vim provides searching of the visual selection
select & search can use either n/N or * in the visual selection, and can avoid jumping.
vim-asterisk provides a z* mapping that also doesn't jump, visual *, more intuitive smartcase handling, and can keep the cursor position when jumping (like ,*)
searchant.vim hooks into the built-in search commands and provides a separate highlighting for the match last jumped to.
The other answers here are good, particularly #rodrigo's, but I wanted to write a solution that preserves scroll position and does so without affecting any of the marks.
This works for me:
function! StarPositionSave()
let g:star_position_cursor = getpos('.')
normal! H
let g:star_position_top = getpos('.')
call setpos('.', g:star_position_cursor)
endfunction
function! StarPositionRestore()
call setpos('.', g:star_position_top)
normal! zt
call setpos('.', g:star_position_cursor)
endfunction
nnoremap <silent> * :call StarPositionSave()<CR>*:call StarPositionRestore()<CR>
Putting normal! * in the function directly doesn't seem to work, as (at least in neovim) it suppresses search highlighting from being triggered (as if :nohlsearch was run).
My solution:
nnoremap <silent><expr> * v:count ? '*'
\ : ':silent execute "keepjumps normal! *" <Bar> call winrestview(' . string(winsaveview()) . ')<CR>'
nnoremap <silent><expr> g* v:count ? 'g*'
\ : ':silent execute "keepjumps normal! g*" <Bar> call winrestview(' . string(winsaveview()) . ')<CR>'
Edit: Recent Vim has <Cmd> mapping, so you can use below to avoid CmdlineEnter/Leave to be fired.
nnoremap <expr> * v:count ? '*'
\ : '<Cmd>silent keepjumps normal! *<CR><Cmd>call winrestview(' .. string(winsaveview()) .. ')<CR>'
nnoremap <expr> g* v:count ? 'g*'
\ : '<Cmd>silent keepjumps normal! g*<CR><Cmd>call winrestview(' .. string(winsaveview()) .. ')<CR>'
Pros:
No flickering.
Jump list remains unchanged.
With a count, it invokes original *; you can use 1* if you miss the original *.
Marks and registers are untouched.
Using original *, its behavior is almost identical with * (except for jumping).
This means 'smartcase' is ignored as * do.
No need to install plugins.
Similar to * we have
[I ..................... it shows where the word under the cursor appears
I also have some useful lines on my vimrc that can, maybe, help you
" When double click a word vim will hightlight all other ocurences
" see CountWordFunction()
" [I shows lines with word under the cursor
nnoremap <silent> <2-LeftMouse> :let #/='\V\<'.escape(expand('<cword>'), '\').'\>'<cr>:set hls<cr>:CountWord<cr>
nnoremap <Leader>* :let #/='\V\<'.escape(expand('<cword>'), '\').'\>'<cr>:set hls<cr>:CountWord<cr>
if !exists('*CountWordFunction')
fun! CountWordFunction()
try
let l:win_view = winsaveview()
exec "%s/" . expand("<cword>") . "//gn"
finally
call winrestview(l:win_view)
endtry
endfun
endif
command! -nargs=0 CountWord :call CountWordFunction()
cnoreabbrev cw CountWord
nnoremap <F3> :CountWord<CR>
I'm adding this answer because I found the other answers either, centered the line in the view (which I found distracting), or seemed overly complicated.
The following command works well for me:
noremap * :let #/ = "\\<<C-r><C-w>\\>"<cr>:set hlsearch<cr>
It simply sets the pattern to the whole word under the cursor and then turns on (or updates) the highlighting for the search pattern.
NOTE: It doesn't modify the search history (which I prefer but might not be quite what you want).
just do
nnoremap * *N
nnoremap # #n
works with this http://www.vim.org/scripts/script.php?script_id=4335 too like so:
vnoremap * :<C-u>call VisualStarSearchSet('/')<CR>/<C-R>=#/<CR><CR>N
vnoremap # :<C-u>call VisualStarSearchSet('?')<CR>?<C-R>=#/<CR><CR>n
nnoremap <silent> ml :<c-u>let #/ = '\<'.expand('<cword>').'\>'\|set hlsearch<CR>wb
and if you want a visual mode one:
function SetSearchVisualSelection()
let clipboard_original_content=#"
normal gvy " this overwrites clipboard
let raw_search=#"
let #/=substitute(escape(raw_search, '\/.*$^~[]'), "\n", '\\n', "g")
let #"=clipboard_original_content
endfunction
vnoremap ml :call SetSearchVisualSelection()<CR>:set hlsearch<CR>

Resources