unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Dmitry Gutov <dgutov@yandex.ru>
To: "João Távora" <joaotavora@gmail.com>, 41531@debbugs.gnu.org
Cc: Stefan Monnier <monnier@iro.umontreal.ca>, andreyk.mad@gmail.com
Subject: bug#41531: 27.0.91; Better handle asynchronous eldoc backends
Date: Tue, 26 May 2020 02:52:58 +0300	[thread overview]
Message-ID: <4987863b-d390-5f87-eb1c-2cca4f4b7262@yandex.ru> (raw)
In-Reply-To: <875zckuet9.fsf@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1027 bytes --]

On 25.05.2020 20:04, João Távora wrote:
> Hi Stefan, Dmitry, Andrii and maintainers,
> 
> Moving the discussion that started in
> https://github.com/joaotavora/eglot/pull/459  to the bug tracker, and
> attaching the two patches that contain what I think is a decent
> short-term solution to the eldoc/async problems.

Here's a modified approach that doesn't use a global var and should make 
it easier to transition to using futures.

Patch attached. Example of usage:

(add-hook 'eldoc-documentation-functions
           #'test-eldoc-async 0 t)

(defun test-eldoc-async ()
   (cons :async
         (lambda (cb)
           (funcall cb "doc here!"))))

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.

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.

[-- Attachment #2: eldoc-async-with-lexical-callback.diff --]
[-- Type: text/x-patch, Size: 5129 bytes --]

diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index ef5dbf8103..8909e8d431 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -5,7 +5,7 @@
 ;; Author: Noah Friedman <friedman@splode.com>
 ;; Keywords: extensions
 ;; Created: 1995-10-06
-;; Version: 1.0.0
+;; Version: 1.1.0
 ;; Package-Requires: ((emacs "26.3"))
 
 ;; This is a GNU ELPA :core package.  Avoid functionality that is not
@@ -338,12 +338,26 @@ eldoc-display-message-no-interference-p
 
 \f
 (defvar eldoc-documentation-functions nil
-  "Hook for functions to call to return doc string.
-Each function should accept no arguments and return a one-line
-string for displaying doc about a function etc. appropriate to
-the context around point.  It should return nil if there's no doc
-appropriate for the context.  Typically doc is returned if point
-is on a function-like name or in its arg list.
+  "Hook of functions that produce doc strings.
+Each hook function should accept no arguments and decide whether
+to display a doc short string about the context around point.
+
+Typically doc is returned if point is on a function-like name or
+in its arg list.
+
+If the decision and the doc string can be produced quickly, the
+hook function should immediately return the doc string, or nil if
+there's no doc appropriate for the context.  Otherwise, if its
+computation is expensive or can't be performed directly, the
+function can instead return a cons (:async . FETCHER) where where
+FETCHER is a function of one argument, CALLBACK.  When the result
+arrives, FETCHER must call CALLBACK and pass it the doc string to
+be displayed.
+
+A current limitation of the asynchronous case is that it is only
+guaranteed to work correctly if the value of
+`eldoc-documentation-function' (notice the singular) is
+`eldoc-documentation-default'.
 
 Major modes should modify this hook locally, for example:
   (add-hook \\='eldoc-documentation-functions #\\='foo-mode-eldoc nil t)
@@ -351,14 +365,18 @@ eldoc-documentation-functions
 taken into account if the major mode specific function does not
 return any documentation.")
 
+(defun eldoc--handle-multiline (res)
+  "Helper for handling a bit of `eldoc-echo-area-use-multiline-p'."
+  (if eldoc-echo-area-use-multiline-p res
+    (truncate-string-to-width
+     res (1- (window-width (minibuffer-window))))))
+
 (defun eldoc-documentation-default ()
   "Show first doc string for item at point.
 Default value for `eldoc-documentation-function'."
   (let ((res (run-hook-with-args-until-success 'eldoc-documentation-functions)))
-    (when res
-      (if eldoc-echo-area-use-multiline-p res
-        (truncate-string-to-width
-         res (1- (window-width (minibuffer-window))))))))
+    (cond ((stringp res) (eldoc--handle-multiline res))
+          (t res))))
 
 (defun eldoc-documentation-compose ()
   "Show multiple doc string results at once.
@@ -368,13 +386,11 @@ eldoc-documentation-compose
      'eldoc-documentation-functions
      (lambda (f)
        (let ((str (funcall f)))
-         (when str (push str res))
+         (when (stringp str) (push str res))
          nil)))
     (when res
       (setq res (mapconcat #'identity (nreverse res) ", "))
-      (if eldoc-echo-area-use-multiline-p res
-        (truncate-string-to-width
-         res (1- (window-width (minibuffer-window))))))))
+      (eldoc--handle-multiline res))))
 
 (defcustom eldoc-documentation-function #'eldoc-documentation-default
   "Function to call to return doc string.
@@ -417,11 +433,30 @@ eldoc-print-current-symbol-info
         ;; Erase the last message if we won't display a new one.
         (when eldoc-last-message
           (eldoc-message nil))
-      (let ((non-essential t))
+      (let ((non-essential t)
+            (buffer (current-buffer)))
         ;; Only keep looking for the info as long as the user hasn't
         ;; requested our attention.  This also locally disables inhibit-quit.
         (while-no-input
-          (eldoc-message (funcall eldoc-documentation-function)))))))
+          (let*
+              ((waiting-for-callback t)
+               (callback
+                (lambda (string)
+                  (with-current-buffer buffer
+                    ;; JT@2020-05-25: Currently, we expect one single
+                    ;; docstring from the client, we silently swallow
+                    ;; anything the client unexpectedly gives us,
+                    ;; including updates.  This could change.
+                    (when waiting-for-callback
+                      (setq waiting-for-callback nil)
+                      (eldoc-message (eldoc--handle-multiline string))))))
+               (res
+                (funcall eldoc-documentation-function)))
+            (cond ((stringp res) (eldoc-message res))
+                  ((and (consp res)
+                        (eq (car res) :async))
+                   (funcall (cdr res) callback))
+                  (t (eldoc-message nil)))))))))
 
 ;; If the entire line cannot fit in the echo area, the symbol name may be
 ;; truncated or eliminated entirely from the output to make room for the

  reply	other threads:[~2020-05-25 23:52 UTC|newest]

Thread overview: 84+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-25 17:04 bug#41531: 27.0.91; Better handle asynchronous eldoc backends João Távora
2020-05-25 23:52 ` Dmitry Gutov [this message]
2020-05-26  1:21   ` João Távora
2020-05-26 13:57     ` Dmitry Gutov
2020-05-26 16:03       ` João Távora
2020-05-26 19:14         ` Dmitry Gutov
2020-05-26 20:00           ` João Távora
2020-05-27 21:14             ` Dmitry Gutov
2020-05-27 22:13               ` João Távora
2020-05-27 23:35                 ` Dmitry Gutov
2020-05-27 23:57                   ` João Távora
2020-05-26  2:38   ` Stefan Monnier
2020-05-26 11:22     ` João Távora
2020-05-26 14:53       ` Stefan Monnier
2020-05-26 15:19         ` João Távora
2020-05-26 15:56           ` Stefan Monnier
2020-05-26 16:26             ` João Távora
2020-05-26 17:39               ` Stefan Monnier
2020-05-26 18:49                 ` João Távora
2020-06-03  2:45                   ` Stefan Monnier
2020-06-03 18:07                     ` João Távora
2020-06-03 20:22                       ` Stefan Monnier
2020-06-03 20:36                         ` João Távora
2020-06-03 21:21                           ` Stefan Monnier
2020-06-05 11:26                             ` João Távora
2020-06-03 21:28                       ` Dmitry Gutov
2020-06-06  1:57         ` Dmitry Gutov
2020-05-26 13:32     ` Dmitry Gutov
2020-05-26 16:56       ` João Távora
2020-06-03 18:56 ` bug#41531: 28.0.50; proper Eldoc async support João Távora
2020-06-04 16:20   ` Andrii Kolomoiets
2020-06-04 18:22     ` Dmitry Gutov
2020-06-04 19:00       ` Andrii Kolomoiets
2020-06-05 22:53         ` João Távora
2020-06-05 11:00     ` João Távora
2020-06-05 17:50       ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2020-06-05 23:25         ` João Távora
2020-06-05 23:28         ` João Távora
2020-06-11 11:11       ` Andrii Kolomoiets
2020-06-30 11:31 ` bug#41531: 27.0.91; Better handle asynchronous eldoc backends João Távora
2020-07-04  7:45   ` Eli Zaretskii
2020-07-04  9:21     ` João Távora
2020-07-04  9:31       ` Eli Zaretskii
2020-07-04  9:37         ` João Távora
2020-07-04  9:44           ` Eli Zaretskii
2020-07-04 11:00     ` João Távora
2020-07-04 21:06       ` Dmitry Gutov
2020-07-04 23:12         ` João Távora
2020-07-07  0:43           ` Dmitry Gutov
2020-07-07 10:58             ` João Távora
2020-07-07 14:18               ` Dmitry Gutov
2020-07-07 14:34                 ` João Távora
2020-07-05 12:03     ` João Távora
2020-07-05 15:09       ` Eli Zaretskii
2020-07-05 15:13       ` Stefan Monnier
2020-07-04 10:04   ` Dmitry Gutov
2020-07-04 11:48     ` João Távora
2020-07-04 21:27       ` Dmitry Gutov
2020-07-04 21:30         ` Dmitry Gutov
2020-07-04 23:07         ` João Távora
2020-07-07  3:01           ` Dmitry Gutov
2020-07-07 10:56             ` João Távora
2020-07-07 12:23               ` João Távora
2020-07-07 13:38               ` Stefan Monnier
2020-07-07 14:24                 ` Dmitry Gutov
2020-07-07 16:07                   ` Stefan Monnier
2020-07-07 23:11                     ` Dmitry Gutov
2020-07-08  3:58                       ` Stefan Monnier
2020-07-08 11:20                         ` Dmitry Gutov
2020-07-08 13:25                           ` Stefan Monnier
2020-07-08 13:41                             ` João Távora
2020-07-08 14:21                             ` Dmitry Gutov
2020-07-08 15:12                               ` João Távora
2020-07-08 18:32                                 ` Dmitry Gutov
2020-07-08 19:12                                   ` Eli Zaretskii
2020-07-07 14:45                 ` João Távora
2020-07-07 14:40               ` Dmitry Gutov
2020-07-07 22:24               ` Dmitry Gutov
2020-07-07 22:49                 ` João Távora
2020-07-07 23:00                   ` Dmitry Gutov
2020-07-07 23:24                     ` João Távora
2020-07-07 23:42                       ` Dmitry Gutov
2020-07-07 23:46                         ` João Távora
2020-07-08  0:10                           ` Dmitry Gutov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4987863b-d390-5f87-eb1c-2cca4f4b7262@yandex.ru \
    --to=dgutov@yandex.ru \
    --cc=41531@debbugs.gnu.org \
    --cc=andreyk.mad@gmail.com \
    --cc=joaotavora@gmail.com \
    --cc=monnier@iro.umontreal.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).