Subject: Re: bug#76606: 30.1; Conflict between completion-preview and yasnippet

From: Eshel Yaron <[email-masked]>
Date: Thu, 27 Feb 2025 08:50:21 +0100

Hi there,

Vincent Foley <[email-masked]> writes:

> In Emacs 30.1, completion-preview and yasnippet appear to conflict with
> each other. In a c-mode buffer with eglot, yasnippet, and
> completion-preview enabled, accepting a completion from
> completion-preview with M-i inserts the formal parameters over and over
> again.
>
>         // Initial state -- cursor is at the pipe (|);
>         int add(int x, int y) { return x + y; }
>         int f(void) { |
>
>         // Type "add"; completion-preview activates and proposes a
>         // completion (the text `(int x, int y)` is not actual text,
>         // but an overlay in a lighter color and underlined.)
>         int add(int x, int y) { return x + y; }
>         int f(void) { add|(int x, int y)
>
>         // Pressing M-i to accept the completion moves the cursor
>         // inside the parentheses, but confusingly creates a copy
>         // of the parameters outside the parentheses.
>         int add(int x, int y) { return x + y; }
>         int f(void) { add(|int x, int y)int x, int y)
>
>         // Pressing M-i again adds more text to the end of the line
>         int add(int x, int y) { return x + y; }
>         int f(void) { add(|int x, int y)int x, int y), int y)
>         
> The following init.el should be sufficient to reproduce the bug.
> I used the clangd LSP from the Debian apt repositories.
>
>         (package-initialize)
>         (use-package eglot
>           :hook (c-mode . eglot-ensure))
>         (use-package completion-preview
>           :hook (c-mode . completion-preview-mode))
>         (use-package yasnippet
>           :hook (c-mode . yas-minor-mode))

Thank you for the detailed report.  I can reproduce the issue.

A general note: TAB (completion-preview-insert) accepts the whole
completion suggestion, while M-i (completion-preview-complete) only
inserts the common prefix of all completion candidates.  In this case
these happen to be the same since there's only one candidate, expect
that TAB doesn't suffer from the problem you've stumbled upon.

The following diff should fix this bug, could you please test it?

diff --git a/lisp/completion-preview.el b/lisp/completion-preview.el
index 4928d9a8824..eb4b9111b30 100644
--- a/lisp/completion-preview.el
+++ b/lisp/completion-preview.el
@@ -520,6 +520,7 @@ completion-preview-complete
             ;; hook update the completion preview in case the candidate
             ;; can be completed further.
             (when (functionp efn)
+              (completion-preview-active-mode -1)
               (funcall efn (concat base com) (if (cdr all) 'exact 'finished)))
           ;; Otherwise, remove the common prefix from the preview.
           (completion-preview--inhibit-update)

Regards,

Eshel