Subject: bug#65089: 30.0.50; shell-command filename completion unexpected behavior change

From: Stefan Monnier <[email-masked]>
Date: Tue, 15 Aug 2023 10:59:14 -0400

>> Now, with emacs -Q:
>> M-!
>> ls ~/bug/bar
>> Put point between "/" and "b" of "bar" and type TAB
>> emacs says "No match", but I expected it to offer completions, foo-1 and
>> foo-2.
>> 
>> That was the behavior, at least in Emacs 28.  Reverting the following
>> commit, returns this behavior for me:

Hmm... I haven't tracked down why/how this worked before, but this is
a general problem with pcomplete when point is in the middle of
an argument.

The root cause is a problem in the API: `pcomplete-completions` (the
function that tries to figure out what it is we're trying to complete)
only tells us:

- you're trying to complete the string "~/bug/bar".
- the start position of the thing you're trying to complete (i.e. the
  point just before ~).
- the completion table.

In this case it seems obvious that the "~/bug/bar" returned is
the "~/bug/bar" found at the starting position, but in the general case,
the strings don't have to match and we have to make an educated guess
about how the returned string maps to the buffer contents (i.e. how
buffer positions map to positions within the string and vice-versa).

The patch below adds yet another heuristic to try and handle the
current case, but really we should rework the pcomplete API so as to
provide us the right info to start with.

        Stefan

diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el
index c7ec228c1db..060ddc1d853 100644
--- a/lisp/pcomplete.el
+++ b/lisp/pcomplete.el
@@ -465,6 +465,8 @@ pcomplete-completions-at-point
            ;; rely less on c-t-subvert.
            (beg (max (- (point) (length pcomplete-stub))
                      argbeg))
+           (end (point))
+           tmp
            buftext)
       ;; Try and improve our guess of `beg' in case the difference
       ;; between pcomplete-stub and the buffer's text is simply due to
@@ -472,11 +474,19 @@ pcomplete-completions-at-point
       ;; indispensable but reduces the reliance on c-t-subvert and
       ;; improves corner case behaviors.
       (while (progn (setq buftext (pcomplete-unquote-argument
-                                   (buffer-substring beg (point))))
+                                   (buffer-substring beg end)))
                     (and (> beg argbeg)
                          (> (length pcomplete-stub) (length buftext))))
         (setq beg (max argbeg (- beg (- (length pcomplete-stub)
                                         (length buftext))))))
+      ;; Try and improve our guess of `end' in case it's not point.
+      (while (and (< (length buftext) (length pcomplete-stub))
+                  (< end (point-max))
+                  (string-prefix-p (setq tmp (pcomplete-unquote-argument
+                                              (buffer-substring beg (1+ end))))
+                                   pcomplete-stub))
+        (setq end (1+ end))
+        (setq buftext tmp))
       (when completions
         (let ((table
                (completion-table-with-quoting
@@ -510,7 +520,7 @@ pcomplete-completions-at-point
                            seen)))))))
           (when completion-ignore-case
             (setq table (completion-table-case-fold table)))
-          (list beg (point) table
+          (list beg end table
                 :annotation-function
                 (lambda (cand)
                   (when (stringp cand)