Skip to content

Commit

Permalink
Rewrite merlin-completion-at-point integration to be faster and better
Browse files Browse the repository at this point in the history
The merlin-completion-at-point integration before was simple and slow.

The new version is much faster and much more featureful:

- Completions are requested for an entire module at once and filtered
locally in Emacs rather than doing the filtering on the Merlin side.

- Completions are cached based on the OCaml atom being completed, so
they are re-used instead of re-requested when a new character is
typed.

- Those completions are cached for a given position inside an OCaml
atom, so that if a user types Li<TAB>ma<TAB> to complete "List.map"
and then decides they actually want the module "Labels", when they
delete the "ist.map" part and hit <TAB>, they'll use the
previously-requested completions.

- We avoid updating Merlin with the new buffer contents as completion
proceeds, so that Merlin doesn't need to re-parse and re-type-check,
substantially improving performance in expensive
files.  (merlin-cap--omit-bounds)

- Completion requests are handled asynchronously and reused, so that
if completion is interrupted and then resumed, we're able to use the
results of the previous completion request.  This makes completion UIs
which use while-no-input (like corfu-mode) much more performant.

- Completion is wrapped in while-no-input when non-essential is set;
this makes completion UIs which don't use while-no-input (like
company-mode) much more responsive.

- Completions are sorted more intelligently: if they're a constructor
or variant or label, they're likely to be more relevant to the user,
so they're sorted first.

- Module names in completions are suffixed with a ., matching Emacs
behavior for file name completion (where directories are suffixed with
a /); this makes completion of module paths much more fluent, since
there's no need to hit . after every module name.

- We use completion boundaries, so the built-in Emacs
partial-completion feature now works: if the user types "Li.ma<TAB>",
it will complete to "List.map".

- Likewise, partial-completion will expand * as a glob, so if the user
types "Deferred.*.map<TAB>" they will be presented with every module
in "Deferred." which contains the method "map"

There are also several tests now, testing the new functionality.
  • Loading branch information
catern committed May 6, 2024
1 parent 2eeb9be commit 404c580
Show file tree
Hide file tree
Showing 2 changed files with 739 additions and 97 deletions.
Loading

0 comments on commit 404c580

Please sign in to comment.