Skip to content

Commit

Permalink
When available, use complete() and TextChangedP rather than completefunc
Browse files Browse the repository at this point in the history
Recently there has been quite a lot of flicker and noticable redraw
latency, ostensibly caused by our auto-triggering approach. Historically
our approach was as follows:

* set completefunc to our CompleteFunc (see later)
* On InsertCharPre, close the popup menu if it is visible
* On TextChangedI, trigger user-defined completion (to re-show the now
  closed popup menu), request completion async and start polling
* When the completion request is done store the result and, trigger
  user-deinfed completion using ctrl-x, ctrl-u (and ctrl-p to prevent
  selecting the top entry)
* In our CompleteFunc, supply the completions stored completion data.

The reason for the closing the popup menu in InsertCharPre was that
TextChangedI is _not_ fired while the popup menu is visible. So we do
this dance.

However, more recent Vims have provided TextChangedP - basically
TextChangedI but triggered while the pum is visible. In addition, the
following new features are useful: complete() - a function to basically
start insert-mode completion with the supplied data, and
completeopt=noselect, which prevents automatically selecting the first
completion. Nice.

But there is one snag. TextChangedP is _also_ triggers when a completion
is selected (e.g. using Ctrl-n or Tab). This is irksome, because it
means that we end up filtering the completions based on the
newly-inserted text. This is not what we want - the user didn't type
that text (they selected it) so we should continue to show all of the
most recent matches. There's no _good_ way to solve this, so we solve it
by recording whether or not the last imput came from a user or not. We
do this using InsertCharPre (setting it to true) and CompleteDone
(setting it to false). The former is when a user types some characters
and the latter when an entry in the menu is selected.

This isn't perfect. As previously mentioned, InsertCharPre is not
triggered when the completion menu is visible, so there is a scenario
which breaks: User tabs to an entry, then adds some more characters.
However, this seems niche enough that the benefits of the new system in
terms of redraw and flicker outweigh this (essentially unfixable) bug.

So the new approach is:

* In InsertCharPre, set the flag to true
* In TextChangedI/TextChangedP, if it's TextChangedP and the flag is
  false, ignore the event.
* Otherwise, in TextChangedI/TextChangedP, call complete() with the
  latest completion response, and fire off another one, staring the poll
  .timer
* When the response is received, just call complete() with the new data.

The only remaining wrinkle is the omnicompleter. complete() cannot be
called from the TextChangedI/TextChangedP autocommand handlers
synchronously (for some reason) - it causes vim to get messed up. This
is reasonable, as complete() is supposed to be used async. So we
workaround that by polling for completion in a 0ms timer, rather than in
athe autocommand handler.

Finally, this commit adds a bunch more tests for completion that test
both the new and the old mechanism. We keep the old mechanism because
the new stuff is too new for our "supported" Vim version.
  • Loading branch information
puremourning committed Apr 12, 2020
1 parent 3a9a1da commit 22928a2
Show file tree
Hide file tree
Showing 7 changed files with 594 additions and 117 deletions.

0 comments on commit 22928a2

Please sign in to comment.