emacs-31 82009df4de5: Fix initials completion style after // in file name

Sean Whitton <[email protected]> Thu, 2 Jul 2026 05:40:26 -0400 (EDT)
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: emacs-31
commit 82009df4de536366097db9c28a139469b3e81ff0
Author: Spencer Baugh <[email protected]>
Commit: Sean Whitton <[email protected]>

    Fix initials completion style after // in file name
    
    * lisp/minibuffer.el (completion-initials-expand): Change the
    heuristic to check for an empty previous field, not total string
    length (bug#81241).
    * test/lisp/minibuffer-tests.el (completion-initials): New test.
---
 lisp/minibuffer.el            |  8 +++++---
 test/lisp/minibuffer-tests.el | 12 ++++++++++++
 2 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 4db3d0d0bea..5b1486d2af4 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -5070,12 +5070,14 @@ usual. Returns (ALL PAT PREFIX SUFFIX)."
         ;; to /usr/share/a/e just because we mistyped "ae" for "ar",
         ;; so we probably don't want initials to touch anything that
         ;; looks like /usr/share/foo.  As a heuristic, we just check that
-        ;; the text before the boundary char is at most 1 char.
-        ;; This allows both ~/eee and /eee and not much more.
+        ;; the previous completion field is empty.
+        ;; This allows ~/eee and /eee and /usr//eee and not much more.
         ;; FIXME: It sadly also disallows the use of ~/eee when that's
         ;; embedded within something else (e.g. "(~/eee" in Info node
         ;; completion or "ancestor:/eee" in bzr-revision completion).
-        (when (< (car bounds) 3)
+        (when (let ((str-without-last-field (substring str 0 (1- (car bounds)))))
+                (= (car (completion-boundaries str-without-last-field table pred ""))
+                   (length str-without-last-field)))
           (let ((sep (substring str (1- (car bounds)) (car bounds))))
             ;; FIXME: the above string-match checks the whole string, whereas
             ;; we end up only caring about the after-boundary part.
diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el
index 1db0c07f510..59ae6ae3758 100644
--- a/test/lisp/minibuffer-tests.el
+++ b/test/lisp/minibuffer-tests.el
@@ -355,6 +355,18 @@
                      ("sources/clang" "sys/class")
                      "pcm/" "")))))
 
+(ert-deftest completion-initials ()
+  ;; Should expand initials:
+  (should (equal (completion-initials-expand "/ttab" #'read-file-name-internal nil)
+                 "/t/t/a/b"))
+  (should (equal (completion-initials-expand "~/ttab" #'read-file-name-internal nil)
+                 "~/t/t/a/b"))
+  (should (equal (completion-initials-expand "/home//ttab" #'read-file-name-internal nil)
+                 "/home//t/t/a/b"))     ; bug#81241
+  ;; Should not expand initials:
+  (should-not (completion-initials-expand "/x/ttab" #'read-file-name-internal nil))
+  (should-not (completion-initials-expand "/usr/share/ttab" #'read-file-name-internal nil)))
+
 (ert-deftest completion-pcm-test-anydelim ()
   ;; After each delimiter is a special wildcard which matches any
   ;; sequence of delimiters.