GNU bug report logs - #28257
26.0.50; [PATCH] expose eldoc functions in a hook

Previous Next

Package: emacs;

Reported by: Mark Oteiza <mvoteiza <at> udel.edu>

Date: Mon, 28 Aug 2017 02:12:01 UTC

Severity: wishlist

Tags: patch

Found in version 26.0.50

Done: Lars Ingebrigtsen <larsi <at> gnus.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 28257 in the body.
You can then email your comments to 28257 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Mon, 28 Aug 2017 02:12:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Mark Oteiza <mvoteiza <at> udel.edu>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Mon, 28 Aug 2017 02:12:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Mark Oteiza <mvoteiza <at> udel.edu>
To: bug-gnu-emacs <at> gnu.org
Subject: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Sun, 27 Aug 2017 22:10:57 -0400
Hi,

This makes it possible to use the results from multiple eldoc functions
at once, through the addition of an abnormal hook.

I'm not aware of a good way to access the default value (or other local
values) of eldoc-documentation-function when shadowed by a :before-until
advice returning non-nil.

Perhaps eldoc-documentation-function should remain a defvar, not sure.

diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 27ac0eb640..ea7f2f3d26 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -1279,9 +1279,27 @@ Lisp Doc
 for a function, it shows the argument list, and for a variable it
 shows the first line of the variable's documentation string.  To
 toggle Eldoc mode, type @kbd{M-x eldoc-mode}.  There's also a Global
-Eldoc mode, which is turned on by default, and affects buffers, such
-as @samp{*scratch*}, whose major mode is Emacs Lisp or Lisp
-Interaction (@w{@kbd{M-x global-eldoc-mode}} to turn it off globally).
+Eldoc mode, which is turned on by default, and affects buffers whose
+major mode sets the variables described below.  Use @w{@kbd{M-x
+global-eldoc-mode}} to turn it off globally.
+
+@vindex eldoc-documentation-function
+@vindex eldoc-documentation-functions
+  These variables can be used to configure ElDoc mode:
+
+@table @code
+@item eldoc-documentation-function
+This variable holds the function which is used to retrieve
+documentation for the item at point from the functions in the hook
+@code{eldoc-documentation-functions}.  By default,
+@code{eldoc-documentation-function} returns the first documentation
+string produced by the @code{eldoc-documentation-functions} hook.
+
+@item eldoc-documentation-functions
+This abnormal hook holds documentation functions.  It acts as a
+collection of backends for ElDoc. This is what modes should use to
+register their documentation functions with ElDoc.
+@end table
 
 @node Hideshow
 @section Hideshow minor mode
diff --git a/etc/NEWS b/etc/NEWS
index 3f38153048..b2dc06b137 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -791,6 +791,15 @@ breakpoint (e.g. with "f" and "o") by customizing the new option
 This allows to enlarge the maximum recursion depth when instrumenting
 code.
 
+** ElDoc
+
+*** New hook 'eldoc-documentation-functions' to be used for registering
+doc string functions.  This makes the results of all doc string
+functions accessible to the user through the existing single function hook
+'eldoc-documentation-function'.
+
+*** 'eldoc-documentation-function' is now a custom variable.
+
 ** Eshell
 
 *** 'eshell-input-filter's value is now a named function
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index bca40ab87d..7d0ff0f359 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -41,9 +41,9 @@
 ;;      (add-hook 'ielm-mode-hook 'eldoc-mode)
 ;;      (add-hook 'eval-expression-minibuffer-setup-hook 'eldoc-mode)
 
-;; Major modes for other languages may use ElDoc by defining an
-;; appropriate function as the buffer-local value of
-;; `eldoc-documentation-function'.
+;; Major modes for other languages may use ElDoc by adding an
+;; appropriate function to the buffer-local value of
+;; `eldoc-documentation-functions'.
 
 ;;; Code:
 
@@ -222,7 +222,11 @@ turn-on-eldoc-mode
 
 (defun eldoc--supported-p ()
   "Non-nil if an ElDoc function is set for this buffer."
-  (not (memq eldoc-documentation-function '(nil ignore))))
+  (let ((hook 'eldoc-documentation-functions))
+    (and eldoc-documentation-function
+         (or (and (local-variable-p hook)
+                  (buffer-local-value hook (current-buffer)))
+             (default-value hook)))))
 
 
 (defun eldoc-schedule-timer ()
@@ -341,7 +345,47 @@ eldoc-display-message-no-interference-p
 
 
 ;;;###autoload
-(defvar eldoc-documentation-function #'ignore
+(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:
+  (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-documentation-default ()
+  "Show 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))))))))
+
+(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))))))))
+
+;;;###autoload
+(defcustom eldoc-documentation-function #'eldoc-documentation-default
   "Function to call to return doc string.
 The function of no args should return a one-line string for displaying
 doc about a function etc. appropriate to the context around point.
@@ -352,14 +396,12 @@ eldoc-documentation-function
 The result is used as is, so the function must explicitly handle
 the variables `eldoc-argument-case' and `eldoc-echo-area-use-multiline-p',
 and the face `eldoc-highlight-function-argument', if they are to have any
-effect.
-
-Major modes should modify this variable using `add-function', for example:
-  (add-function :before-until (local \\='eldoc-documentation-function)
-                #\\='foo-mode-eldoc-function)
-so that the global documentation function (i.e. the default value of the
-variable) is taken into account if the major mode specific function does not
-return any documentation.")
+effect."
+  :link '(info-link "(emacs) Lisp Doc")
+  :type '(radio (function-item eldoc-documentation-default)
+                (function-item eldoc-documentation-compose)
+                (function :tag "Other function"))
+  :group 'eldoc)
 
 (defun eldoc-print-current-symbol-info ()
   "Print the text produced by `eldoc-documentation-function'."
@@ -371,7 +413,8 @@ eldoc-print-current-symbol-info
              (when eldoc-last-message
                (eldoc-message nil)
                nil))
-	 (eldoc-message (funcall eldoc-documentation-function)))))
+         (let ((fun eldoc-documentation-function))
+           (when fun (eldoc-message (funcall fun)))))))
 
 ;; 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 0a598b22f6..e1c5386c1a 100644
--- a/lisp/hexl.el
+++ b/lisp/hexl.el
@@ -395,8 +395,8 @@ hexl-mode
     (add-hook 'change-major-mode-hook 'hexl-maybe-dehexlify-buffer nil t)
 
     ;; Set a callback function for eldoc.
