Regarding the recursive mapping of Vim - vim

Here is the code that is a practice for writing vim plugin. I write it as per the vim docs: :help usr_41.txt section 41.11 write a plugin.
let s:save_cpo = &cpo
set cpo&vim
if exists("g:loaded_echoplugin")
finish
endif
function s:EchoWord()
echo expand('<cword>')
endfunction
if !exists(":EchoWord")
command -nargs=0 EchoWord :call s:EchoWord()
endif
if !hasmapto('<Plug>EchoWord')
map <F8> <Plug>EchoWord
endif
noremap <script> <Plug>EchoWord <SID>EchoWord
noremap <SID>EchoWord :call <SID>EchoWord()<CR>
let g:loaded_echoplugin = 1
let &cpo = s:save_cpo
unlet s:save_cpo
The code is for displaying the word under the cursor when clicking <F8>. Here is the mapping sequence: <F8> -> <Plug>EchoWord -> <SID>EchoWord -> :call <SID>Echoword() and it works.
Here, however, I have 2 questions:
1. I have used the noremap here, why it is still able to remap or recursive mapping?
2. If I change the mapping from map <F8> <Plug>EchoWord to noremap <F8> <Plug>EchoWord, it will do NOT work.
Could anybody please help to figure it out? thanks!

<Plug>Foo is a mapping. Whether it is itself recursive or not doesn't matter.
When you do a recursive mapping, Vim uses whatever is the current meaning of the commands in the right hand side:
map b B
map <key> db " works like dB
When you do a non-recursive mapping, Vim uses the original meaning of the commands in the right hand side:
map b B
noremap <key> db " works like db
<Plug>Foo doesn't mean anything by default so there's no point mapping it non-recursively.
You want recursiveness, here, so you are supposed to use map, imap, nmap, etc.

I have read the vim docs for the related command carefully, and I have found the root cause. I add it here just for anybody who may concern.
Type the command :help :map-<script> and here is the reason:
Note: ":map <script>" and ":noremap <script>" do the same thing. The
"<script>" overrules the command name. Using ":noremap <script>" is
preferred, because it's clearer that remapping is (mostly) disabled.

Related

Vim inline remap to check first character

I am trying to do a comment remap in Vim with an inline if to check if it's already commented or not. This is what I have already and of course it's not working ha ha:
imap <c-c> <Esc>^:if getline(".")[col(".")-1] == '/' i<Delete><Delete> else i// endif
What I want to do is check the first character if it's a / or not. If it's a / then delete the first two characters on that line, if it's not a / then add two // in front of the line.
What I had originally was this:
imap <c-c> <Esc>^i//
And that worked perfectly, but what I want is to be able to comment/uncomment at a whim.
I completely agree with #Peter Rincker's answer warning against doing this in insert mode, and pointing you to fully-featured plugins.
However, I couldn't resist writing this function to do precisely what you ask for. I find it easier to deal with this kind of mapping with functions. As an added bonus, it returns you to insert mode in the same position on the line as you started (which has been shifted by inserting or deleting the characters).
function! ToggleComment()
let pos=getpos(".")
let win=winsaveview()
if getline(".") =~ '\s*\/\/'
normal! ^2x
let pos[2]-=1
else
normal! ^i//
let pos[2]+=3
endif
call winrestview(win)
call setpos(".",pos)
startinsert
endfunction
inoremap <c-c> <Esc>:call ToggleComment()<CR>
Notice the modifications to pos to ensure the cursor is returned to the correct column. The command startinsert is useful in this type of function to return to insert mode. It is always safer to use noremap for mappings, unless there is a very good reason not to.
This seems to work well, but it is not very Vim-like, and you might find other plugins more flexible in the long run.
There are many commenting plugins for vim:
commentary.vim
tComment
EnhCommentify
NERD Commenter
and many more at www.vim.org
I would highly suggest you take a look at some these plugins first before you decide to roll your own. It will save you great effort.
As a side note you typically would want to comment/uncomment in normal mode not insert mode. This is not only the vim way, but will also provide a nicer undo history.
If you are dead set on creating your own mappings I suggest you create a function to do all the hard work and have your mapping call that function via :call. If you think you can get by with simple logic that doesn't need a function then you can use an expression mapping (see :h map-<expr>). You may want organize into a plugin as it could be large. If that is the case look at :h write-plugin to give you a feel
for writing plugins the proper way.
Example of a simple expression mapping for toggling comments:
nnoremap <expr> <leader>c getline(".") =~ '\m^\s*\/\/' ? '^"_2x' : 'I//<esc>`['
there's also this vimtip! http://vim.wikia.com/wiki/Comment/UnComment_visually_selected_text
i use the bottom one with the
...
noremap <silent> ,c :<C-B>sil <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:noh<CR>
noremap <silent> ,u :<C-B>sil <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:noh<CR>
,c comments out a region
,u uncomments a region

Vim: Resolve ambiguity of key mappings in a specific buffer to avoid timeout

I use plugin "Buffet", and there's local-to-buffer mapping "d" to delete buffer under cursor.
I also use plugun Surround, and there's global mapping "ds" that means "delete surround".
So, when i press "d" in the Buffet's window, Vim waits for a second before execute mapping "d". I know about &timeoutlen, but i don't want to change it. So that I want to resolve ambiguity of key mappings for "d" in the Buffet's window to avoid timeout to delete a buffer.
To resolve the problem, I want to unmap in Buffet window all the mappings that start with "d", but except Buffet's own mappings. How can i do that?
P.S. I have read about maparg() and mapcheck(), but they seem not to be what i need, unfortunately.
It seems like i found the solution myself:
au BufEnter buflisttempbuffer* nunmap ds
au BufLeave buflisttempbuffer* nmap ds <Plug>Dsurround
I hoped that there's more universal approach (to remove really all mappings starting from "d"), but at this moment i failed to find it.
Even if i found out how to get all these mappings, unfortunately i can't do unmap <buffer> ds, because ds is a global mapping. I'm sure that i should be able to disable global mapping for some buffer, though. Vim is great but not perfect.
Well, it works for me now.
Now that the question has been "rephrased", this solution is no longer relevant, but I'll post it anyway since I spent a few minutes on it.
Here's a function that grabs the output of map <letter> and extracts the individual maps. Then it unmaps them all.
function! Unmap(leader)
redir => maps
sil exe "map " . a:leader
redir END
let maps_list = split(strtrans(maps),'\^#')
if len(maps_list) > 1
for this in maps_list
let mapn = matchstr(this,"^\\w\\s*\\zsd\\w*\\>")
exe "unmap " . mapn
endfor
endif
endfunction
Example usage: call Unmap("d"). This will remove all mappings that begin with d, leaving only Vim's defaults.
Disclaimer: this has not been rigorously tested. In particular I don't know how portable the \^# character is, but that's how it looks on my (Win32) machine.
The easiest way to do it is:
:e /WHERE/YOU/HAD/INSTALLED/buffet.vim
:%s:map <buffer> <silent> d:"&:
:wq
$ vim # Restart Vim to take effect...
Generally you can't unmap based on a pattern.
If you want to use another key (e.g. with <leader>, just change this line in the plugin:
map <buffer> <silent> d :call <sid>deletebuffer(0)<cr>
This question is rather old, but if you're still interested, you might want to give Bufstop a try.
This issue is handled by the plugin, you can press the d key to delete a buffer, and you won't get any timeout if you installed other plugins which have global mappings.
A cheap trick that worked for me was to make the timeoutlen so short it becomes pretty much instantaneous. As long as you don't use multiple key mappings yourself, that will cover all plugins in one shot.
We don't want that setting to stay however, so we remove it every time we leave the buffer.
Add this so that it runs inside your custom buffer:
augroup no_map_chords
autocmd!
autocmd BufEnter <buffer> let g:bak_timeoutlen = &timeoutlen | set timeoutlen=1
autocmd BufLeave <buffer> let &timeoutlen = g:bak_timeoutlen | unlet g:bak_timeoutlen
augroup END
A similar technique could be used for a specific file type, or other such "global" settings.
Buffet is a very young plugin, I don't think it's used by as many people as Command-T or NERDTree so you may not receive lots of answers. Its author has been very responsive on the numerous threads he created there about it you should contact him directly or create an issue on Buffet's github.

In vim, how can I map a key to toggle folding over the whole buffer?

I'd like to map a key to toggle between foldmethod=indent and no folding. How can I do that?
I'd say zi (toggle foldenable) does the job.
No mapping required. (see also :he folding)
(You could also look at zM and zR)
Since you want to map it to a single key, proceed as follows:
:nnoremap <F10> zi
To force the foldmode to indent each time (not really recommended for me), you'd need a function:
Add the function to your vimrc[2]:
function ForceFoldmethodIndent()
if &foldenable
se foldmethod=indent
endif
endfunction
nnoremap <F10> :normal zi^M|call ForceFoldmethodIndent()^M
inoremap <F10> ^O:normal zi^M|call ForceFoldmethodIndent()^M
Let me know if that works for you. I appreciate if you accept this answer if it does :)
Cheers
[1] with behave mswin
[2] To enter the special keys (e.g. ^O) in commandline or insertmode use e.g.
Ctrl-VCtrl-O or
on windows[1] Ctrl-QCtrl-O

