Subject: Re: bug#16555: 24.3.50; Company and CAPF: dealing with completion values containing extra text

From: Dmitry Gutov <[email-masked]>
Date: Tue, 28 Jan 2014 07:37:50 +0200

On 27.01.2014 04:46, Stefan Monnier wrote:
>> * Without the additional text property, a1 and a2 are considered equal, and
>> only one of them is listed in the completions buffer.
>
> Hmm??  AFAIK even with the text property, they are considered equal, so
> it's odd that it would influence whether only one or both are displayed.
> AFAIK in *Completions* "duplicates" are only eliminated if their
> "text+annotation" is identical.

Indeed, sorry, I made a wrong conclusion (when it tried it, the 
annotation function didn't work because it used the `eq' test).

But should we document that the only way to retain the information about 
different annotations corresponding to equal strings is to use text 
properties? I don't see any other way. Like mentioned, hash tables don't 
work:

(add-hook 'completion-at-point-functions
            (lambda ()
              (let ((bounds (bounds-of-thing-at-point 'symbol)))
                (when bounds
                  (list
                   (car bounds)
                   (cdr bounds)
                   (list a1 a2 a3)
                   :annotation-function #'annota)))) nil t)

(setq tt (make-hash-table :test 'eq)
       a1 "aaa"
       a2 "aaa"
       a3 "aab")

(puthash a1 "a1" tt)
(puthash a2 "a2" tt)
(puthash a3 "a3" tt)

(defun annota (s)
   (format "(%s)"
    (gethash s tt "?")))

aa| -> aaa(?), aab(?)

`company-capf' would be affected, too, since it uses 
`completion-all-completions' now, and the latter, via 
`completion-basic-all-completions', passes the completions through 
`completion-hilit-commonality', which intentionally creates new strings.

And if text properties is the only way, maybe disperse with the 
annotation function and just document the desired property on the strings?

> ELISP> (js2-time (all-completions "" obarray 'fboundp))
>> 0.0121
> ELISP> (js2-time (mapcar
>>             (lambda (s)
>>               (if (> (length s) 2)
>>                   (propertize s 's (substring s (/ (length s) 2)))
>>                 s))
>>             (all-completions "" obarray 'fboundp)))
>> 0.1318
>
>> The second measurement fluctuates between 130ms and 80ms, probably due to
>> GC.  Maybe this is negligible, considering that on my machine
>> that's a collection with 19000 elements, and most completion lists will be
>> considerably smaller.
>
> While I don't want to minimize the performance problem, you also have to
> take into account the fact that the whole completion operation will
> actually do something with those strings, so 19K candidates will
> typically suffer from performance problems elsewhere.

Well, I'm not sure: at least in this case, as you can see, fetching the 
candidates is an order or magnitude faster than separating their 
annotations. `all-completions' and `try-completion' are also faster than 
annotation processing, as well as `sort', but not, surprisingly, 
`delete-dups' (which can be replaced with `delete-consecutive-dups', 
however, which is also fast), and when displaying, we only need to work 
with a few elements of the whole completions list, so that takes more or 
less constant time.

But that's an extreme case: Emacs symbols, where we have the list 
readily at hand. As an opposite example, I have a completion backend 
which fetches candidates from an external Ruby process (also not the 
fastest of languages). Walking through all classes and returning all 
methods defined anywhere takes about 3.2s, the resulting list is 16K 
long in this case, and processing annotations takes 120ms. So in this 
case we're good.

>> `finished' can only be the status when the inserted completion doesn't
>> have any possible continuations in the completions table,
>
> That description was from the point of view of "TAB-style completion".
> In the case of company-complete-selection', we know that even if there
> could be further continuations, the user's action indicates he doesn't
> want those, so it really should be `finished'.
>
> IOW the problem is one of how to better document the meaning of `finished'.

But wouldn't it clash with the current completion-at-point interface? I 
don't think we can define a CAPF function without regard to how it work 
there.

>> The reverse is also true: being able not to insert the arguments list
>> for a sole candidate can also be useful, and in Company user can do that at
>> least by repeatedly using TAB (company-complete-common) instead of
>> company-complete-selection'.
>
> Then I guess that company-complete-common wouldn't want to pass
> `finished' to the exit-function.

It won't call the exit-function at all, currently, so when going though 
company-capf, exit-function would only be called with `finished'. That's 
another discrepancy, I guess.