unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
@ 2017-08-28  2:10 Mark Oteiza
  2017-08-28  2:49 ` npostavs
  0 siblings, 1 reply; 19+ messages in thread
From: Mark Oteiza @ 2017-08-28  2:10 UTC (permalink / raw)
  To: 28257


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)))))
 
 \f
 (defun eldoc-schedule-timer ()
@@ -341,7 +345,47 @@ eldoc-display-message-no-interference-p
 
 \f
 ;;;###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)





^ permalink raw reply related	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2017-08-28  2:10 bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook Mark Oteiza
@ 2017-08-28  2:49 ` npostavs
  2017-08-28  3:11   ` Mark Oteiza
  0 siblings, 1 reply; 19+ messages in thread
From: npostavs @ 2017-08-28  2:49 UTC (permalink / raw)
  To: Mark Oteiza; +Cc: 28257

Mark Oteiza <mvoteiza@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"?





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2017-08-28  2:49 ` npostavs
@ 2017-08-28  3:11   ` Mark Oteiza
  2017-08-29  1:05     ` npostavs
  0 siblings, 1 reply; 19+ messages in thread
From: Mark Oteiza @ 2017-08-28  3:11 UTC (permalink / raw)
  To: npostavs; +Cc: 28257

On 27/08/17 at 10:49pm, npostavs@users.sourceforge.net wrote:
>Mark Oteiza <mvoteiza@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.





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2017-08-28  3:11   ` Mark Oteiza
@ 2017-08-29  1:05     ` npostavs
  2017-08-30  1:50       ` Mark Oteiza
  0 siblings, 1 reply; 19+ messages in thread
From: npostavs @ 2017-08-29  1:05 UTC (permalink / raw)
  To: Mark Oteiza; +Cc: 28257

Mark Oteiza <mvoteiza@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.





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2017-08-29  1:05     ` npostavs
@ 2017-08-30  1:50       ` Mark Oteiza
  2019-06-24 16:16         ` Lars Ingebrigtsen
  0 siblings, 1 reply; 19+ messages in thread
From: Mark Oteiza @ 2017-08-30  1:50 UTC (permalink / raw)
  To: npostavs; +Cc: 28257

On 28/08/17 at 09:05pm, npostavs@users.sourceforge.net wrote:
>Mark Oteiza <mvoteiza@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).





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2017-08-30  1:50       ` Mark Oteiza
@ 2019-06-24 16:16         ` Lars Ingebrigtsen
  2019-06-24 22:51           ` Noam Postavsky
  0 siblings, 1 reply; 19+ messages in thread
From: Lars Ingebrigtsen @ 2019-06-24 16:16 UTC (permalink / raw)
  To: Mark Oteiza; +Cc: npostavs, 28257

Mark Oteiza <mvoteiza@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





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2019-06-24 16:16         ` Lars Ingebrigtsen
@ 2019-06-24 22:51           ` Noam Postavsky
  2019-06-26 14:03             ` Mark Oteiza
  0 siblings, 1 reply; 19+ messages in thread
From: Noam Postavsky @ 2019-06-24 22:51 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Mark Oteiza, 28257, npostavs

Lars Ingebrigtsen <larsi@gnus.org> writes:

