Subject: bug#4708: 23.1; completion-try-completion adds an extra $: $$HOMj

From: Stefan Monnier <[email-masked]>
Date: Tue, 13 Oct 2009 22:40:12 -0400

>> File name entry assigns a special meaning to $ for envvars, 
>> but in order to be able to refer to the file "$HOME", it offers
>> an escape system where you write "$$HOME".  Since Emacs-21 or so,
>> I've made this escape unnecessary in the case where the $ is
>> used unambiguously (because there is no envvar of this name),
>> so if you enter "$HOMj", Emacs know you don't refer to the file
>> name contained in the envvar `HOMj', but to
>> the file named $HOMj.

> But there is no such file either. There is neither an envar that
> starts with `$HOMj' nor a file whose name starts with `$HOMj'.
> No matter how you look at it, there is no possible completion for
> `$HOMj'.  Based on the doc string, I would expect nil, to indicate
> that there is no possible completion.

Good point, I missed it, sorry.

That try-completion will return nil when applied to "/foo/bar/$$HOMj",
so basically the $HOMj -> $$HOMj is considered to be a form of completion.

That decision is taken by read-file-name-internal.  Can you try the
patch below to see if it improves the behavior?

>> > Also, if this is correct behavior, then please explain this 
>> > in the doc string of `completion-try-completion'.
>> It can't be described there, because that function applies to any kind
>> of completion, whereas the above only applies to completions using
>> read-file-name-internal.
> You describe some of the foibles of the implementation, but you don't
> really address the bug.

No it's not just a detail of the implementation.  The completion is
split into "completion code" and "completion tables".

> It's not clear whether you intend to do that later or you are trying
> to say that there is in fact no bug.

There is no bug w.r.t the docstring of completion-try-completion not
mentioning anything about $-escaping.

> Code that calls `completion-try-completion' does not necessarily know
> what the value of the TABLE arg is (i.e. whether it is equivalent to
> `read-file-name-internal'), so it does not necessarily know whether
> this is file-name completion or not.

Code that calls completion-try-completion has to assume that if it
returns a string, some expansion took place.  It's as simple as that.

> `completion-try-completion' needs to have an unambiguous way to
> indicate that there is no completion possible for the given input,

There is, it's the nil return value.  Note that some completion tables
may never be able to determine it and may hence never return nil.

Note that a completion table might very well allow completion from /u to
/usr/ even if none of the files in /usr/ are acceptable completion
candidates.  I.e. try-completion may sometimes return a string even if
there is no real valid completion with that prefix.  This is simply
because it may be too costly for the function to verify it (e.g. having
to traverse the whole /usr/ subtree).

        Stefan

--- minibuffer.el.~1.84.~	2009-09-24 10:49:09.000000000 -0400
+++ minibuffer.el	2009-10-13 22:36:42.000000000 -0400
@@ -1078,16 +1078,18 @@
        ((null action)
         (let ((comp (file-name-completion name realdir
                                           read-file-name-predicate)))
-          (if (stringp comp)
+          (cond
+           ((stringp comp)
               ;; Requote the $s before returning the completion.
-              (minibuffer--double-dollars (concat specdir comp))
+            (minibuffer--double-dollars (concat specdir comp)))
+           (comp
             ;; Requote the $s before checking for changes.
             (setq str (minibuffer--double-dollars str))
             (if (string-equal string str)
                 comp
               ;; If there's no real completion, but substitute-in-file-name
               ;; changed the string, then return the new string.
-              str))))
+              str)))))
 
        ((eq action t)
         (let ((all (file-name-all-completions name realdir)))