Subject: Re: bug#77718: 31.0.50; completion styles substring and flex are broken

From: Stephen Berman <[email-masked]>
Date: Wed, 18 Jun 2025 14:01:54 +0200

On Wed, 18 Jun 2025 01:00:31 +0200 Stephen Berman <[email-masked]> wrote:

> On Tue, 17 Jun 2025 15:26:36 -0400 Spencer Baugh <[email-masked]> wrote:
>
>> Stephen Berman <[email-masked]> writes:
>>
>>> On Tue, 17 Jun 2025 10:59:56 -0400 Spencer Baugh <[email-masked]> wrote:
> [...]
>> This is the behavior of the substring completion style.  When there's a
>> common substring between all the completions,
>> completion-substring-try-completion inserts that substring.  Since the
>> substring is a common suffix, it positions point before that common
>> suffix.
> [...]
>> To have only "substring" configured as a completion style implies you
>> want the behavior of the "substring" completion style, and only that
>> behavior.  You are indeed getting that behavior.  What's the problem?
>> substring has always behaved like this, so improved consistency in edge
>> cases like this one should not be a surprise.
>
> You may consider this an edge case, but it is proof that the substring
> style has not "always behaved like this".  I don't recall that the goal
> of "improved consistency in edge cases" was previously cited in this
> thread; if it had been, I might have viewed the issue in a different
> light, though I still find the current behavior problematic...
>
>> Is there some problem with this behavior, some reason it doesn't work
>> right for you?  A more concrete complaint about what doesn't work would
>> be helpful.
>
> ...to wit:
>
> Before your change, when using substring style, typing `C-x d /us TAB
> TAB' completes to "/usr/" and pops up the *Completions* buffer.  If I
> want to change the input, e.g. to "/var/", I can type `M-DEL va TAB'.
>
> After your change, when using substring style, typing `C-x d /us TAB
> TAB' completes to "/usr//" and only after typing TAB a third time does
> the *Completions* buffer pop up.  If I want to change the input, typing
> `M-DEL' has no effect, though e.g. `C-f / va TAB' works (the minibuffer
> then displays "~//usr//var/" with "~//usr/" in file-name-shadow face).
> It works, but it's a bit more cumbersome than previously (an extra TAB
> to get the *Completions* buffer) and visually confusing (at least given
> familiarity with the previous behavior).
>
> And again, with my patch (posted in
> https://lists.gnu.org/archive/html/bug-gnu-emacs/2025-04/msg01860.html)
> I get the behavior and appearance I'm used to, and for my usage have
> encountered no problems with it.  I ask you once again: please tell me
> specifically what problems that patch causes.

Assuming the current behavior of substring completion in master is not a
bug/regression but the intended "improved consistency", I would like to
propose a different patch (attached) to make the completion behavior
conditional on a user option, defaulting to the current behavior in
master.

Steve Berman

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 122459be062..598383e7b51 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -4516,6 +4516,14 @@ completion--common-suffix
   "Return the common suffix of the strings STRS."
   (nreverse (try-completion "" (mapcar #'reverse strs))))
 
+(defcustom completion-/-not-common-suffix nil
+  "If non-nil, ignore \"/\" as common suffix.
+With the substring completion style enabled, this prevents displaying
+\"/\" as the common substring suffix when all completion candidate end
+in \"/\" (that was the only dispay prior to Emacs 31)."
+  :version "31.1"
+  :type 'boolean)
+
 (defun completion-pcm--merge-completions (strs pattern)
   "Extract the commonality in STRS, with the help of PATTERN.
 PATTERN can contain strings and symbols chosen among `star', `any', `point',
@@ -4622,7 +4630,9 @@ completion-pcm--merge-completions
                                 (mapcar (lambda (str) (substring str skip))
                                         comps))))))
                       (cl-assert (stringp suffix))
-                      (unless (equal suffix "")
+                      (unless (if completion-/-not-common-suffix
+                                  (member suffix '("" "/"))
+                                (equal suffix ""))
                         (push suffix res))))
                   ;; We pushed these wildcards on RES, so we're done with them.
                   (setq wildcards nil))