> Mark Oteiza <mvoteiza@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?





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2019-06-24 22:51           ` Noam Postavsky
@ 2019-06-26 14:03             ` Mark Oteiza
  2020-01-26 23:47               ` Mark Oteiza
  0 siblings, 1 reply; 19+ messages in thread
From: Mark Oteiza @ 2019-06-26 14:03 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: Lars Ingebrigtsen, 28257, npostavs

On 24/06/19 at 06:51pm, Noam Postavsky wrote:
> Lars Ingebrigtsen <larsi@gnus.org> writes:
> 
> > Mark Oteiza <mvoteiza@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.





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2019-06-26 14:03             ` Mark Oteiza
@ 2020-01-26 23:47               ` Mark Oteiza
  2020-01-31 14:05                 ` Eli Zaretskii
  2020-02-14 14:50                 ` Stefan Monnier
  0 siblings, 2 replies; 19+ messages in thread
From: Mark Oteiza @ 2020-01-26 23:47 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: Lars Ingebrigtsen, 28257, npostavs

On 26/06/19 at 10:03am, Mark Oteiza wrote:
> On 24/06/19 at 06:51pm, Noam Postavsky wrote:
> > Lars Ingebrigtsen <larsi@gnus.org> writes:
> > 
> > > Mark Oteiza <mvoteiza@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)))))
 
 \f
 (defun eldoc-schedule-timer ()
@@ -348,7 +352,47 @@ eldoc-display-message-no-interference-p
 
 \f
 ;;;###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





^ permalink raw reply related	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-01-26 23:47               ` Mark Oteiza
@ 2020-01-31 14:05                 ` Eli Zaretskii
  2020-02-08 10:03                   ` Eli Zaretskii
  2020-02-14 14:50                 ` Stefan Monnier
  1 sibling, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2020-01-31 14:05 UTC (permalink / raw)
  To: Mark Oteiza; +Cc: larsi, 28257, npostavs, npostavs

> Date: Sun, 26 Jan 2020 18:47:31 -0500
> From: Mark Oteiza <mvoteiza@udel.edu>
> Cc: Lars Ingebrigtsen <larsi@gnus.org>, 28257@debbugs.gnu.org,
>  npostavs@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@gnus.org> writes:
> > > 
> > > > Mark Oteiza <mvoteiza@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?





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-01-31 14:05                 ` Eli Zaretskii
@ 2020-02-08 10:03                   ` Eli Zaretskii
  2020-02-11 21:57                     ` Noam Postavsky
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2020-02-08 10:03 UTC (permalink / raw)
  To: larsi, npostavs; +Cc: mvoteiza, npostavs, 28257

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@gnu.org>
> Cc: larsi@gnus.org, 28257@debbugs.gnu.org, npostavs@gmail.com,
>  npostavs@users.sourceforge.net
> 
> > Date: Sun, 26 Jan 2020 18:47:31 -0500
> > From: Mark Oteiza <mvoteiza@udel.edu>
> > Cc: Lars Ingebrigtsen <larsi@gnus.org>, 28257@debbugs.gnu.org,
> >  npostavs@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@gnus.org> writes:
> > > > 
> > > > > Mark Oteiza <mvoteiza@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?
> 
> 
> 
> 





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-02-08 10:03                   ` Eli Zaretskii
@ 2020-02-11 21:57                     ` Noam Postavsky
  2020-02-14  9:58                       ` Eli Zaretskii
  0 siblings, 1 reply; 19+ messages in thread
From: Noam Postavsky @ 2020-02-11 21:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: mvoteiza, larsi, 28257

Eli Zaretskii <eliz@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.





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-02-11 21:57                     ` Noam Postavsky
@ 2020-02-14  9:58                       ` Eli Zaretskii
  2020-02-14 14:51                         ` Stefan Monnier
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2020-02-14  9:58 UTC (permalink / raw)
  To: Noam Postavsky, Stefan Monnier; +Cc: mvoteiza, larsi, 28257

> From: Noam Postavsky <npostavs@gmail.com>
> Cc: larsi@gnus.org, mvoteiza@udel.edu, 28257@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?





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-01-26 23:47               ` Mark Oteiza
  2020-01-31 14:05                 ` Eli Zaretskii
@ 2020-02-14 14:50                 ` Stefan Monnier
  2020-02-14 22:54                   ` Mark Oteiza
  1 sibling, 1 reply; 19+ messages in thread
From: Stefan Monnier @ 2020-02-14 14:50 UTC (permalink / raw)
  To: Mark Oteiza; +Cc: Lars Ingebrigtsen, npostavs, Noam Postavsky, 28257

> +*** '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






