reusing earlier inoremap sequence in newer inoremap sequence - vim

I'm using a plugin that performs multiple *noremap operations in its initialization. After I added the following mapping to YCM/UltiSnips:
inoremap <expr> <CR> pumvisible() ? "<C-R>=<SID>ExpandSnippetOrReturn()<CR>" : "\<CR>"
I've broken the other plugin's ability to see the <Enter> key, or rather I overrode/destroyed the original inoremap. I want to reenable that functionality, but without disabling ability to pick snippets from the auto-complete menu using Enter key. This keymap is effectively saying "if the menu is visible, select the option, otherwise simulate <Enter> key". What I need it to say instead is "if the menu is visible, select the option, otherwise perform whatever command <Enter> key is already mapped to". So basically, at the time of declaration of this inoremap, I need to expand existing inoremap for this key into the else.
I've looked at <C-R>=feedkeys('<CR>')<CR> and <C-R>=mapcheck('\<CR>', 'i')<CR> but couldn't get either to work. Can someone help me out?

Finally figured it out... this was way more vim troubleshooting than I needed for one day.
function! LoadPumvisibleEnter()
let smartmap = maparg("<Enter>", "i")
execute printf('inoremap <script> <expr> <CR> pumvisible() ? "<C-R>=<SID>ExpandSnippetOrReturn()<CR>" : %s', smartmap)
endfunction
au VimEnter * :execute LoadPumvisibleEnter()
throwing it in a function allowed me to stay sane with escapes and which mode I was in
autocommand is needed because vim apparently runs vimrc BEFORE loading any plugins (Does Vim load plugins after loading vimrc?), so without it the map will not yet be set

Related

In a vim expression, how can I return back to normal mode?

I'm using the CoC plugin for which works great.
This command remaps the enter key so we can use enter to confirm selection of the suggestion popover.
This mapping works great, except that it stays in insert mode after accepting the selection:
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"`
From https://github.com/neoclide/coc.nvim/wiki/Completion-with-sources#use-cr-to-confirm-completion
How can I modify this to return back to normal mode after I hit enter?
Any ideas? Thanks!
First, let's deconstruct the mapping you got from that page:
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"
inoremap makes it a non-recursive insert mode mapping. i means "insert mode" and nore means "non-recursive". I know the remap at the end makes it tempting to call those things "remaps" or "remappings" but they are not. They are just "mappings".
<expr> makes it an expression mapping. It means that the right-hand side is an expression that is going to be evaluated at runtime to produce the actual RHS.
pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>" is what Vim calls a "trinary operator" (AKA ternary operator): :help trinary. In plain english, it reads like this: "if the popup menu is visible, return <C-y>, if not, return <C-g>u<CR>".
:help complete_ctrl-y accepts the current suggestion in the popup menu.
:help ctrl-g_u prevents Vim from inserting an undo point before <CR>. It is not strictly necessary but good practice.
You want to adjust the mapping's behaviour when you accept a suggestion so it is the "\<C-y>" part that must be changed.
Now…
you already know what key to press to leave insert mode,
you can take a look at :help key-notation if you are unsure how to represent that key,
you know what part of the mapping you need to change,
you should get an idea of how to proceed just by looking at the two last parts of the "trinary" operator.

vim autocmd hook after performing a search in the text?

I had a look at the list of events autocmd can handle, but I could not find one about search queries.
What I would like to do is to add some custom behavior after each text search is performed. I was able to remap the * command with this command:
map * *<F11>
In this case I mapped <F11> to a :call My_function() which will do something with the search pattern contained in #/.
But I still need to add my custom behavior to the / command, which is more complicated, as before it's completed it's getting the input search pattern.
Do you have any hint on how to proceed? Can I use autocmd? Or maybe is there a map trick?
An (bad) way to do it would be to remap the return key (and the esc key)
when / is pressed, something like that:
function! MyCustomBehaviour()
echo "Oui oui"
endf
function! UnmapSearch()
cunmap <cr>
cunmap <esc>
endf
function! MapSearch()
cnoremap <cr> <cr>:call UnmapSearch()<bar>call MyCustomBehaviour()<cr>
cnoremap <silent> <esc> <c-c>:call UnmapSearch()<cr>
endf
noremap / :<c-u>call MapSearch()<cr>/
It's a bad way because it's quite buggy : if you press Ctrl-C while editing the
search, it won't unmap <cr> and <esc>, then the next time you will enter : (command-line) mode,
the mappings will still be active... That's a problem that can't be solved (<c-c> can't be
remapped).
It's also a bad way because remapping directly the / key this way, IMO, is not a good practice.
But... This was the only solution I found some times ago to half-solve this problem.
Another workaround (the one I finally choose) can be written in one line :
cnoremap <c-cr> <cr>:call MyCustomBehaviour()<cr>

open a completion popup from normal mode

Gived this script (packages suggestion was simplified, original takes care of <cword>)
function! CompleteImport()
let packages = ['java.util.Vector','java.lang.String']
call complete(col('.'),packages)
return ''
endfunction
inoremap <F8> import <C-R>=CompleteImport()<CR>
while on insert mode you can add an import and choose between suggested packages pressing F8
But I want to be enable to reach that popup selection from normal mode
function! InsertImport()
exe "normal iimport \<C-R>=CompleteImport()\<CR>"
"this commented line would work too
"exe "normal i\<F8>"
endfunction
map <Leader>ji :call InsertImport()<CR>
so from normal mode ,ji (stands for java import) add an import to word under cursor if it is found
(Move to right position is not a problem so I ommit here)
By now ,ji adds first suggestion from popup and exists insert mode
I have tried :startinsert but no luck.
see on http://vimdoc.sourceforge.net/htmldoc/eval.html#:execute there is a suggested code:
:execute "normal ixxx\<Esc>"
but that final Esc doesn't matter at all (for my vim install at least) This do exactly the same for me:
:execute "normal ixxx"
I would think this is not possible if I haven't found that on docs. So, it's possible to stay on a popup calling from a function?
Other interested docs:
http://vimdoc.sourceforge.net/htmldoc/various.html#:normal
http://vimdoc.sourceforge.net/htmldoc/insert.html#:startinsert
:startinsert usually is the right approach, but it indeed gives control back to the user, so you cannot automatically trigger the completion.
Through the feedkeys() function, you can send arbitrary keys "as if typed". This allows you to start insert mode and trigger the completion:
function! InsertImport()
call feedkeys("iimport \<C-R>=CompleteImport()\<CR>", 't')
endfunction
nnoremap <Leader>ji :call InsertImport()<CR>
PS: You should use :noremap for the normal mode mapping, too; it makes the mapping immune to remapping and recursion.

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

How to create a mapping for Insert mode but not for the autocomplete submode in Vim?

I have these insert mode mappings in my .vimrc file:
imap <C-e> <C-o>A
imap <C-a> <C-o>I
They make Ctrl-A and Ctrl-E move the cursor to the start and end of the line without leaving insert mode, a la emacs keybindings.
However, I just realized that the Ctrl-E mapping introduces a conflict with the autocompletion submode. The documentation in :help complete_CTRL-E states:
When completion is active, you can use CTRL-E to stop it and go back to the originally typed text.
Thus, my Ctrl-E mapping interferes with this. Is there a way that I can make Ctrl-E jump to the end of the line only if auto-completion is not active?
There is no proper way to test whether the
Ctrl+X-completion mode is active or not.
However, the following two workarounds are possible.
1. If one uses the popup menu to choose from the list of available
completions (especially in the case of menuone set in the completeopt
option), an acceptable solution might be the mapping
inoremap <expr> <c-e> pumvisible() ? "\<c-e>" : "\<c-o>A"
2. A general solution can be based on a side effect: In the
completion submode, it is disallowed to enter Insert mode recursively
(see :helpgrep Note: While completion), so if an attempt to do so
fails, we can suppose that we are in the midst of a completion:
inoremap <c-e> <c-r>=InsCtrlE()<cr>
function! InsCtrlE()
try
norm! i
return "\<c-o>A"
catch
return "\<c-e>"
endtry
endfunction

Resources