bug#81629: 32.0.50; completion-pcm-leading-wildcard t

Spencer Baugh via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <[email protected]>
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
Sean Whitton <[email protected]> writes:
> lucas jimenez [15/Aug 12:04pm GMT] wrote:
>> Hi, all.
>>
>> Setting completion-pcm-leading-wildcard to `t` messes completion
>>
>> emacs -Q --eval "(setq completion-pcm-leading-wildcard t)"
>>
>> I have a directory ~/Documents/Tests/Notes/ with files inside.
>>
>> When doing `C-x C-f ~/do/te/no` (Notice I didn't ended with a slash)
>>
>> it completes to ~/Documents/Tests/no when the directory 'Notes' was the
>> only available candidate.
>>
>> And when doing `C-x C-f ~/do/t/no` it expands it to `~/Dos/t/no` When I
>> don't even have a directory starting with ~/Dos.
>
> Thanks.  Spencer, would you be able to say whether there a bug here, or
> maybe this how it's expected to work?

Yes, this is how it's expected to work.

In case 1 you probably have another directory with "no" as a substring,
so we can't expand any more than that with
completion-pcm-leading-wildcard=t.

For this reason what you probably want is something more like:

(setopt completion-styles
        '(basic
          partial-completion
          (partial-completion ((completion-pcm-leading-wildcard t)))
          emacs22))

So non-leading-wildcard completion is tried first.  That may behave more
intuitively.

In case 2, point is between "Do" and "s", right?  In partial-completion
(and Emacs completion in general) you're intended to then type
additional characters to help narrow it down.  You presumably have
multiple directories matching "Do*s*", and point is placed at the first
* to make it possible for you to select which one you want, by typing
additional characters.

That being said, if point *isn't* between "Do" and "s", that might be
fixed by the attached patch.  Which I've been running with for about 10
months, and which I forgot to push... ccing Stefan if he wants to
comment on this patch.
0001-pcm-try-completion-move-point-to-the-last-non-empty-.patch (text/x-patch, 5.1 KB)
From 1ccab0ae98103bb9b19d7480f84acf96ce52b72d Mon Sep 17 00:00:00 2001
From: Spencer Baugh <[email protected]>
Date: Thu, 20 Aug 2026 15:27:12 -0400
Subject: [PATCH] pcm-try-completion: move point to the last non-empty wildcard

completion-pcm-try-completion expands text at wildcards, and
then possibly moves point to a new position in the string.  The
goal is to allow the user to type some characters to narrow down
the completions, then hit TAB again to expand and possibly move
point again.

Previously, it always moved point to the last wildcard in the
string which could still potentially be expanded to match a
completion.  However, if the desired completion has an empty
string at the site of that wildcard, there's nothing the user
could usefully type to continue completion: the user would have
to manually move point.  So, it's better to move point to last
wildcard where all the possible completions have non-empty text,
if there is such a wildcard.  We do that now.

Also, drop the incorrect addition of "prefix" in
completion-pcm--string->pattern for
completion-pcm-leading-wildcard, that's too aggressive.

* lisp/minibuffer.el (completion-pcm--string->pattern): Don't
replace 'any-delim with 'prefix on leading-wildcard=t.
(completion-pcm--merge-completions): Push 'nonempty when all
completions are nonempty at this wildcard. (bug#81629)
(completion-pcm--merge-try): Move point to the last 'nonempty.
* test/lisp/minibuffer-tests.el (completion-pcm-test-anydelim)
(completion-substring-test-5): Update for new behavior.
---
 lisp/minibuffer.el            |  7 ++++++-
 test/lisp/minibuffer-tests.el | 10 +++++-----
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index e25961220af..14440a635eb 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -4392,7 +4392,7 @@ completion-pcm--string->pattern
               (setq p0 p)
             (push (substring string p (match-end 0)) pattern)
             ;; `any-delim' is used so that "a-b" also finds "array->beginning".
-            (setq pending (if completion-pcm-leading-wildcard 'prefix 'any-delim))
+            (setq pending 'any-delim)
             (setq p0 (match-end 0))))
         (setq p p0))
 
@@ -4801,6 +4801,10 @@ completion-pcm--merge-completions
                   (when (seq-some (lambda (elem) (eq elem 'prefix)) wildcards)
                     (setq prefix (substring prefix 0 (length fixed))))
                   (push prefix res)
+                  (when (seq-every-p (lambda (comp) (< (length prefix) (length comp))) comps)
+                    ;; Wherever the user could type a character to disambiguate between
+                    ;; completions, possibly move point there.
+                    (push 'nonempty res))
                   ;; Push all the wildcards in this stretch, to preserve `point' and
                   ;; `star' wildcards before ELEM.  Collapse multiple `star's down to one
                   ;; on each side of point. (bug#81394)
@@ -4880,6 +4884,7 @@ completion-pcm--merge-try
            ;; the last place where there's something to choose, or
            ;; at the very end.
            (pointpat (or (memq 'point mergedpat)
+                         (memq 'nonempty mergedpat)
                          (memq 'any   mergedpat)
                          (memq 'star  mergedpat)
                          ;; Not `prefix'.
diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el
index ab40b40c7e4..fa4d65d7fce 100644
--- a/test/lisp/minibuffer-tests.el
+++ b/test/lisp/minibuffer-tests.el
@@ -373,7 +373,7 @@ completion-pcm-test-anydelim
   ;; sequence of delimiters.
   (should (equal (completion-pcm-try-completion
                   "-x" '("-_.x" "-__x") nil 2)
-                 '("-_x" . 3))))
+                 '("-_x" . 2))))
 
 (ert-deftest completion-pcm-test-pattern->regex ()
   (should (equal (completion-pcm--pattern->regex
@@ -476,7 +476,7 @@ completion-substring-test-5
   ;; prefix is also a common suffix, it should be included.
   (should (equal
            (completion-pcm--merge-try '(prefix "b") '("ab" "sab") "" "")
-           '("ab" . 2)))
+           '("ab" . 0)))
   (should (equal
            (completion-pcm--merge-try '(prefix "b") '("ab" "ab") "" "")
            '("ab" . 2)))
@@ -484,16 +484,16 @@ completion-substring-test-5
   ;; should always be included.
   (should (equal
            (completion-pcm--merge-try '("a" prefix "b") '("axb" "ayb") "" "")
-           '("ab" . 2)))
+           '("ab" . 1)))
   ;; Letter-casing from the completions on the common prefix is still applied.
   (should (equal
            (let ((completion-ignore-case t))
              (completion-pcm--merge-try '("a" prefix "b") '("Axb" "Ayb") "" ""))
-           '("Ab" . 2)))
+           '("Ab" . 1)))
   (should (equal
            (let ((completion-ignore-case t))
              (completion-pcm--merge-try '("a" prefix "b") '("AAxb" "AAyb") "" ""))
-           '("Ab" . 2)))
+           '("Ab" . 1)))
   ;; substring completion should successfully complete the entire string
   (should (equal
            (completion-substring-try-completion "b" '("ab" "ab") nil 0)
-- 
2.43.7
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.