^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-02-14  9:58                       ` Eli Zaretskii
@ 2020-02-14 14:51                         ` Stefan Monnier
  0 siblings, 0 replies; 19+ messages in thread
From: Stefan Monnier @ 2020-02-14 14:51 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: mvoteiza, larsi, Noam Postavsky, 28257

> 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






^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-02-14 14:50                 ` Stefan Monnier
@ 2020-02-14 22:54                   ` Mark Oteiza
  2020-02-21  8:47                     ` Eli Zaretskii
  0 siblings, 1 reply; 19+ messages in thread
From: Mark Oteiza @ 2020-02-14 22:54 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Lars Ingebrigtsen, npostavs, Noam Postavsky, 28257

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)))))
 
 \f
 (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))))
 
 \f
-;;;###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





^ permalink raw reply related	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-02-14 22:54                   ` Mark Oteiza
@ 2020-02-21  8:47                     ` Eli Zaretskii
  2020-02-25 23:18                       ` Mark Oteiza
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2020-02-21  8:47 UTC (permalink / raw)
  To: Mark Oteiza; +Cc: larsi, npostavs, npostavs, monnier, 28257

> Date: Fri, 14 Feb 2020 17:54:15 -0500
> From: Mark Oteiza <mvoteiza@udel.edu>
> Cc: Lars Ingebrigtsen <larsi@gnus.org>, npostavs@users.sourceforge.net,
>  Noam Postavsky <npostavs@gmail.com>, 28257@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.





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-02-21  8:47                     ` Eli Zaretskii
@ 2020-02-25 23:18                       ` Mark Oteiza
  2020-08-10 14:42                         ` Lars Ingebrigtsen
  0 siblings, 1 reply; 19+ messages in thread
From: Mark Oteiza @ 2020-02-25 23:18 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: larsi, npostavs, npostavs, monnier, 28257

On 21/02/20 at 10:47am, Eli Zaretskii wrote:
> > Date: Fri, 14 Feb 2020 17:54:15 -0500
> > From: Mark Oteiza <mvoteiza@udel.edu>
> > Cc: Lars Ingebrigtsen <larsi@gnus.org>, npostavs@users.sourceforge.net,
> >  Noam Postavsky <npostavs@gmail.com>, 28257@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'





^ permalink raw reply	[flat|nested] 19+ messages in thread

* bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook
  2020-02-25 23:18                       ` Mark Oteiza
@ 2020-08-10 14:42                         ` Lars Ingebrigtsen
  0 siblings, 0 replies; 19+ messages in thread
From: Lars Ingebrigtsen @ 2020-08-10 14:42 UTC (permalink / raw)
  To: Mark Oteiza; +Cc: 28257, npostavs, monnier, npostavs

Mark Oteiza <mvoteiza@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





^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2020-08-10 14:42 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-28  2:10 bug#28257: 26.0.50; [PATCH] expose eldoc functions in a hook Mark Oteiza
2017-08-28  2:49 ` npostavs
2017-08-28  3:11   ` Mark Oteiza
2017-08-29  1:05     ` npostavs
2017-08-30  1:50       ` Mark Oteiza
2019-06-24 16:16         ` Lars Ingebrigtsen
2019-06-24 22:51           ` Noam Postavsky
2019-06-26 14:03             ` Mark Oteiza
2020-01-26 23:47               ` Mark Oteiza
2020-01-31 14:05                 ` Eli Zaretskii
2020-02-08 10:03                   ` Eli Zaretskii
2020-02-11 21:57                     ` Noam Postavsky
2020-02-14  9:58                       ` Eli Zaretskii
2020-02-14 14:51                         ` Stefan Monnier
2020-02-14 14:50                 ` Stefan Monnier
2020-02-14 22:54                   ` Mark Oteiza
2020-02-21  8:47                     ` Eli Zaretskii
2020-02-25 23:18                       ` Mark Oteiza
2020-08-10 14:42                         ` Lars Ingebrigtsen

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).