Synonym for Vim's normal mode CTRL-A?

I came to an idea that <C-a> in Vim's normal mode should not only increase numbers but toggle booleans. It makes sense if you consider true and false as integers modulo 2.
So, I downloaded an excellent script to do the hairy work and wrote a new definition for <C-a>:
fun! NewCA()
let cw = tolower(expand("<cword>"))
if cw == "true" || cw == "false"
ToggleWord
else
" run the built-in <C-a>
execute "normal \<C-a>"
endif
endfun
command! NewCA :call NewCA()
nnoremap <C-a> :NewCA<cr>
But as it happens, nnoremap doesn't go as far as to check inside functions. I get recursive behaviour if my cursor is not on words true or false.
In this point I swear a lot, why didn't Bram go pick an excellent idea from Emacs, that everything should be functions and key bindings freely setable. Then I just could check the function for <C-a> and call it in that function. But no, I can't find such a function, and the execute "normal foo" phrases seem to be the Vim idiom.
Any suggestions on how I could make <C-a> work such that
Toggle booleans when the cursor is over a word true or false
Fall back to built-in <C-a> behaviour otherwise
Help appreciated!
change
execute "normal \<C-a>" to:
normal! ^A
you can get ^A by running <C-v><C-a> in normal mode
the "!" at the end of normal say "use default mapping"
From :help :normal
:norm[al][!] {commands}
...
If the [!] is given, mappings will not be used.
....
Also defining a command is not needed, you can directly
nnoremap <C-a> :call NewCA()

Unable to understand a line in .vimrc

I do not understand what the following line does in .vimrc
nmap <silent> <leader>v :EditConfig<cr>
It seems that
nmap mean noremap
silent seems to mean apparently no beep in Vim
leader seems to mean the first character in the mode :
v seems to mean visual mode
EditConfig should be a command in vim in the mode : (However, it is not.)
What does the line mean in .vimrc?
nmap means "map a key sequence when in normal mode" (see vim's docs).
<silent> tells vim to show no message when this key sequence is used.
<leader> means the key sequence starts with the character assigned to variable mapleader -- a backslash, if no let mapleader = statement has executed yet at the point nmap executes.
And the v is the rest of the key sequence.
So overall this is mapping, in normal mode, a backslash-v key sequence to show no message and execute :EditConfig which is likely a function defined previously in the vimrc to edit configuration files (see for example this vimrc, search in browser for editconfig). :call EditConfig() at the end (as the vimrc file I gave the URL to uses) would be better, I believe.
It would appear that you are missing a function...
Try,
function! EditConfig()
for config in ['$MYGVIMRC', '$MYVIMRC']
if exists(config)
execute 'edit '.config
endif
endfor
endfunction
Check this example.

Resources