Subject: Re: bug#41531: 27.0.91; Better handle asynchronous eldoc backends

From: Dmitry Gutov <[email-masked]>
Date: Tue, 26 May 2020 16:57:13 +0300

On 26.05.2020 04:21, João Távora wrote:
> Dmitry Gutov <[email-masked]> writes:
> 
>> On 25.05.2020 20:04, João Távora wrote:
>> (add-hook 'eldoc-documentation-functions
>>            #'test-eldoc-async 0 t)
>>
>> (defun test-eldoc-async ()
>>    (cons :async
>>          (lambda (cb)
>>            (funcall cb "doc here!"))))
> 
> Thanks.  As I told you, it's not bad.  These aren't exactly "futures"
> though, they're a way to simulate argument passing.

It's a simple stand-in, easy to replace.

> I prefer my
> version, which matches what flymake.el, url.el, jsonrpc.el, sly.el and
> others do.

They also use *special* variables? Flymake's API looks fairly clean (you 
call back to REPORTER-FN, that's very reasonable), but I haven't looked 
under the covers yet.

>> If you like, we could simplify the returned objects to be just FETCHER
>> (as documented in the patch) rather than (:async . FETCHER). But the
>> latter seems more explicit.
> 
> Yes, if we do go with your version, I'd like that.  The latter is hard
> to read and understand from the docstring.

I think it's more explicit, and once you understand it, it clicks. But 
that choice is not important to me.

> To simplify, hopefully you agree that your proposal can be summarized
> as:
> 
>    "return a function that receives a function that you
>     should call with the docstring"
> 
> whereas my proposal can be summarized as:
> 
>    "receive a function that you should call with the docstring"

To clarify, you actually included two proposals (behavior with patch 1, 
and with both patch 1 and patch 2 applied). The first one is the one I 
really didn't like. The second one is better, API-wise, but will 
conflict with the futures-based approach.

>> There also exist a possible modification of this patch with a bit of
>> functional programming where both calls to eldoc--handle-multiline
>> happen from inside of eldoc-documentation-default's definition.
> 
> Yes, that's independent of the shape of the callback we want to use.
> I'll leave that for later.  Specifically, eldoc--handle-multiline has to
> do quite a bit more handling (to satisfy Andrey's expectations).

I'm not so such it's independent. The complexity of implementing that 
would certainly vary.

BTW, maybe eldoc-message should handle this after all? Since it's the 
last link in the chain. Or even do that in the default value of 
eldoc-message-function (create a wrapper for minibuffer-message).

> Replying to parts from the other discussion in the Github tracker now.
> 
>> OK, another question: if the result still /valid/?
>                          ^^ Assuming you mean "is".
> 
> Well, if the client discovers the result to be invalid, ...

So the client will have to save and then compare the current buffer, the 
value of point and buffer-chars-modification-tick now?

>> No idea, a hypothetical one. It's an open API, not everybody is or
>> will be using LSP until the end of time. And it doesn't really have to
>> be huge. A saving is a saving.
> 
> There's no free lunch.  A very small saving in time for more complexity
> is bad.  That's what overengineered means.

Futures are not particularly more complex to use.

>> You can certainly kill the external process outside of it. Saving on
>> CPU expenses in general.
> 
> The future's creditor is the only one who could do that to any useful
> effect.  Does it have access to the process?  Probably not.

It can (barring any complex abstractions). It created the process, after 
all.

> You would
> have to return a complex future with a kill switch.  That's possible,
> but tricky, because you'd then have to be ready in the sentinel for
> another source of unexpected kills.

That would be none of ElDoc's business, though. But the implementation 
will get to be as complex as it needs.

> Why indeed?  Your other argument, that this makes the transition to
> proper futures (such as the ones in https://github.com/skeeto/emacs-aio)
> easier, is questionable, too.  There are two scenarios here:
> 
> - You want to keep backward compatibility to this API published in eldoc
>    1.1.0 until the time of the Emacs 28 release:

The one thing I want to avoid doing is changing the callsig of every 
documentation function, and then changing them back when we switch to 
futures.

>    This is something that I -- and Stefan, if I'm not mistaken, -- don't
>    think we should worry about.  Just because a package is :core GNU ELPA
>    doesn't necessarily mean we guarantee stability of its API.

Um, I'm pretty sure we guarantee a fair amount of stability.

>    But if we do, then we'll have to explain in the docstring that there
>    is a fourth return value for the hook functions.  In my version we'll
>    have to do exactly the same.
> 
> - You don't want to keep backward compatibility until Emacs 28:
> 
>    Then, when the super-futures are here, you can just kill the CALLBACK
>    arg if we find it doesn't fit and rewrite the docstring without
>    concerns.

Kill the arg in all built-in functions, as well as in external consumers?