Subject: bug#68514: 30.0.50; minibuffer-choose-completion + elisp-c-a-p delete next sexp when completing after open paren

From: Spencer Baugh <[email-masked]>
Date: Tue, 16 Jan 2024 14:43:35 -0500

Juri Linkov <[email-masked]> writes:
>> This is because elisp-completion-at-point returns a completion region
>> covering the entire sexp after the start of the region.  That sexp is
>> "(progn (some) (big) (expression))" in the first case and "a" in the
>> second case.
>
> This is a known bug without a known fix:
> https://debbugs.gnu.org/64903#18

Alright, how about this patch?  Should fix most of the issue without
changing the code too much.

>From d76353122ec563eb2d3ca23f96ea67a6843974c5 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <[email-masked]>
Date: Tue, 16 Jan 2024 14:32:21 -0500
Subject: [PATCH] Make elisp-cap region cover a symbol, not lists or strings

elisp-completion-at-point calculates the completion region by running
backward-sexp from (point) and storing that position as the beginning,
then running forward-sexp from the beginning and storing that position
as the end.

elisp-completion-at-point does symbol completion, so the completion
region should only cover a symbol.  Therefore,
elisp-completion-at-point checks whether the beginning of the
completion region (the result from backward-sexp) is a quotation mark
or open paren, which would indicate that the region covers a string or
list, and returns nil in that case.

If point is already at the start of a sexp (e.g. immediately after an
open paren) then backward-sexp will not move point and the beginning
of the completion region would (correctly) be the original point.

forward-sexp, in this case, will move over the next sexp after point
and include it in the completion region, even if it's a string or
list.

That has several bad effects:

- An unrelated next sexp can be deleted by doing completion at the
start of some earlier sexp (bug#64903, bug#68514)

- The completion region can be very large, breaking some completion
styles; if the next sexp is large enough, completion will fail with::
(invalid-regexp "Regular expression too big")

- External completion packages can be broken by this, including corfu:
https://github.com/minad/corfu/issues/350

We fix this by mirroring the check on the character at start of the
region.  We now also check if the character at the end of the
completion region is a quotation mark or close paren, and set the end
of the region equal to the beginning in that case.  This way, we avoid
including a string or list in the completion region, but still allow
completion to proceed.

* lisp/progmodes/elisp-mode.el (elisp-completion-at-point): Avoid
returning a completion region which includes a string or
list. (bug#64903)
---
 lisp/progmodes/elisp-mode.el | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 0ce98ee471c..c9ae92b5680 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -668,6 +668,9 @@ elisp-completion-at-point
 		    (goto-char beg)
 		    (forward-sexp 1)
                     (skip-chars-backward "'’")
+                    (when (member (char-syntax (char-before (point)))
+                                  '(?\" ?\)))
+                      (goto-char beg))
 		    (when (>= (point) pos)
 		      (point)))
 		(scan-error pos))))
-- 
2.39.3