-    (add-function :before-until (local 'eldoc-documentation-function)
-                  #'hexl-print-current-point-info)
+    (add-hook 'eldoc-documentation-functions
+              #'hexl-print-current-point-info nil t)
     (eldoc-add-command-completions "hexl-")
     (eldoc-remove-command "hexl-save-buffer"
 			  "hexl-current-address")
@@ -506,6 +506,8 @@ hexl-mode-exit
 
   (remove-hook 'change-major-mode-hook 'hexl-maybe-dehexlify-buffer t)
   (remove-hook 'post-command-hook 'hexl-follow-ascii-find t)
+  (remove-hook 'eldoc-documentation-functions
+               #'hexl-print-current-point-info t)
   (setq hexl-ascii-overlay nil)
 
   (let ((mms ()))
diff --git a/lisp/ielm.el b/lisp/ielm.el
index 42b065fe62..3e8b8198f5 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -541,8 +541,8 @@ inferior-emacs-lisp-mode
   (set (make-local-variable 'completion-at-point-functions)
        '(comint-replace-by-expanded-history
          ielm-complete-filename elisp-completion-at-point))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'elisp-eldoc-documentation-function)
+  (add-hook 'eldoc-documentation-functions
+            #'elisp-eldoc-documentation-function nil t)
   (set (make-local-variable 'ielm-prompt-internal) ielm-prompt)
   (set (make-local-variable 'comint-prompt-read-only) ielm-prompt-read-only)
   (setq comint-get-old-input 'ielm-get-old-input)
diff --git a/lisp/progmodes/cfengine.el b/lisp/progmodes/cfengine.el
index 5bc7b66063..c775a18220 100644
--- a/lisp/progmodes/cfengine.el
+++ b/lisp/progmodes/cfengine.el
@@ -1390,12 +1390,15 @@ cfengine3-mode
                  (when buffer-file-name
                    (shell-quote-argument buffer-file-name)))))
 
-  ;; For emacs < 25.1 where `eldoc-documentation-function' defaults to
-  ;; nil.
-  (or eldoc-documentation-function
-      (setq-local eldoc-documentation-function #'ignore))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'cfengine3-documentation-function)
+  (if (> emacs-major-version 25)
+      (add-hook 'eldoc-documentation-functions
+                #'cfengine3-documentation-function nil t)
+    ;; For emacs < 25.1 where `eldoc-documentation-function' defaults
+    ;; to nil.
+    (or eldoc-documentation-function
+        (setq-local eldoc-documentation-function #'ignore))
+    (add-function :before-until (local 'eldoc-documentation-function)
+                  #'cfengine3-documentation-function))
 
   (add-hook 'completion-at-point-functions
             #'cfengine3-completion-function nil t)
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index b3f452ca5b..07210381f3 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -235,8 +235,8 @@ emacs-lisp-mode
               (append '((?\` . ?\') (?‘ . ?’)) electric-pair-text-pairs))
   (setq-local electric-quote-string t)
   (setq imenu-case-fold-search nil)
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'elisp-eldoc-documentation-function)
+  (add-hook 'eldoc-documentation-functions
+            #'elisp-eldoc-documentation-function nil t)
   (add-hook 'xref-backend-functions #'elisp--xref-backend nil t)
   (setq-local project-vc-external-roots-function #'elisp-load-path-roots)
   (add-hook 'completion-at-point-functions
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index ac9ba630c4..5843361dbd 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -596,8 +596,7 @@ octave-mode
   (add-hook 'before-save-hook 'octave-sync-function-file-names nil t)
   (setq-local beginning-of-defun-function 'octave-beginning-of-defun)
   (and octave-font-lock-texinfo-comment (octave-font-lock-texinfo-comment))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                'octave-eldoc-function)
+  (add-hook 'eldoc-documentation-functions 'octave-eldoc-function nil t)
 
   (easy-menu-add octave-mode-menu))
 
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 6f169123b9..8b4cfaa77a 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -5198,8 +5198,10 @@ python-mode
       ;; Emacs<25
       (set (make-local-variable 'eldoc-documentation-function)
            #'python-eldoc-function)
-    (add-function :before-until (local 'eldoc-documentation-function)
-                  #'python-eldoc-function))
+    (if (< emacs-major-version 26)
+        (add-function :before-until (local 'eldoc-documentation-function)
+                      #'python-eldoc-function)
+      (add-hook 'eldoc-documentation-functions #'python-eldoc-function nil t)))
 
   (add-to-list
    'hs-special-modes-alist
diff --git a/lisp/simple.el b/lisp/simple.el
index 58f8372192..f56a505523 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -1490,8 +1490,8 @@ read--expression
     (minibuffer-with-setup-hook
         (lambda ()
           ;; FIXME: call emacs-lisp-mode?
-          (add-function :before-until (local 'eldoc-documentation-function)
-                        #'elisp-eldoc-documentation-function)
+          (add-hook 'eldoc-documentation-functions
+                    #'elisp-eldoc-documentation-function nil t)
           (eldoc-mode 1)
           (add-hook 'completion-at-point-functions
                     #'elisp-completion-at-point nil t)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Mon, 28 Aug 2017 02:48:01 GMT) Full text and rfc822 format available.

Message #8 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: npostavs <at> users.sourceforge.net
To: Mark Oteiza <mvoteiza <at> udel.edu>
Cc: 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Sun, 27 Aug 2017 22:49:06 -0400
Mark Oteiza <mvoteiza <at> udel.edu> writes:

> This makes it possible to use the results from multiple eldoc functions
> at once, through the addition of an abnormal hook.

Last time this came up, there were some objections[1].  I'm not sure
what exactly what you mean by "use the results from multiple eldoc
functions".  Is that something not possible currently?

[1]: http://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00291.html

> I'm not aware of a good way to access the default value (or other local
> values) of eldoc-documentation-function when shadowed by a :before-until
> advice returning non-nil.

Doesn't (default-value 'eldoc-documentation-function) work?

Note also that (cl-prin1 eldoc-documentation-function) will print out a
representation of all the added functions.  It uses some "internal"
advice--* functions, perhaps they should be "externalized"?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Mon, 28 Aug 2017 03:12:02 GMT) Full text and rfc822 format available.

Message #11 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Mark Oteiza <mvoteiza <at> udel.edu>
To: npostavs <at> users.sourceforge.net
Cc: 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Sun, 27 Aug 2017 23:11:04 -0400
On 27/08/17 at 10:49pm, npostavs <at> users.sourceforge.net wrote:
>Mark Oteiza <mvoteiza <at> udel.edu> writes:
>
>> This makes it possible to use the results from multiple eldoc functions
>> at once, through the addition of an abnormal hook.
>
> I'm not sure
>what exactly what you mean by "use the results from multiple eldoc
>functions".  Is that something not possible currently?

If both the buffer mode's documentation function and the function in the
default-value return non-nil at a point, execution stops at the
:before-until function, analogous to run-hook-with-args-until-success.

I want to see both results.  See what eldoc-documentation-compose does.

>> I'm not aware of a good way to access the default value (or other local
>> values) of eldoc-documentation-function when shadowed by a :before-until
>> advice returning non-nil.
>
>Doesn't (default-value 'eldoc-documentation-function) work?

FSVO "work".

To do what is described above, not only does one have to write some
wrapper function to advise eldoc-documentation-function buffer-locally,
but also one has to hook into any/every mode that might add its own doc
function.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Tue, 29 Aug 2017 01:04:02 GMT) Full text and rfc822 format available.

Message #14 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: npostavs <at> users.sourceforge.net
To: Mark Oteiza <mvoteiza <at> udel.edu>
Cc: 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Mon, 28 Aug 2017 21:05:10 -0400
Mark Oteiza <mvoteiza <at> udel.edu> writes:

> If both the buffer mode's documentation function and the function in the
> default-value return non-nil at a point, execution stops at the
> :before-until function, analogous to run-hook-with-args-until-success.
>
> I want to see both results.  See what eldoc-documentation-compose does.
>
> To do what is described above, not only does one have to write some
> wrapper function to advise eldoc-documentation-function buffer-locally,
> but also one has to hook into any/every mode that might add its own doc
> function.

Ah, I think I understand now.  The patch makes it easier to customize
the composition of all eldoc-documentation-functions at the cost of
making it more difficult for the individual mode to decide how its own
doc function will compose.  Could be a worthwhile tradeoff (especially
since there currently aren't any individual mode eldoc functions using
anything apart from :before-until, afaik); I'm on the fence about this.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Wed, 30 Aug 2017 01:51:01 GMT) Full text and rfc822 format available.

Message #17 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Mark Oteiza <mvoteiza <at> udel.edu>
To: npostavs <at> users.sourceforge.net
Cc: 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Tue, 29 Aug 2017 21:50:17 -0400
On 28/08/17 at 09:05pm, npostavs <at> users.sourceforge.net wrote:
>Mark Oteiza <mvoteiza <at> udel.edu> writes:
>
>> If both the buffer mode's documentation function and the function in the
>> default-value return non-nil at a point, execution stops at the
>> :before-until function, analogous to run-hook-with-args-until-success.
>>
>> I want to see both results.  See what eldoc-documentation-compose does.
>>
>> To do what is described above, not only does one have to write some
>> wrapper function to advise eldoc-documentation-function buffer-locally,
>> but also one has to hook into any/every mode that might add its own doc
>> function.
>
>Ah, I think I understand now.  The patch makes it easier to customize
>the composition of all eldoc-documentation-functions at the cost of
>making it more difficult for the individual mode to decide how its own
>doc function will compose.

Correct.

> Could be a worthwhile tradeoff (especially
>since there currently aren't any individual mode eldoc functions using
>anything apart from :before-until, afaik); I'm on the fence about this.

Not in emacs, at least.  In elpa, ggtags uses :after-until.  The only
3rd party packages--that I can think of right now--that implement eldoc
functions just use setq-local (elpy and geiser).




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Mon, 24 Jun 2019 16:17:02 GMT) Full text and rfc822 format available.

Message #20 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Mark Oteiza <mvoteiza <at> udel.edu>
Cc: 28257 <at> debbugs.gnu.org, npostavs <at> users.sourceforge.net
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Mon, 24 Jun 2019 18:16:23 +0200
Mark Oteiza <mvoteiza <at> udel.edu> writes:

>> Could be a worthwhile tradeoff (especially
>>since there currently aren't any individual mode eldoc functions using
>>anything apart from :before-until, afaik); I'm on the fence about this.
>
> Not in emacs, at least.  In elpa, ggtags uses :after-until.  The only
> 3rd party packages--that I can think of right now--that implement eldoc
> functions just use setq-local (elpy and geiser).

Noam, still on the fence?

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Mon, 24 Jun 2019 22:52:02 GMT) Full text and rfc822 format available.

Message #23 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Noam Postavsky <npostavs <at> gmail.com>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: Mark Oteiza <mvoteiza <at> udel.edu>, npostavs <at> users.sourceforge.net,
 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Mon, 24 Jun 2019 18:51:33 -0400
Lars Ingebrigtsen <larsi <at> gnus.org> writes:

> Mark Oteiza <mvoteiza <at> udel.edu> writes:
>
>>> Could be a worthwhile tradeoff (especially
>>>since there currently aren't any individual mode eldoc functions using
>>>anything apart from :before-until, afaik); I'm on the fence about this.
>>
>> Not in emacs, at least.  In elpa, ggtags uses :after-until.  The only
>> 3rd party packages--that I can think of right now--that implement eldoc
>> functions just use setq-local (elpy and geiser).
>
> Noam, still on the fence?

I haven't really thought about this much, maybe bring it up on
emacs-devel?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Wed, 26 Jun 2019 14:04:02 GMT) Full text and rfc822 format available.

Message #26 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Mark Oteiza <mvoteiza <at> udel.edu>
To: Noam Postavsky <npostavs <at> gmail.com>
Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, npostavs <at> users.sourceforge.net,
 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Wed, 26 Jun 2019 10:03:03 -0400
On 24/06/19 at 06:51pm, Noam Postavsky wrote:
> Lars Ingebrigtsen <larsi <at> gnus.org> writes:
> 
> > Mark Oteiza <mvoteiza <at> udel.edu> writes:
> >
> >>> Could be a worthwhile tradeoff (especially
> >>>since there currently aren't any individual mode eldoc functions using
> >>>anything apart from :before-until, afaik); I'm on the fence about this.
> >>
> >> Not in emacs, at least.  In elpa, ggtags uses :after-until.  The only
> >> 3rd party packages--that I can think of right now--that implement eldoc
> >> functions just use setq-local (elpy and geiser).
> >
> > Noam, still on the fence?
> 
> I haven't really thought about this much, maybe bring it up on
> emacs-devel?

I happily rolled with this patch until some changes to eldoc broke it.
Will post a new patch when I get around to it.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Sun, 26 Jan 2020 23:48:01 GMT) Full text and rfc822 format available.

Message #29 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Mark Oteiza <mvoteiza <at> udel.edu>
To: Noam Postavsky <npostavs <at> gmail.com>
Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, npostavs <at> users.sourceforge.net,
 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Sun, 26 Jan 2020 18:47:31 -0500
On 26/06/19 at 10:03am, Mark Oteiza wrote:
> On 24/06/19 at 06:51pm, Noam Postavsky wrote:
> > Lars Ingebrigtsen <larsi <at> gnus.org> writes:
> > 
> > > Mark Oteiza <mvoteiza <at> udel.edu> writes:
> > >
> > >>> Could be a worthwhile tradeoff (especially
> > >>>since there currently aren't any individual mode eldoc functions using
> > >>>anything apart from :before-until, afaik); I'm on the fence about this.
> > >>
> > >> Not in emacs, at least.  In elpa, ggtags uses :after-until.  The only
> > >> 3rd party packages--that I can think of right now--that implement eldoc
> > >> functions just use setq-local (elpy and geiser).
> > >
> > > Noam, still on the fence?
> > 
> > I haven't really thought about this much, maybe bring it up on
> > emacs-devel?
> 
> I happily rolled with this patch until some changes to eldoc broke it.
> Will post a new patch when I get around to it.

New patch below.  Lightly tested, at first sight it seems to work
(setting eldoc-documentation-function to eldoc-documentation-compose)

diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 683374c615..8e2a22b2ff 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -1269,9 +1269,27 @@ Lisp Doc
 for a function, it shows the argument list, and for a variable it
 shows the first line of the variable's documentation string.  To
 toggle Eldoc mode, type @kbd{M-x eldoc-mode}.  There's also a Global
-Eldoc mode, which is turned on by default, and affects buffers, such
-as @samp{*scratch*}, whose major mode is Emacs Lisp or Lisp
-Interaction (@w{@kbd{M-x global-eldoc-mode}} to turn it off globally).
+Eldoc mode, which is turned on by default, and affects buffers whose
+major mode sets the variables described below.  Use @w{@kbd{M-x
+global-eldoc-mode}} to turn it off globally.
+
+@vindex eldoc-documentation-function
+@vindex eldoc-documentation-functions
+  These variables can be used to configure ElDoc mode:
+
+@table @code
+@item eldoc-documentation-function
+This variable holds the function which is used to retrieve
+documentation for the item at point from the functions in the hook
+@code{eldoc-documentation-functions}.  By default,
+@code{eldoc-documentation-function} returns the first documentation
+string produced by the @code{eldoc-documentation-functions} hook.
+
+@item eldoc-documentation-functions
+This abnormal hook holds documentation functions.  It acts as a
+collection of backends for ElDoc. This is what modes should use to
+register their documentation functions with ElDoc.
+@end table
 
 @node Hideshow
 @section Hideshow minor mode
diff --git a/etc/NEWS b/etc/NEWS
index 276b8f46e8..8396afc277 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -103,6 +103,16 @@ To revert to the previous behaviour,
 unconditionally aborts the current edebug instrumentation with the
 supplied error message.
 
++++
+** ElDoc
+
+*** New hook 'eldoc-documentation-functions' to be used for registering
+doc string functions.  This makes the results of all doc string
+functions accessible to the user through the existing single function hook
+'eldoc-documentation-function'.
+
+*** 'eldoc-documentation-function' is now a custom variable.
+
 ** Tramp
 
 +++
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index 7a7b8ec164..0a17e7e2ea 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -40,9 +40,9 @@
 ;;      (add-hook 'ielm-mode-hook 'eldoc-mode)
 ;;      (add-hook 'eval-expression-minibuffer-setup-hook 'eldoc-mode)
 
-;; Major modes for other languages may use ElDoc by defining an
-;; appropriate function as the buffer-local value of
-;; `eldoc-documentation-function'.
+;; Major modes for other languages may use ElDoc by adding an
+;; appropriate function to the buffer-local value of
+;; `eldoc-documentation-functions'.
 
 ;;; Code:
 
@@ -222,8 +222,8 @@ global-eldoc-mode
 (defun eldoc--eval-expression-setup ()
   ;; Setup `eldoc', similar to `emacs-lisp-mode'.  FIXME: Call
   ;; `emacs-lisp-mode' itself?
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'elisp-eldoc-documentation-function)
+  (add-hook 'eldoc-documentation-functions
+            #'elisp-eldoc-documentation-function nil t)
   (eldoc-mode +1))
 
 ;;;###autoload
@@ -235,7 +235,11 @@ turn-on-eldoc-mode
 
 (defun eldoc--supported-p ()
   "Non-nil if an ElDoc function is set for this buffer."
-  (not (memq eldoc-documentation-function '(nil ignore))))
+  (let ((hook 'eldoc-documentation-functions))
+    (and eldoc-documentation-function
+         (or (and (local-variable-p hook)
+                  (buffer-local-value hook (current-buffer)))
+             (default-value hook)))))
 
 
 (defun eldoc-schedule-timer ()
@@ -348,7 +352,47 @@ eldoc-display-message-no-interference-p
 
 
 ;;;###autoload
-(defvar eldoc-documentation-function #'ignore
+(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:
+  (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-documentation-default ()
+  "Show 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))))))))
+
+(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))))))))
+
+;;;###autoload
+(defcustom eldoc-documentation-function #'eldoc-documentation-default
   "Function to call to return doc string.
 The function of no args should return a one-line string for displaying
 doc about a function etc. appropriate to the context around point.
@@ -359,14 +403,12 @@ eldoc-documentation-function
 The result is used as is, so the function must explicitly handle
 the variables `eldoc-argument-case' and `eldoc-echo-area-use-multiline-p',
 and the face `eldoc-highlight-function-argument', if they are to have any
-effect.
-
-Major modes should modify this variable using `add-function', for example:
-  (add-function :before-until (local \\='eldoc-documentation-function)
-                #\\='foo-mode-eldoc-function)
-so that the global documentation function (i.e. the default value of the
-variable) is taken into account if the major mode specific function does not
-return any documentation.")
+effect."
+  :link '(info-link "(emacs) Lisp Doc")
+  :type '(radio (function-item eldoc-documentation-default)
+                (function-item eldoc-documentation-compose)
+                (function :tag "Other function"))
+  :group 'eldoc)
 
 (defun eldoc-print-current-symbol-info ()
   "Print the text produced by `eldoc-documentation-function'."
@@ -381,7 +423,8 @@ eldoc-print-current-symbol-info
         ;; 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 ((fun eldoc-documentation-function))
+            (when fun (eldoc-message (funcall fun)))))))))
 
 ;; 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 58518e7416..cf7118f208 100644
--- a/lisp/hexl.el
+++ b/lisp/hexl.el
@@ -367,8 +367,8 @@ hexl-mode
     (add-hook 'change-major-mode-hook #'hexl-maybe-dehexlify-buffer nil t)
 
     ;; Set a callback function for eldoc.
-    (add-function :before-until (local 'eldoc-documentation-function)
-                  #'hexl-print-current-point-info)
+    (add-hook 'eldoc-documentation-functions
+              #'hexl-print-current-point-info nil t)
     (eldoc-add-command-completions "hexl-")
     (eldoc-remove-command "hexl-save-buffer"
 			  "hexl-current-address")
@@ -455,6 +455,8 @@ hexl-revert-buffer-function
     ;; 2. reset change-major-mode-hook in case that `hexl-mode'
     ;; previously added hexl-maybe-dehexlify-buffer to it.
     (remove-hook 'change-major-mode-hook #'hexl-maybe-dehexlify-buffer t)
+    (remove-hook 'eldoc-documentation-functions
+                 #'hexl-print-current-point-info t)
     (setq major-mode 'fundamental-mode)
     (hexl-mode)))
 
diff --git a/lisp/ielm.el b/lisp/ielm.el
index 41675c011d..fc06ebfa2d 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -541,8 +541,8 @@ inferior-emacs-lisp-mode
   (set (make-local-variable 'completion-at-point-functions)
        '(comint-replace-by-expanded-history
          ielm-complete-filename elisp-completion-at-point))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'elisp-eldoc-documentation-function)
+  (add-hook 'eldoc-documentation-functions
+            #'elisp-eldoc-documentation-function nil t)
   (set (make-local-variable 'ielm-prompt-internal) ielm-prompt)
   (set (make-local-variable 'comint-prompt-read-only) ielm-prompt-read-only)
   (setq comint-get-old-input 'ielm-get-old-input)
diff --git a/lisp/progmodes/cfengine.el b/lisp/progmodes/cfengine.el
index 9ddb2ab2bb..af02f5fea8 100644
--- a/lisp/progmodes/cfengine.el
+++ b/lisp/progmodes/cfengine.el
@@ -1390,12 +1390,15 @@ cfengine3-mode
                  (when buffer-file-name
                    (shell-quote-argument buffer-file-name)))))
 
-  ;; For emacs < 25.1 where `eldoc-documentation-function' defaults to
-  ;; nil.
-  (or eldoc-documentation-function
-      (setq-local eldoc-documentation-function #'ignore))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'cfengine3-documentation-function)
+  (if (> emacs-major-version 25)
+      (add-hook 'eldoc-documentation-functions
+                #'cfengine3-documentation-function nil t)
+    ;; For emacs < 25.1 where `eldoc-documentation-function' defaults
+    ;; to nil.
+    (or eldoc-documentation-function
+        (setq-local eldoc-documentation-function #'ignore))
+    (add-function :before-until (local 'eldoc-documentation-function)
+                  #'cfengine3-documentation-function))
 
   (add-hook 'completion-at-point-functions
             #'cfengine3-completion-function nil t)
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 2617a6e4cc..813b628bc3 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -250,8 +250,8 @@ emacs-lisp-mode
     (add-hook 'electric-pair-mode-hook #'emacs-lisp-set-electric-text-pairs))
   (setq-local electric-quote-string t)
   (setq imenu-case-fold-search nil)
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'elisp-eldoc-documentation-function)
+  (add-hook 'eldoc-documentation-functions
+            #'elisp-eldoc-documentation-function nil t)
   (add-hook 'xref-backend-functions #'elisp--xref-backend nil t)
   (setq-local project-vc-external-roots-function #'elisp-load-path-roots)
   (add-hook 'completion-at-point-functions
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index 9e03956254..352c1810d1 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -619,8 +619,7 @@ octave-mode
   (add-hook 'before-save-hook 'octave-sync-function-file-names nil t)
   (setq-local beginning-of-defun-function 'octave-beginning-of-defun)
   (and octave-font-lock-texinfo-comment (octave-font-lock-texinfo-comment))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                'octave-eldoc-function)
+  (add-hook 'eldoc-documentation-functions 'octave-eldoc-function nil t)
 
   (easy-menu-add octave-mode-menu))
 
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index a2d85d0bef..48401b1e54 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -5544,8 +5544,10 @@ python-mode
       ;; Emacs<25
       (set (make-local-variable 'eldoc-documentation-function)
            #'python-eldoc-function)
-    (add-function :before-until (local 'eldoc-documentation-function)
-                  #'python-eldoc-function))
+    (if (< emacs-major-version 26)
+        (add-function :before-until (local 'eldoc-documentation-function)
+                      #'python-eldoc-function)
+      (add-hook 'eldoc-documentation-functions #'python-eldoc-function nil t)))
 
   (add-to-list
    'hs-special-modes-alist




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Fri, 31 Jan 2020 14:06:01 GMT) Full text and rfc822 format available.

Message #32 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Mark Oteiza <mvoteiza <at> udel.edu>
Cc: larsi <at> gnus.org, npostavs <at> users.sourceforge.net, npostavs <at> gmail.com,
 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Fri, 31 Jan 2020 16:05:09 +0200
> Date: Sun, 26 Jan 2020 18:47:31 -0500
> From: Mark Oteiza <mvoteiza <at> udel.edu>
> Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, 28257 <at> debbugs.gnu.org,
>  npostavs <at> users.sourceforge.net
> 
> On 26/06/19 at 10:03am, Mark Oteiza wrote:
> > On 24/06/19 at 06:51pm, Noam Postavsky wrote:
> > > Lars Ingebrigtsen <larsi <at> gnus.org> writes:
> > > 
> > > > Mark Oteiza <mvoteiza <at> udel.edu> writes:
> > > >
> > > >>> Could be a worthwhile tradeoff (especially
> > > >>>since there currently aren't any individual mode eldoc functions using
> > > >>>anything apart from :before-until, afaik); I'm on the fence about this.
> > > >>
> > > >> Not in emacs, at least.  In elpa, ggtags uses :after-until.  The only
> > > >> 3rd party packages--that I can think of right now--that implement eldoc
> > > >> functions just use setq-local (elpy and geiser).
> > > >
> > > > Noam, still on the fence?
> > > 
> > > I haven't really thought about this much, maybe bring it up on
> > > emacs-devel?
> > 
> > I happily rolled with this patch until some changes to eldoc broke it.
> > Will post a new patch when I get around to it.
> 
> New patch below.  Lightly tested, at first sight it seems to work
> (setting eldoc-documentation-function to eldoc-documentation-compose)

Lars, Noam, any comments?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Sat, 08 Feb 2020 10:05:02 GMT) Full text and rfc822 format available.

Message #35 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: larsi <at> gnus.org, npostavs <at> users.sourceforge.net
Cc: mvoteiza <at> udel.edu, npostavs <at> gmail.com, 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Sat, 08 Feb 2020 12:03:44 +0200
Ping!

Can we please review Mark's patch some time soon?  TIA.

> Date: Fri, 31 Jan 2020 16:05:09 +0200
> From: Eli Zaretskii <eliz <at> gnu.org>
> Cc: larsi <at> gnus.org, 28257 <at> debbugs.gnu.org, npostavs <at> gmail.com,
>  npostavs <at> users.sourceforge.net
> 
> > Date: Sun, 26 Jan 2020 18:47:31 -0500
> > From: Mark Oteiza <mvoteiza <at> udel.edu>
> > Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, 28257 <at> debbugs.gnu.org,
> >  npostavs <at> users.sourceforge.net
> > 
> > On 26/06/19 at 10:03am, Mark Oteiza wrote:
> > > On 24/06/19 at 06:51pm, Noam Postavsky wrote:
> > > > Lars Ingebrigtsen <larsi <at> gnus.org> writes:
> > > > 
> > > > > Mark Oteiza <mvoteiza <at> udel.edu> writes:
> > > > >
> > > > >>> Could be a worthwhile tradeoff (especially
> > > > >>>since there currently aren't any individual mode eldoc functions using
> > > > >>>anything apart from :before-until, afaik); I'm on the fence about this.
> > > > >>
> > > > >> Not in emacs, at least.  In elpa, ggtags uses :after-until.  The only
> > > > >> 3rd party packages--that I can think of right now--that implement eldoc
> > > > >> functions just use setq-local (elpy and geiser).
> > > > >
> > > > > Noam, still on the fence?
> > > > 
> > > > I haven't really thought about this much, maybe bring it up on
> > > > emacs-devel?
> > > 
> > > I happily rolled with this patch until some changes to eldoc broke it.
> > > Will post a new patch when I get around to it.
> > 
> > New patch below.  Lightly tested, at first sight it seems to work
> > (setting eldoc-documentation-function to eldoc-documentation-compose)
> 
> Lars, Noam, any comments?
> 
> 
> 
> 




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Tue, 11 Feb 2020 21:58:02 GMT) Full text and rfc822 format available.

Message #38 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Noam Postavsky <npostavs <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mvoteiza <at> udel.edu, larsi <at> gnus.org, 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Tue, 11 Feb 2020 16:57:04 -0500
Eli Zaretskii <eliz <at> gnu.org> writes:

> Ping!
>
> Can we please review Mark's patch some time soon?  TIA.

+  (if (> emacs-major-version 25)
+      (add-hook 'eldoc-documentation-functions
+                #'cfengine3-documentation-function nil t)

I think the 25 should be 26?  Or 27 if this is going to master (in which
case the python.el hunk needs to be adjusted too).  Otherwise, the patch
itself is fine in terms of coding details, as far as I can tell.

I feel somewhat underqualified to decide whether or not the change in
the semantics of eldoc-documentation-function is a good idea.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Fri, 14 Feb 2020 09:59:01 GMT) Full text and rfc822 format available.

Message #41 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Noam Postavsky <npostavs <at> gmail.com>,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: mvoteiza <at> udel.edu, larsi <at> gnus.org, 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Fri, 14 Feb 2020 11:58:27 +0200
> From: Noam Postavsky <npostavs <at> gmail.com>
> Cc: larsi <at> gnus.org, mvoteiza <at> udel.edu, 28257 <at> debbugs.gnu.org
> Date: Tue, 11 Feb 2020 16:57:04 -0500
> 
> > Can we please review Mark's patch some time soon?  TIA.
> 
> +  (if (> emacs-major-version 25)
> +      (add-hook 'eldoc-documentation-functions
> +                #'cfengine3-documentation-function nil t)
> 
> I think the 25 should be 26?  Or 27 if this is going to master (in which
> case the python.el hunk needs to be adjusted too).  Otherwise, the patch
> itself is fine in terms of coding details, as far as I can tell.

Thanks.

> I feel somewhat underqualified to decide whether or not the change in
> the semantics of eldoc-documentation-function is a good idea.

Thanks.  I'm even less qualified here, but the same thoughts crossed
my mind as well.

Stefan, any comments or ideas?  Who else might be relevant to discuss
these changes?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Fri, 14 Feb 2020 14:52:01 GMT) Full text and rfc822 format available.

Message #44 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Mark Oteiza <mvoteiza <at> udel.edu>
Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, 28257 <at> debbugs.gnu.org,
 Noam Postavsky <npostavs <at> gmail.com>, npostavs <at> users.sourceforge.net
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Fri, 14 Feb 2020 09:50:47 -0500
> +*** 'eldoc-documentation-function' is now a custom variable.

I think this should be expanded to clarify that packages should stop
using this var and use `eldoc-documentation-functions` instead.

>  (defun eldoc--supported-p ()
>    "Non-nil if an ElDoc function is set for this buffer."
> -  (not (memq eldoc-documentation-function '(nil ignore))))
> +  (let ((hook 'eldoc-documentation-functions))
> +    (and eldoc-documentation-function
> +         (or (and (local-variable-p hook)
> +                  (buffer-local-value hook (current-buffer)))
> +             (default-value hook)))))

The

    (and eldoc-documentation-function

should really be

    (and (not (memq eldoc-documentation-function '(nil ignore))))

>  ;;;###autoload
> -(defvar eldoc-documentation-function #'ignore
> +(defvar eldoc-documentation-functions nil

There's no reason to autoload this new var.
[ Of course, it makes no practical difference since eldoc is preloaded
  anyway, but if it weren't preloaded this var should be autoloaded.  ]

> +(defun eldoc-documentation-default ()
> +  "Show doc string for item at point.
> +Default value for `eldoc-documentation-function'."

This doc should clarify that it shows the *first* doc.

> +;;;###autoload
> +(defcustom eldoc-documentation-function #'eldoc-documentation-default

Now that it's a defcustom this shouldn't be autoloaded either (currently
packages are expected to use `add-function` on it even if there's no
indication that the user will actually use eldoc, which is why it is/was
autoloaded.  But with its new meaning this need disappears).

> +  (if (> emacs-major-version 25)
> +      (add-hook 'eldoc-documentation-functions
> +                #'cfengine3-documentation-function nil t)

The test should be fixed (I guess you could use (boundp
'eldoc-documentation-functions)).

> +    (if (< emacs-major-version 26)
> +        (add-function :before-until (local 'eldoc-documentation-function)
> +                      #'python-eldoc-function)
> +      (add-hook 'eldoc-documentation-functions #'python-eldoc-function nil t)))

Same here.


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Fri, 14 Feb 2020 14:52:01 GMT) Full text and rfc822 format available.

Message #47 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: mvoteiza <at> udel.edu, larsi <at> gnus.org, Noam Postavsky <npostavs <at> gmail.com>,
 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Fri, 14 Feb 2020 09:51:26 -0500
> Thanks.  I'm even less qualified here, but the same thoughts crossed
> my mind as well.
>
> Stefan, any comments or ideas?  Who else might be relevant to discuss
> these changes?

It's fine by me.


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Fri, 14 Feb 2020 22:55:02 GMT) Full text and rfc822 format available.

Message #50 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Mark Oteiza <mvoteiza <at> udel.edu>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, 28257 <at> debbugs.gnu.org,
 Noam Postavsky <npostavs <at> gmail.com>, npostavs <at> users.sourceforge.net
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Fri, 14 Feb 2020 17:54:15 -0500
Thanks Noam and Stefan for the catches.  New patch below.

On 14/02/20 at 09:50am, Stefan Monnier wrote:
> > +*** 'eldoc-documentation-function' is now a custom variable.
> 
> I think this should be expanded to clarify that packages should stop
> using this var and use `eldoc-documentation-functions` instead.
> 
> <...snip...>

 doc/emacs/programs.texi      | 24 ++++++++++++--
 etc/NEWS                     | 12 +++++++
 lisp/emacs-lisp/eldoc.el     | 75 ++++++++++++++++++++++++++++++++++----------
 lisp/hexl.el                 |  6 ++--
 lisp/ielm.el                 |  4 +--
 lisp/progmodes/cfengine.el   | 15 +++++----
 lisp/progmodes/elisp-mode.el |  4 +--
 lisp/progmodes/octave.el     |  3 +-
 lisp/progmodes/python.el     |  6 ++--
 9 files changed, 113 insertions(+), 36 deletions(-)

diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 683374c615..8e2a22b2ff 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -1269,9 +1269,27 @@ Lisp Doc
 for a function, it shows the argument list, and for a variable it
 shows the first line of the variable's documentation string.  To
 toggle Eldoc mode, type @kbd{M-x eldoc-mode}.  There's also a Global
-Eldoc mode, which is turned on by default, and affects buffers, such
-as @samp{*scratch*}, whose major mode is Emacs Lisp or Lisp
-Interaction (@w{@kbd{M-x global-eldoc-mode}} to turn it off globally).
+Eldoc mode, which is turned on by default, and affects buffers whose
+major mode sets the variables described below.  Use @w{@kbd{M-x
+global-eldoc-mode}} to turn it off globally.
+
+@vindex eldoc-documentation-function
+@vindex eldoc-documentation-functions
+  These variables can be used to configure ElDoc mode:
+
+@table @code
+@item eldoc-documentation-function
+This variable holds the function which is used to retrieve
+documentation for the item at point from the functions in the hook
+@code{eldoc-documentation-functions}.  By default,
+@code{eldoc-documentation-function} returns the first documentation
+string produced by the @code{eldoc-documentation-functions} hook.
+
+@item eldoc-documentation-functions
+This abnormal hook holds documentation functions.  It acts as a
+collection of backends for ElDoc. This is what modes should use to
+register their documentation functions with ElDoc.
+@end table
 
 @node Hideshow
 @section Hideshow minor mode
diff --git a/etc/NEWS b/etc/NEWS
index b0e17c7c03..793be515c5 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -120,6 +120,18 @@ To revert to the previous behaviour,
 unconditionally aborts the current edebug instrumentation with the
 supplied error message.
 
++++
+** ElDoc
+
+*** New hook 'eldoc-documentation-functions' to be used for registering
+doc string functions.  This makes the results of all doc string
+functions accessible to the user through the existing single function hook
+'eldoc-documentation-function'.
+
+*** 'eldoc-documentation-function' is now a custom variable.
+Modes should use the new hook instead of this variable to register
+their backends.
+
 ** Tramp
 
 +++
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index 7a7b8ec164..d96e569fca 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -40,9 +40,9 @@
 ;;      (add-hook 'ielm-mode-hook 'eldoc-mode)
 ;;      (add-hook 'eval-expression-minibuffer-setup-hook 'eldoc-mode)
 
-;; Major modes for other languages may use ElDoc by defining an
-;; appropriate function as the buffer-local value of
-;; `eldoc-documentation-function'.
+;; Major modes for other languages may use ElDoc by adding an
+;; appropriate function to the buffer-local value of
+;; `eldoc-documentation-functions'.
 
 ;;; Code:
 
@@ -222,8 +222,8 @@ global-eldoc-mode
 (defun eldoc--eval-expression-setup ()
   ;; Setup `eldoc', similar to `emacs-lisp-mode'.  FIXME: Call
   ;; `emacs-lisp-mode' itself?
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'elisp-eldoc-documentation-function)
+  (add-hook 'eldoc-documentation-functions
+            #'elisp-eldoc-documentation-function nil t)
   (eldoc-mode +1))
 
 ;;;###autoload
@@ -235,7 +235,11 @@ turn-on-eldoc-mode
 
 (defun eldoc--supported-p ()
   "Non-nil if an ElDoc function is set for this buffer."
-  (not (memq eldoc-documentation-function '(nil ignore))))
+  (let ((hook 'eldoc-documentation-functions))
+    (and (not (memq eldoc-documentation-function '(nil ignore)))
+         (or (and (local-variable-p hook)
+                  (buffer-local-value hook (current-buffer)))
+             (default-value hook)))))
 
 
 (defun eldoc-schedule-timer ()
@@ -347,8 +351,46 @@ eldoc-display-message-no-interference-p
   (not (or executing-kbd-macro (bound-and-true-p edebug-active))))
 
 
-;;;###autoload
-(defvar eldoc-documentation-function #'ignore
+(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:
+  (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-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))))))))
+
+(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))))))))
+
+(defcustom eldoc-documentation-function #'eldoc-documentation-default
   "Function to call to return doc string.
 The function of no args should return a one-line string for displaying
 doc about a function etc. appropriate to the context around point.
@@ -359,14 +401,12 @@ eldoc-documentation-function
 The result is used as is, so the function must explicitly handle
 the variables `eldoc-argument-case' and `eldoc-echo-area-use-multiline-p',
 and the face `eldoc-highlight-function-argument', if they are to have any
-effect.
-
-Major modes should modify this variable using `add-function', for example:
-  (add-function :before-until (local \\='eldoc-documentation-function)
-                #\\='foo-mode-eldoc-function)
-so that the global documentation function (i.e. the default value of the
-variable) is taken into account if the major mode specific function does not
-return any documentation.")
+effect."
+  :link '(info-link "(emacs) Lisp Doc")
+  :type '(radio (function-item eldoc-documentation-default)
+                (function-item eldoc-documentation-compose)
+                (function :tag "Other function"))
+  :group 'eldoc)
 
 (defun eldoc-print-current-symbol-info ()
   "Print the text produced by `eldoc-documentation-function'."
@@ -381,7 +421,8 @@ eldoc-print-current-symbol-info
         ;; 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 ((fun eldoc-documentation-function))
+            (when fun (eldoc-message (funcall fun)))))))))
 
 ;; 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 58518e7416..cf7118f208 100644
--- a/lisp/hexl.el
+++ b/lisp/hexl.el
@@ -367,8 +367,8 @@ hexl-mode
     (add-hook 'change-major-mode-hook #'hexl-maybe-dehexlify-buffer nil t)
 
     ;; Set a callback function for eldoc.
-    (add-function :before-until (local 'eldoc-documentation-function)
-                  #'hexl-print-current-point-info)
+    (add-hook 'eldoc-documentation-functions
+              #'hexl-print-current-point-info nil t)
     (eldoc-add-command-completions "hexl-")
     (eldoc-remove-command "hexl-save-buffer"
 			  "hexl-current-address")
@@ -455,6 +455,8 @@ hexl-revert-buffer-function
     ;; 2. reset change-major-mode-hook in case that `hexl-mode'
     ;; previously added hexl-maybe-dehexlify-buffer to it.
     (remove-hook 'change-major-mode-hook #'hexl-maybe-dehexlify-buffer t)
+    (remove-hook 'eldoc-documentation-functions
+                 #'hexl-print-current-point-info t)
     (setq major-mode 'fundamental-mode)
     (hexl-mode)))
 
diff --git a/lisp/ielm.el b/lisp/ielm.el
index 41675c011d..fc06ebfa2d 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -541,8 +541,8 @@ inferior-emacs-lisp-mode
   (set (make-local-variable 'completion-at-point-functions)
        '(comint-replace-by-expanded-history
          ielm-complete-filename elisp-completion-at-point))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'elisp-eldoc-documentation-function)
+  (add-hook 'eldoc-documentation-functions
+            #'elisp-eldoc-documentation-function nil t)
   (set (make-local-variable 'ielm-prompt-internal) ielm-prompt)
   (set (make-local-variable 'comint-prompt-read-only) ielm-prompt-read-only)
   (setq comint-get-old-input 'ielm-get-old-input)
diff --git a/lisp/progmodes/cfengine.el b/lisp/progmodes/cfengine.el
index 9ddb2ab2bb..a94a6666bb 100644
--- a/lisp/progmodes/cfengine.el
+++ b/lisp/progmodes/cfengine.el
@@ -1390,12 +1390,15 @@ cfengine3-mode
                  (when buffer-file-name
                    (shell-quote-argument buffer-file-name)))))
 
-  ;; For emacs < 25.1 where `eldoc-documentation-function' defaults to
-  ;; nil.
-  (or eldoc-documentation-function
-      (setq-local eldoc-documentation-function #'ignore))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'cfengine3-documentation-function)
+  (if (boundp 'eldoc-documentation-functions)
+      (add-hook 'eldoc-documentation-functions
+                #'cfengine3-documentation-function nil t)
+    ;; For emacs < 25.1 where `eldoc-documentation-function' defaults
+    ;; to nil.
+    (or eldoc-documentation-function
+        (setq-local eldoc-documentation-function #'ignore))
+    (add-function :before-until (local 'eldoc-documentation-function)
+                  #'cfengine3-documentation-function))
 
   (add-hook 'completion-at-point-functions
             #'cfengine3-completion-function nil t)
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 2617a6e4cc..813b628bc3 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -250,8 +250,8 @@ emacs-lisp-mode
     (add-hook 'electric-pair-mode-hook #'emacs-lisp-set-electric-text-pairs))
   (setq-local electric-quote-string t)
   (setq imenu-case-fold-search nil)
-  (add-function :before-until (local 'eldoc-documentation-function)
-                #'elisp-eldoc-documentation-function)
+  (add-hook 'eldoc-documentation-functions
+            #'elisp-eldoc-documentation-function nil t)
   (add-hook 'xref-backend-functions #'elisp--xref-backend nil t)
   (setq-local project-vc-external-roots-function #'elisp-load-path-roots)
   (add-hook 'completion-at-point-functions
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index 9e03956254..352c1810d1 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -619,8 +619,7 @@ octave-mode
   (add-hook 'before-save-hook 'octave-sync-function-file-names nil t)
   (setq-local beginning-of-defun-function 'octave-beginning-of-defun)
   (and octave-font-lock-texinfo-comment (octave-font-lock-texinfo-comment))
-  (add-function :before-until (local 'eldoc-documentation-function)
-                'octave-eldoc-function)
+  (add-hook 'eldoc-documentation-functions 'octave-eldoc-function nil t)
 
   (easy-menu-add octave-mode-menu))
 
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index a2d85d0bef..67383b3415 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -5544,8 +5544,10 @@ python-mode
       ;; Emacs<25
       (set (make-local-variable 'eldoc-documentation-function)
            #'python-eldoc-function)
-    (add-function :before-until (local 'eldoc-documentation-function)
-                  #'python-eldoc-function))
+    (if (boundp 'eldoc-documentation-functions)
+        (add-hook 'eldoc-documentation-functions #'python-eldoc-function nil t)
+      (add-function :before-until (local 'eldoc-documentation-function)
+                    #'python-eldoc-function)))
 
   (add-to-list
    'hs-special-modes-alist




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Fri, 21 Feb 2020 08:48:01 GMT) Full text and rfc822 format available.

Message #53 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Mark Oteiza <mvoteiza <at> udel.edu>
Cc: larsi <at> gnus.org, 28257 <at> debbugs.gnu.org, npostavs <at> gmail.com,
 monnier <at> iro.umontreal.ca, npostavs <at> users.sourceforge.net
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Fri, 21 Feb 2020 10:47:25 +0200
> Date: Fri, 14 Feb 2020 17:54:15 -0500
> From: Mark Oteiza <mvoteiza <at> udel.edu>
> Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, npostavs <at> users.sourceforge.net,
>  Noam Postavsky <npostavs <at> gmail.com>, 28257 <at> debbugs.gnu.org
> 
> Thanks Noam and Stefan for the catches.  New patch below.

Any further comments?

I only spotted an occasional single space between sentences in doc
strings, but have nothing else?  If there are no further comments, I'd
like to install this.

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Tue, 25 Feb 2020 23:19:01 GMT) Full text and rfc822 format available.

Message #56 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Mark Oteiza <mvoteiza <at> udel.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: larsi <at> gnus.org, 28257 <at> debbugs.gnu.org, npostavs <at> gmail.com,
 monnier <at> iro.umontreal.ca, npostavs <at> users.sourceforge.net
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Tue, 25 Feb 2020 18:18:33 -0500
On 21/02/20 at 10:47am, Eli Zaretskii wrote:
> > Date: Fri, 14 Feb 2020 17:54:15 -0500
> > From: Mark Oteiza <mvoteiza <at> udel.edu>
> > Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, npostavs <at> users.sourceforge.net,
> >  Noam Postavsky <npostavs <at> gmail.com>, 28257 <at> debbugs.gnu.org
> > 
> > Thanks Noam and Stefan for the catches.  New patch below.
> 
> Any further comments?
> 
> I only spotted an occasional single space between sentences in doc
> strings, but have nothing else?  If there are no further comments, I'd
> like to install this.
> 
> Thanks.

Pushed as c0fcbd2c119, hopefully adding the missing spaces, and adding
:version and a null option to the type of 'eldoc-documentation-function'




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28257; Package emacs. (Mon, 10 Aug 2020 14:43:01 GMT) Full text and rfc822 format available.

Message #59 received at 28257 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Mark Oteiza <mvoteiza <at> udel.edu>
Cc: Eli Zaretskii <eliz <at> gnu.org>, npostavs <at> users.sourceforge.net,
 npostavs <at> gmail.com, monnier <at> iro.umontreal.ca, 28257 <at> debbugs.gnu.org
Subject: Re: bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
Date: Mon, 10 Aug 2020 16:42:15 +0200
Mark Oteiza <mvoteiza <at> udel.edu> writes:

> Pushed as c0fcbd2c119, hopefully adding the missing spaces, and adding
> :version and a null option to the type of 'eldoc-documentation-function'

If I'm skimming this thread correctly, this commit fixes everything
discussed in this bug report, so I'm closing it.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




bug closed, send any further explanations to 28257 <at> debbugs.gnu.org and Mark Oteiza <mvoteiza <at> udel.edu> Request was from Lars Ingebrigtsen <larsi <at> gnus.org> to control <at> debbugs.gnu.org. (Mon, 10 Aug 2020 14:43:02 GMT) Full text and rfc822 format available.

bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Tue, 08 Sep 2020 11:24:09 GMT) Full text and rfc822 format available.

This bug report was last modified 3 years and 203 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.