Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On automatic triggering of relevant completion suggestions via snippets #204

Closed
TryerGit opened this issue Oct 22, 2022 · 7 comments
Closed

Comments

@TryerGit
Copy link

I use mucomplete in conjunction with UltiSnips and VimTeX for autocompletion of .tex files. What I would like to have is automatically triggering of completion suggestions based on context of a snippet placeholder's cursor position. An example follows:

I have the following .vimrc

if empty(glob('~/.vim/autoload/plug.vim'))
    silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs
        \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
    autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
endif
call plug#begin('~/.vim/plugged')
Plug 'lervag/vimtex'
Plug 'lifepillar/vim-mucomplete'
Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
call plug#end()

set completeopt+=menuone
set completeopt+=noselect
let g:mucomplete#enable_auto_at_startup = 1

let g:mucomplete#can_complete = {}
let g:mucomplete#can_complete.tex = { 'omni': { t -> t =~# g:vimtex#re#neocomplete . '$' } }

Consider the following .tex file:

\documentclass[11pt]{article}

\usepackage{amsmath}

\begin{document}

\begin{alignat}{2}
	\text{ }& asdfasdf              &&\quad\nonumber\\
	\text{ }&89999\times 45\leq 88	&&\quad \forall\label{eq:1}\\
		&abc	                &&\quad\exists\label{eq:okay}\\
		&\times x	        &&\quad\forall\nonumber\\
		&abcd                   &&\quad\nonumber
\end{alignat}

\bibliographystyle{apalike}
\bibliography{bibfile.bib}

\end{document}

I have the following snippets via UltiSnips:

snippet refe "Refer Equation" w
(\ref{eq:$1})$0
endsnippet

On typing refe<SnippetTrigger>, one should get:

(\ref{eq:|}) where | denotes the cursor in insert mode. I was hoping that when this happens, automatically, all of the equation labels of the .tex file whose labels start with eq: are displayed in a popup for automatic completion. (Note, this is after ensuring that the .aux file resides in the folder following one compilation as answered here)

