open a completion popup from normal mode - vim

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.

Related

Using "z=" in a mapping

If I use <Esc>[s1z=`]a in an inoremap mapping (jump to normal mode, find previous spelling error, replace it with the first choice, jump back to last edit and append), everything works fine. The problem is I often don't want the first spelling choice. If I remove the 1 I will be given the spelling menu, but the `]a seems to get swallowed up and lands me at the first character of the corrected word still in normal mode. The mapping itself shouldn't be looking for input, perse, as the z= itself should handle the menu entry. Indeed if I run these commands manually (without the 1, it works as expected. I have tried making named marks and jumping back to those, but it seems z= and everything after it gets consumed as one thing. Does anyone have any suggestion as to how to make the mapping continue after I make a spelling menu selection? Thanks.
I think that Vim stops processing the right-hand side of the mapping as soon as it presses z= because it's not a complete command (you have to provide the index of a suggestion in the menu for it to be complete).
The :normal command has the same issue:
:norm[al][!] {commands}
...
{commands} should be a complete command. If
{commands} does not finish a command, the last one
will be aborted as if <Esc> or <C-C> was typed.
As an alternative, you could invoke the feedkeys() function to press z=.
For example:
ino <c-j> <c-r>=<sid>fix_typo()<cr>
fu! s:fix_typo() abort
let spell_save = &l:spell
try
setl spell
call feedkeys("\e\e[sz=", 'int')
augroup fix_typo
au!
au TextChanged * call feedkeys('`]a', 'int')
\ | exe 'au! fix_typo'
\ | aug! fix_typo
augroup END
finally
call timer_start(0, {-> execute('let &l:spell = '.spell_save)})
endtry
return ''
endfu
This code installs a mapping in insert mode using the C-j key. You could use another key if you don't like this one.
The mapping calls the s:fix_typo() function which:
temporarily enables 'spell' to avoid the error E756
presses the keys Esc Esc [s z= to prompt the suggestions
installs a fire-once autocmd listening to TextChanged to press the keys `]a once you've selected a word in the menu
restores the original value of 'spell'

Enter insert mode between two tags after command in vim

I'm looking to change the following vim command so that after entering the :%s/temp123//g part it will put me into insert mode between the \begin and \end tags.
inoremap \\beg \begin{temp123}<enter><enter>\end{temp123}<enter><esc>:%s/temp123//g<left><left>
I have been able to use :startinsert to get into go into insert mode after entering the search/replace command but I am not able to place the cursor between the \begin and \end tags.
Any help/solutions/improvements would be appreciated.
TL;DR: can't.
:star[tinsert][!] Start Insert mode just after executing this command.
Works like typing "i" in Normal mode. When the ! is
included it works like "A", append to the line.
Otherwise insertion starts at the cursor position.
Note that when using this command in a function or
script, the insertion only starts after the function
or script is finished.
This command does not work from :normal.
I was trying to get the following line working:
nnoremap <MiddleMouse> :set paste<cr>:startinsert<MiddleMouse><esc>
What this means is that all of my commands that I put after :startinsert instead run immediately before :startinsert and then :startinsert runs and changes the mode to insert (Note: this appears to hold true for using i instead of :startinsert as well).
My next step was to try to make a nested function, where I had one function that calls another function, and the second function runs :startinsert and then returns to the first function, which then completes the paste:
function! Paste()
call InsertMode()<cr>
:set paste<cr>
<S-Insert>
:set nopaste<cr>
<esc>
endfunction
function! InsertMode()
:startinsert<cr>
endfunction
nnoremap <MiddleMouse> call Paste()<cr>
But this did not work either. I also tried using the "+p and "*p registers without :startinsert with nnoremap <MiddleMouse> :set paste<cr>"+p:set nopaste<cr>, but this again just pastes directly as if I were typing it in, it does not enter insert mode first. I am willing to believe this would work on a version of Vim compiled with +clipboard, but that is not the version I have. Link to my original question and answer
Yep, you're looking for an interactive substitution, but :s/new/old/gc doesn't let you edit each match. For this kind of work, switch to the gn command + . recipe:
First search for {temp123} (or whatever you wish to replace) with /
Then press cgn to change the visually selected next match
Once done, go back to normal mode to commit your edits.
If the next edit is the same as the last, press only . otherwise cgn
Rise and repeat.
For more ideas, see this vimcast: gn command
This is the solution I am now using.
function TexBegin()
let currline = line(".")
call inputsave()
let tagname = input("enter tag name: ")
call inputrestore()
call setline(currline, "\\begin{" . tagname . " }")
normal o
normal o
call setline(currline + 2, "\\end{" . tagname . "}")
normal l
startinsert
normal o<Esc>
endfunction
inoremap \\beg <Esc>:call TexBegin()<CR>

reusing earlier inoremap sequence in newer inoremap sequence

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

Programmatically equalize windows inside a function

I've got the following vimscript function:
function! WindowCommand(cmd)
execute a:cmd
if !g:golden_ratio_enabled
normal <C-w>=
endif
endfunction
And I use it like so:
map <space>w/ :call WindowCommand(':vs')<cr>
It's supposed to equalize the windows, but only if g:golden_ratio_enabled is 0, otherwise it should do nothing.
It doesn't work, though, and I'm not sure why, because the following DOES work:
map <space>w/ :vs<cr><C-w>=
What am I doing wrong here?
There are a couple fixes. Thankfully, the fix is really simple
For whatever reason, normal <C-w>foo does not work; You must use wincmd instead. From :h wincmd
*:winc* *:wincmd*
These commands can also be executed with ":wincmd":
:[count]winc[md] {arg}
Like executing CTRL-W [count] {arg}. Example: >
:wincmd j
Moves to the window below the current one.
This command is useful when a Normal mode cannot be used (for
the |CursorHold| autocommand event). Or when a Normal mode
command is inconvenient.
The count can also be a window number. Example: >
:exe nr . "wincmd w"
This goes to window "nr".
So in this case, you should do
wincmd =
Alternatively, you could enter a literal <C-w> character, by typing <C-v><C-w>. In your vim session, this character will be displayed as ^W.
To execute actions with <notation>, use instead:
:exe "normal \<notation>"
I use it a lot to debug mappings.
But in this case, prefer indeed wincmd.

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