Subject: Re: bug#1085: 23.0.60; all-completions, try-completion inconsistent: Info-read-node-name-1

From: Stefan Monnier <[email-masked]>
Date: Tue, 07 Oct 2008 10:52:44 -0400

> 1. User's input should be a prefix of what it is completed to in the
> minibuffer, which should be the same as the common prefix of all its
> completions, the result of `try-completion':

Let's keep "completion in the minibuffer" for another thread and let's
focus on try0completion, all-completions, and test-competion.

>> M-: (try-completion "(el" 'Info-read-node-name-1)
>> 
>> It returns "(elisp)", meaning that this is the common prefix of all
>> completions of "(el". [This is reasonable, and it satisfies the
>> requirement that "(el" is a prefix of "(elisp)".]

I believe this is indeed a valid invariant.  At least I can't remember
any case where this needed to be broken.

> 2. `all-completions' elements correspond to `test-completion':

>> And each of the completions returned by `all-completions' must
>> also satisfy `test-completion'.  In particular,
>> (test-completion STRG (all-completions strg TABLE)) must always
>> return t, for all STRG and TABLE. In this case, for STRG = "(el" and
>> TABLE = `Info-read-node-name-1', it returns nil.

This is not a valid invariant.  E.g. let's say you want a completion
table for "png files".  You'll probably want the completion to happen
"one directory at a time", like all other file completions in Emacs, so
all-completions will have to be able to return directories, even though
these are not png files and will hence be rejected by test-completion.

> 3. `all-completions' elements correspond to `try-completion':

>> the valid completions returned by `all-completions' have the
>> common prefix that is returned by `try-completion' (which
>> must in turn have the input as its prefix [see #1]). 

As discussed, this would imply that the code that builds *Completions*
figure out which part of the prefix to drop.  So this is the feature
about which I said:

   I have banged my head against this problem for a long time, while
   working on the minibuffer.el code (and its predecessors), so I don't
   expect to change my mind any time soon.  The only thing you'd accomplish
   with your proposal is "change", but it would just shift the problems
   from one place to another without any actual benefit.

So of the 3 invariants you request, the first already holds, the second
cannot always hold, and the third will not always hold because it would
introduce problems elsewhere.

> P.S. Could you explain a little about boundaries and what you meant
> about using that feature. This is not clear to me.

minibuffer.el says:

;; - The `action' can be (additionally to nil, t, and lambda) of the form
;;   (boundaries . SUFFIX) in which case it should return
;;   (boundaries START . END).  See `completion-boundaries'.
;;   Any other return value should be ignored (so we ignore values returned
;;   from completion tables that don't know about this new `action' form).

So, (funcall TABLE STRING PRED '(boundary . "")) should return
(boundaries START END) where END should be (length STRING) or
nil, and START will be the length of the prefix of STRING which is
missing from the all-completions output (e.g. for file completion,
START should be more or less equivalent to
(length (file-name-directory STRING))).
Note that some completion tables may not know about this new
`boundaries' argument, and will hence return something different from
(boundaries START END), so any code using this new feature should treat
any other return value as equivalent to (boundaries 0 (length STRING)).

As for your invariant 3, you should be able to recover it if you use
something like:

  (defun drew-all-completions (string table pred)
    (if (not (functionp table))
        (all-completions string table pred)
      (let* ((bs (funcall table string pred '(boundaries . "")))
             (prefix (and (eq 'boundaries (car-safe bs))
                          (integerp (car-safe (cdr bs)))
                          (substring string 0 (cadr bs)))))
        (mapcar (lambda (s) (concat prefix s))
                (all-completions string table pred)))))

I'm pretty sure this code won't work as-is, but hopefully, you get
the idea.

        Stefan