However, this does not happen. The automatic triggering happens only if the user manually types in \ref{| individually that the triggering happens.

Please see explanatory video/gif of the above at : https://imgur.com/a/3attCKA

Is this expected behavior or is there a setting to have automatic completion suggestions triggered even if the cursor is at a placeholder position within a snippet?

Thank you.

PS: The official response from VimTeX seems to be that it is not possible from within VimTeX to trigger such snippet placeholder contextual completion. See here

@lifepillar
Copy link
Owner

I'll try to reproduce it. If you press Tab after the snippet is expanded, is any completion offered?

@TryerGit
Copy link
Author

TryerGit commented Oct 22, 2022

I'll try to reproduce it. If you press Tab after the snippet is expanded, is any completion offered?

With just the bare minimum vimrc I posted, inside it, I have not reconfigured the UltiSnips trigger keys as you suggest in https://github.com/lifepillar/vim-mucomplete/blob/master/doc/mucomplete.txt

So, Tab after refe trigger expands the snippet to get me to (\ref{eq:|}) where | is the insert mode cursor. Pressing Tab again gets intercepted by UltiSnips and not mucomplete. However, if I follow this up with Ctrl-n or Ctrl-p, the expansion does work. I was trying to see whether it is possible to somehow do away with having to press Ctrl-n or Ctrl-p and have this expansion done automatically when the cursor finds itself in (\ref{eq:|})

I should add that the suggestions that come from Ctrl-n and Ctrl-p are forward looking and backward looking plain textual completions (one could get this completion without VimTeX for instance) and not omnifunc completions (which is due to VimTeX's interaction with MuComplete). With CtrlX-CtrlO, I get the actual omnifunc completions which is what I was hoping gets automatically triggered from within the snippet placeholder position.

@lifepillar
Copy link
Owner

Thanks for the feeback. In principle, something like this should work:

fun MyTabAction()
  return "\<c-r>=UltiSnips#ExpandSnippet()\<cr>\<plug>(MyFwd)"
endf

let g:UltiSnipsExpandTrigger = "<f5>"   " Not to interfere with the mapping below
imap <plug>(MyFwd) <plug>(MUcompleteFwd)
imap <expr> <silent> <tab> MyTabAction()

This does perform completion, but unfortunately the pop-up menu does not stay open. I have not found a way to prevent that, and I have not understood why that happens.

My suggestion would be to follow the recommendations in MUcomplete's help:

  • redefine UltiSnips's trigger to something different from Tab (as done above);
  • add the 'ulti' method at the beginning of your completion chain;
  • enable automatic expansion of snippets as explained in :help mucomplete-tips.
  • Optionally set completopt=menuone,noinsert to have the first menu entry automatically selected.

By doing so, MUcomplete will offer to complete snippets' names and you can expand them by selecting them from the pop-up menu and pressing Enter. After that, you may press Tab to complete the inserted entry.

This (Enter + Tab) is still one keystroke more than what you want to achieve; on the other hand, you may save several keystrokes by enabling 'ulti' completion.

@lifepillar
Copy link
Owner

To be clear, the problem with the pop-up menu closing after expanding the snippet is not related to MUcomplete. The same happens if you try to complete with Ctrl-X Ctrl-N after the expansion:

fun MyTabAction()
  let l:expand_snippet = "\<c-r>=UltiSnips#ExpandSnippet()\<cr>"
  let l:complete = "\<c-x>\<c-n>\<c-n>"  " Ctrl-x Ctrl-n for keyword completion, Ctrl-n to select an entry (assumes completeopt contains noselect)
  return l:expand_snippet .. l:complete
endf

let g:UltiSnipsExpandTrigger = "<f5>"   " Not to interfere with the mapping below
imap <expr> <silent> <tab> MyTabAction()

Maybe UltiSnips's developers have an idea on how to make Ctrl-X Ctrl-N work. If so, it's likely that MUcomplete can be made to work in the same way.

@TryerGit
Copy link
Author

> fun MyTabAction()
>   return "\<c-r>=UltiSnips#ExpandSnippet()\<cr>\<plug>(MyFwd)"
> endf
> 
> let g:UltiSnipsExpandTrigger = "<f5>"   " Not to interfere with the mapping below
> imap <plug>(MyFwd) <plug>(MUcompleteFwd)
> imap <expr> <silent> <tab> MyTabAction()
> 

This works at my end. Thank you for your help.

@TryerGit
Copy link
Author

TryerGit commented Oct 24, 2022

fun MyTabAction()
  return "\<c-r>=UltiSnips#ExpandSnippet()\<cr>\<plug>(MyFwd)"
endf

let g:UltiSnipsExpandTrigger = "<f5>"   " Not to interfere with the mapping below
imap <plug>(MyFwd) <plug>(MUcompleteFwd)
imap <expr> <silent> <tab> MyTabAction()

and

fun MyTabAction()
  let l:expand_snippet = "\<c-r>=UltiSnips#ExpandSnippet()\<cr>"
  let l:complete = "\<c-x>\<c-n>\<c-n>"  " Ctrl-x Ctrl-n for keyword completion, Ctrl-n to select an entry (assumes completeopt contains noselect)
  return l:expand_snippet .. l:complete
endf

let g:UltiSnipsExpandTrigger = "<f5>"   " Not to interfere with the mapping below
imap <expr> <silent> <tab> MyTabAction()

If it is not asking too much, could you please provide a high level explanation of what precisely is being done in these functions and why they work while it would not work before?

In particular, the lines:

imap <plug>(MyFwd) <plug>(MUcompleteFwd)
imap <expr> <silent> <tab> MyTabAction()
return "\<c-r>=UltiSnips#ExpandSnippet()\<cr>\<plug>(MyFwd)"
let l:expand_snippet = "\<c-r>=UltiSnips#ExpandSnippet()\<cr>"
let l:complete = "\<c-x>\<c-n>\<c-n>"  " Ctrl-x Ctrl-n for keyword completion, Ctrl-n to select an entry (assumes completeopt contains noselect)
return l:expand_snippet .. l:complete

@lifepillar
Copy link
Owner

We first set the keystroke to activate UltiSnips to be something different from Tab. This is because we want to define Tab ourselves (I have chosen <f5>, but anything different from <tab> is fine):

let g:UltiSnipsExpandTrigger = "<f5>"

Then we define an internal mapping (see :help <Plug>). This is not associated to any key, but prevents MUcomplete to map Tab (again, because we want to define it ourselves). In fact, MUcomplete maps Tab to <plug>(MUcompleteFwd), but only if nothing else already maps to that:

imap <plug>(MyFwd) <plug>(MUcompleteFwd)

We are now free to map Tab to whatever we want, and we choose to map it to an expression (MyTabAction()):

imap <expr> <silent> <tab> MyTabAction()

The expression that is evaluated is the string returned by MyTabAction(). The first part of the string evaluates Ultisnips#ExpandSnippet(), again in expression context (in this case, the expression context is activated by Ctrl-R followed by =—see :help c_CTRL-R_=). If you are not familiar with Ctrl-R-=, try this: enter Insert mode then type Ctrl-R followed by =, then type 1+1 followed by Enter.

The second part of the string just triggers our mapping, which invokes MUcomplete.

In the second example, I have split the string just for clarity, assigning each part to a local variable. Then, the return statement just returns the concatenation of the two strings.

In the second example, the second part of the string is used to trigger keyword completion (Ctrl-X Ctrl-N) and to select the first item in the pop-up menu (another Ctrl-N).

Note that, when such strings are evaluated in a mapping < must be escaped (\<).

Hope this helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants