[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
scratch/eldoc-async cde8a6a 1/6: Better handle asynchronously produced e
From: |
João Távora |
Subject: |
scratch/eldoc-async cde8a6a 1/6: Better handle asynchronously produced eldoc docstrings |
Date: |
Wed, 3 Jun 2020 13:54:06 -0400 (EDT) |
branch: scratch/eldoc-async
commit cde8a6ab981fc67789b9cc036bd783201c9e5ae5
Author: João Távora <joaotavora@gmail.com>
Commit: João Távora <joaotavora@gmail.com>
Better handle asynchronously produced eldoc docstrings
No longer do clients of eldoc need to call eldoc-message (an internal
function) directly. They may return any non-nil, non-string value and
call a callback afterwards. This enables eldoc.el to exert control
over how (and crucially also when) to display the docstrings to the
user.
* lisp/emacs-lisp/eldoc.el (eldoc-documentation-functions):
Overhaul docstring.
(eldoc-documentation-compose, eldoc-documentation-default): Handle
non-nil, non-string values of elements of
eldoc-documentation-functions. Use eldoc--handle-multiline.
(eldoc-print-current-symbol-info): Honour non-nil, non-string
values returned by eldoc-documentation-callback.
(eldoc--handle-multiline): New helper.
* lisp/hexl.el (hexl-print-current-point-info): Adjust to new
eldoc-documentation-functions protocol.
* lisp/progmodes/cfengine.el (cfengine3-documentation-function):
Adjust to new eldoc-documentation-functions protocol.
* lisp/progmodes/elisp-mode.el
(elisp-eldoc-documentation-function): Adjust to new
eldoc-documentation-functions protocol.
* lisp/progmodes/octave.el (octave-eldoc-function): Adjust to new
eldoc-documentation-functions protocol.
* lisp/progmodes/python.el (python-eldoc-function): Adjust to new
eldoc-documentation-functions protocol.
---
lisp/emacs-lisp/eldoc.el | 109 ++++++++++++++++++++++++++++++++-----------
lisp/hexl.el | 2 +-
lisp/progmodes/cfengine.el | 2 +-
lisp/progmodes/elisp-mode.el | 6 ++-
lisp/progmodes/octave.el | 4 +-
lisp/progmodes/python.el | 2 +-
6 files changed, 91 insertions(+), 34 deletions(-)
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index ef5dbf8..4c05ef9 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -338,43 +338,70 @@ Also store it in `eldoc-last-message' and return that
value."
(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.
-
-Major modes should modify this hook locally, for example:
+ "Hook of functions that produce doc strings.
+
+A doc string is typically relevant if point is on a function-like
+name, inside its arg list, or on any object with some associated
+information.
+
+Each hook function should accept at least one argument CALLBACK
+and decide whether to display a doc short string about the
+context around point. If the decision and the doc string can be
+produced quickly, the hook function can ignore CALLBACK and
+immediately return the doc string, or nil if there's no doc
+appropriate for the context. Otherwise, if the computation of
+said docstring is expensive or can't be performed directly, the
+hook function should return a non-nil, non-string value and then
+arrange for CALLBACK to be called at a later time.
+
+That call is expected to pass CALLBACK a single argument
+DOCSTRING followed by an optional list of keyword-value pairs of
+the form (:KEY VALUE :KEY2 VALUE2...). KEY can be:
+
+* `:hint', whereby the corresponding VALUE should be a short
+ string designating the thing being reported on by the hook
+ function.
+
+Note that this hook is only in effect if the value of
+`eldoc-documentation-function' (notice the singular) is bound to
+one of its pre-set values. Major modes should modify this hook
+locally, for example:
(add-hook \\='eldoc-documentation-functions #\\='foo-mode-eldoc nil t)
so that the global value (i.e. the default value of the hook) is
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))))))
+
+;; this variable should be unbound, but that confuses
+;; `describe-symbol' for some reason.
+(defvar eldoc--make-callback nil
+ "Dynamically bound to a nullary function that returns a callback.
+Besides producing the callback that is passed to
+`eldoc-documentation-functions', this also remembers the
+callbacks relative order in the queue of callbacks waiting to be
+called. ")
+
(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))))))))
+ (run-hook-with-args-until-success 'eldoc-documentation-functions
+ (funcall eldoc--make-callback)))
(defun eldoc-documentation-compose ()
"Show multiple doc string results at once.
Meant as a value for `eldoc-documentation-function'."
- (let (res)
- (run-hook-wrapped
- 'eldoc-documentation-functions
- (lambda (f)
- (let ((str (funcall f)))
- (when 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))))))))
+ (run-hook-wrapped 'eldoc-documentation-functions
+ (lambda (f)
+ (let* ((callback (funcall eldoc--make-callback))
+ (str (funcall f callback)))
+ (if (stringp str) (funcall callback str))
+ nil)))
+ t)
(defcustom eldoc-documentation-function #'eldoc-documentation-default
"Function to call to return doc string.
@@ -417,11 +444,39 @@ effect."
;; 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* (;; `want' and `received' keep track of how many
+ ;; docstrings we expect from the clients.
+ (pos 0) (want 0) (received '())
+ (receive-doc
+ (lambda (_pos string _plist)
+ (with-current-buffer buffer
+ (when (and string (> (length string) 0))
+ (push string received))
+ (setq want (1- want))
+ (when (zerop want)
+ (eldoc-message
+ (eldoc--handle-multiline
+ (mapconcat #'identity
+ (nreverse received)
+ ",")))))))
+ (eldoc--make-callback
+ (lambda ()
+ (setq want (1+ want))
+ (let ((pos (setq pos (1+ pos))))
+ (lambda (string &rest plist)
+ (funcall receive-doc pos string plist)))))
+ (res (funcall eldoc-documentation-function)))
+ (cond (;; old protocol: got string, output immediately
+ (stringp res) (setq want 1) (funcall receive-doc 0 res nil))
+ (;; old protocol: got nil, clear the echo area
+ (null res) (eldoc-message nil))
+ (;; got something else, trust callback will be called
+ t) )))))))
;; 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
diff --git a/lisp/hexl.el b/lisp/hexl.el
index cf7118f..38eca77 100644
--- a/lisp/hexl.el
+++ b/lisp/hexl.el
@@ -515,7 +515,7 @@ Ask the user for confirmation."
(message "Current address is %d/0x%08x" hexl-address hexl-address))
hexl-address))
-(defun hexl-print-current-point-info ()
+(defun hexl-print-current-point-info (&rest _ignored)
"Return current hexl-address in string.
This function is intended to be used as eldoc callback."
(let ((addr (hexl-current-address)))
diff --git a/lisp/progmodes/cfengine.el b/lisp/progmodes/cfengine.el
index f25b3cb..9a6d81c 100644
--- a/lisp/progmodes/cfengine.el
+++ b/lisp/progmodes/cfengine.el
@@ -1294,7 +1294,7 @@ Calls `cfengine-cf-promises' with \"-s json\"."
'symbols))
syntax)))
-(defun cfengine3-documentation-function ()
+(defun cfengine3-documentation-function (&rest _ignored)
"Document CFengine 3 functions around point.
Intended as the value of `eldoc-documentation-function', which see.
Use it by enabling `eldoc-mode'."
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 8812c49..5e32b25 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -1403,8 +1403,10 @@ which see."
or argument string for functions.
2 - `function' if function args, `variable' if variable documentation.")
-(defun elisp-eldoc-documentation-function ()
- "`eldoc-documentation-function' (which see) for Emacs Lisp."
+(defun elisp-eldoc-documentation-function (_ignored &rest _also-ignored)
+ "Contextual documentation function for Emacs Lisp.
+Intended to be placed in `eldoc-documentation-functions' (which
+see)."
(let ((current-symbol (elisp--current-symbol))
(current-fnsym (elisp--fnsym-in-current-sexp)))
(cond ((null current-fnsym)
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index 352c181..2cf305c 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -1639,8 +1639,8 @@ code line."
(nreverse result)))))
(cdr octave-eldoc-cache))
-(defun octave-eldoc-function ()
- "A function for `eldoc-documentation-function' (which see)."
+(defun octave-eldoc-function (&rest _ignored)
+ "A function for `eldoc-documentation-functions' (which see)."
(when (inferior-octave-process-live-p)
(let* ((ppss (syntax-ppss))
(paren-pos (cadr ppss))
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 1ca9f01..404a67b 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -4571,7 +4571,7 @@ returns will be used. If not FORCE-PROCESS is passed what
:type 'boolean
:version "25.1")
-(defun python-eldoc-function ()
+(defun python-eldoc-function (&rest _ignored)
"`eldoc-documentation-function' for Python.
For this to work as best as possible you should call
`python-shell-send-buffer' from time to time so context in
- branch scratch/eldoc-async created (now 10834f2), João Távora, 2020/06/03
- scratch/eldoc-async cde8a6a 1/6: Better handle asynchronously produced eldoc docstrings,
João Távora <=
- scratch/eldoc-async 0612bb7 4/6: Introduce eldoc-prefer-doc-buffer defcustom, João Távora, 2020/06/03
- scratch/eldoc-async fe93e5b 5/6: Make eldoc-print-current-symbol-info a command, João Távora, 2020/06/03
- scratch/eldoc-async 10834f2 6/6: * lisp/emacs-lisp/eldoc.el (Version): Bump to 1.1.0, João Távora, 2020/06/03
- scratch/eldoc-async 600b9c0 2/6: New eldoc-documentation-eager value for eldoc-documentation-function, João Távora, 2020/06/03
- scratch/eldoc-async 40d4506 3/6: Overhaul and handle (most of) eldoc-echo-area-use-multiline-p in Eldoc itself, João Távora, 2020/06/03