Subject: bug#50470: 27.1; 'company-mode' 'eshell'

From: Stefan Monnier <[email-masked]>
Date: Fri, 10 Dec 2021 08:10:43 -0500

> In my package capf-autosuggest, I run completion-at-point-functions
> somewhat like this:
>
>     (let (;; `pcomplete-completions-at-point' may illegally use
>           ;; `completion-in-region' itself instead of returning a collection.
>           ;; Let's try to outsmart it.
>           (completion-in-region-function
>            (lambda (start end collection predicate)
>              (throw 'illegal-comp-in-region
>                     (list start end collection :predicate predicate))))
>           ;; Prevent `pcomplete-completions-at-point' from inserting a TAB
>           (buffer-read-only t))
>       ;; `ielm-complete-filename' may illegaly move point
>       (save-excursion
>         (condition-case nil
>             (catch 'illegal-comp-in-region
>               (run-hook-wrapped 'completion-at-point-functions ...))
>           (buffer-read-only nil))))
>
> This way, old style capf functions are prevented from inserting a TAB or
> moving point.

Hmm... capf itself tries to "solve" that problem in the following way:

    (defvar completion--capf-misbehave-funs nil
      "List of functions found on `completion-at-point-functions' that misbehave.
    These are functions that neither return completion data nor a completion
    function but instead perform completion right away.")
    (defvar completion--capf-safe-funs nil
      "List of well-behaved functions found on `completion-at-point-functions'.
    These are functions which return proper completion data rather than
    a completion function or god knows what else.")
    
    (defun completion--capf-wrapper (fun which)
      ;; FIXME: The safe/misbehave handling assumes that a given function will
      ;; always return the same kind of data, but this breaks down with functions
      ;; like comint-completion-at-point or mh-letter-completion-at-point, which
      ;; could be sometimes safe and sometimes misbehaving (and sometimes neither).
      (if (pcase which
            ('all t)
            ('safe (member fun completion--capf-safe-funs))
            ('optimist (not (member fun completion--capf-misbehave-funs))))
          (let ((res (funcall fun)))
            (cond
             ((and (consp res) (not (functionp res)))
              (unless (member fun completion--capf-safe-funs)
                (push fun completion--capf-safe-funs))
              (and (eq 'no (plist-get (nthcdr 3 res) :exclusive))
                   ;; FIXME: Here we'd need to decide whether there are
                   ;; valid completions against the current text.  But this depends
                   ;; on the actual completion UI (e.g. with the default completion
                   ;; it depends on completion-style) ;-(
                   ;; We approximate this result by checking whether prefix
                   ;; completion might work, which means that non-prefix completion
                   ;; will not work (or not right) for completion functions that
                   ;; are non-exclusive.
                   (null (try-completion (buffer-substring-no-properties
                                          (car res) (point))
                                         (nth 2 res)
                                         (plist-get (nthcdr 3 res) :predicate)))
                   (setq res nil)))
             ((not (or (listp res) (functionp res)))
              (unless (member fun completion--capf-misbehave-funs)
                (message
                 "Completion function %S uses a deprecated calling convention" fun)
                (push fun completion--capf-misbehave-funs))))
            (if res (cons fun res)))))

    (defun completion-at-point ()
      "Perform completion on the text around point.
    The completion method is determined by `completion-at-point-functions'."
      (interactive)
      (let ((res (run-hook-wrapped 'completion-at-point-functions
                                   #'completion--capf-wrapper 'all)))
        ...))

Maybe this should be improved/refined?

        Stefan