Introduce eldoc-display-functions
See bug#43609. * lisp/emacs-lisp/eldoc.el (eldoc--request-state): Add comment. (eldoc--last-request-state): No longer buffer-local. (eldoc--request-docs-p): Delete. (eldoc-display-functions): New user variable. (eldoc--doc-buffer-docs): New variable. (eldoc-display-message-p): Rework. (eldoc--format-doc-buffer): Rework from eldoc--handle-docs. (eldoc-display-in-echo-area, eldoc-display-in-buffer): New user-visible function. (eldoc--invoke-strategy): Take INTERACTIVE arg. Invoke eldoc-display-in-buffer (eldoc-print-current-symbol-info): Simplify. (Version): Bump to 1.11.0 * etc/NEWS: Mention eldoc-display-functions.
This commit is contained in:
parent
8c2382d309
commit
4c543a724f
2 changed files with 158 additions and 105 deletions
|
@ -5,7 +5,7 @@
|
|||
;; Author: Noah Friedman <friedman@splode.com>
|
||||
;; Keywords: extensions
|
||||
;; Created: 1995-10-06
|
||||
;; Version: 1.10.0
|
||||
;; Version: 1.11.0
|
||||
;; Package-Requires: ((emacs "26.3"))
|
||||
|
||||
;; This is a GNU ELPA :core package. Avoid functionality that is not
|
||||
|
@ -350,40 +350,26 @@ Also store it in `eldoc-last-message' and return that value."
|
|||
;; for us, but do note that the last-message will be gone.
|
||||
(setq eldoc-last-message nil))))
|
||||
|
||||
(defvar-local eldoc--last-request-state nil
|
||||
;; The point of `eldoc--request-state' is not to over-request, which
|
||||
;; can happen if the idle timer is restarted on execution of command
|
||||
;; which is guaranteed not to change the conditions that warrant a new
|
||||
;; request for documentation.
|
||||
(defvar eldoc--last-request-state nil
|
||||
"Tuple containing information about last ElDoc request.")
|
||||
(defun eldoc--request-state ()
|
||||
"Compute information to store in `eldoc--last-request-state'."
|
||||
(list (current-buffer) (buffer-modified-tick) (point)))
|
||||
|
||||
(defun eldoc-display-message-p ()
|
||||
(eldoc--request-docs-p (eldoc--request-state)))
|
||||
"Tell if ElDoc can use the echo area."
|
||||
(and (eldoc-display-message-no-interference-p)
|
||||
(not this-command)
|
||||
(eldoc--message-command-p last-command)))
|
||||
|
||||
(make-obsolete 'eldoc-display-message-p
|
||||
"Use `eldoc-documentation-functions' instead."
|
||||
"eldoc-1.6.0")
|
||||
|
||||
(defun eldoc--request-docs-p (request-state)
|
||||
"Return non-nil when it is appropriate to request docs.
|
||||
REQUEST-STATE is a candidate for `eldoc--last-request-state'"
|
||||
(and
|
||||
;; FIXME: The original idea behind this function is to protect the
|
||||
;; Echo area from ElDoc interference, but since that is only one of
|
||||
;; the possible outlets of ElDoc, this must soon be reworked.
|
||||
(eldoc-display-message-no-interference-p)
|
||||
(not (and eldoc--doc-buffer
|
||||
(get-buffer-window eldoc--doc-buffer)
|
||||
(equal request-state
|
||||
(with-current-buffer
|
||||
eldoc--doc-buffer
|
||||
eldoc--last-request-state))))
|
||||
;; If this-command is non-nil while running via an idle
|
||||
;; timer, we're still in the middle of executing a command,
|
||||
;; e.g. a query-replace where it would be annoying to
|
||||
;; overwrite the echo area.
|
||||
(not this-command)
|
||||
(eldoc--message-command-p last-command)))
|
||||
|
||||
|
||||
;; Check various conditions about the current environment that might make
|
||||
;; it undesirable to print eldoc messages right this instant.
|
||||
(defun eldoc-display-message-no-interference-p ()
|
||||
|
@ -416,43 +402,112 @@ about the context around point.
|
|||
|
||||
To call the CALLBACK function, the hook function must pass it an
|
||||
obligatory argument DOCSTRING, a string containing the
|
||||
documentation, followed by an optional list of keyword-value
|
||||
pairs of the form (:KEY VALUE :KEY2 VALUE2...). KEY can be:
|
||||
documentation, followed by an optional list of arbitrary
|
||||
keyword-value pairs of the form (:KEY VALUE :KEY2 VALUE2...).
|
||||
The information contained in these pairs is understood by members
|
||||
of `eldoc-display-functions', allowing the
|
||||
documentation-producing backend to cooperate with specific
|
||||
documentation-displaying frontends. For example, KEY can be:
|
||||
|
||||
* `:thing', VALUE is a short string or symbol designating what is
|
||||
being reported on. The documentation display engine can elect
|
||||
to remove this information depending on space constraints;
|
||||
* `:thing', VALUE being a short string or symbol designating what
|
||||
is being reported on. It can, for example be the name of the
|
||||
function whose signature is being documented, or the name of
|
||||
the variable whose docstring is being documented.
|
||||
`eldoc-display-in-echo-area', a member of
|
||||
`eldoc-display-functions', sometimes omits this information
|
||||
depending on space constraints;
|
||||
|
||||
* `:face', VALUE is a symbol designating a face to use when
|
||||
displaying `:thing''s value.
|
||||
* `:face', VALUE being a symbol designating a face which both
|
||||
`eldoc-display-in-echo-area' and `eldoc-display-in-buffer' will
|
||||
use when displaying `:thing''s value.
|
||||
|
||||
Major modes should modify this hook locally, for example:
|
||||
Finally, 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.")
|
||||
|
||||
(defvar eldoc-display-functions
|
||||
'(eldoc-display-in-echo-area eldoc-display-in-buffer)
|
||||
"Hook of functions tasked with displaying ElDoc results.
|
||||
Each function is passed two arguments: DOCS and INTERACTIVE. DOCS
|
||||
is a list (DOC ...) where DOC looks like (STRING :KEY VALUE :KEY2
|
||||
VALUE2 ...). STRING is a string containing the documentation's
|
||||
text and the remainder of DOC is an optional list of
|
||||
keyword-value pairs denoting additional properties of that
|
||||
documentation. For commonly recognized properties, see
|
||||
`eldoc-documentation-functions'.
|
||||
|
||||
INTERACTIVE says if the request to display doc strings came
|
||||
directly from the user or from ElDoc's automatic mechanisms'.")
|
||||
|
||||
(defvar eldoc--doc-buffer nil "Buffer displaying latest ElDoc-produced docs.")
|
||||
|
||||
(defvar eldoc--doc-buffer-docs nil "Documentation items in `eldoc--doc-buffer'.")
|
||||
|
||||
(defun eldoc-doc-buffer (&optional interactive)
|
||||
"Get latest *eldoc* help buffer. Interactively, display it."
|
||||
(interactive (list t))
|
||||
(prog1
|
||||
(if (and eldoc--doc-buffer (buffer-live-p eldoc--doc-buffer))
|
||||
eldoc--doc-buffer
|
||||
(setq eldoc--doc-buffer (get-buffer-create "*eldoc*")))
|
||||
(when interactive (display-buffer eldoc--doc-buffer))))
|
||||
"Display ElDoc documentation buffer.
|
||||
This holds the results of the last documentation request."
|
||||
(unless (buffer-live-p eldoc--doc-buffer)
|
||||
(setq eldoc--doc-buffer (get-buffer-create "*eldoc*")))
|
||||
(when interactive
|
||||
(display-buffer eldoc--doc-buffer)))
|
||||
|
||||
(defun eldoc--format-doc-buffer (docs)
|
||||
"Ensure DOCS are displayed in an *eldoc* buffer."
|
||||
(interactive (list t))
|
||||
(eldoc-doc-buffer) ;; ensure buffer exists
|
||||
(with-current-buffer eldoc--doc-buffer
|
||||
(unless (eq docs eldoc--doc-buffer-docs)
|
||||
(setq-local eldoc--doc-buffer-docs docs)
|
||||
(let ((inhibit-read-only t)
|
||||
(things-reported-on))
|
||||
(erase-buffer) (setq buffer-read-only t)
|
||||
(local-set-key "q" 'quit-window)
|
||||
(cl-loop for (docs . rest) on docs
|
||||
for (this-doc . plist) = docs
|
||||
for thing = (plist-get plist :thing)
|
||||
when thing do
|
||||
(cl-pushnew thing things-reported-on)
|
||||
(setq this-doc
|
||||
(concat
|
||||
(propertize (format "%s" thing)
|
||||
'face (plist-get plist :face))
|
||||
": "
|
||||
this-doc))
|
||||
do (insert this-doc)
|
||||
when rest do (insert "\n"))
|
||||
;; Maybe rename the buffer.
|
||||
(rename-buffer (if things-reported-on
|
||||
(format "*eldoc for %s*"
|
||||
(mapconcat (lambda (s) (format "%s" s))
|
||||
things-reported-on
|
||||
", "))
|
||||
"*eldoc*")))))
|
||||
eldoc--doc-buffer)
|
||||
|
||||
(defun eldoc--handle-docs (docs)
|
||||
"Display multiple DOCS in echo area.
|
||||
DOCS is a list of (STRING PLIST...). It is already sorted.
|
||||
Honor most of `eldoc-echo-area-use-multiline-p'."
|
||||
;; If there's nothing to report clear the echo area, but don't erase
|
||||
;; the last *eldoc* buffer.
|
||||
(if (null docs) (eldoc--message nil)
|
||||
(defun eldoc-display-in-echo-area (docs _interactive)
|
||||
"Display DOCS in echo area.
|
||||
Honor `eldoc-echo-area-use-multiline-p' and
|
||||
`eldoc-prefer-doc-buffer'."
|
||||
(cond
|
||||
(;; Check if he wave permission to mess with echo area at all. For
|
||||
;; example, if this-command is non-nil while running via an idle
|
||||
;; timer, we're still in the middle of executing a command, e.g. a
|
||||
;; query-replace where it would be annoying to overwrite the echo
|
||||
;; area.
|
||||
(or
|
||||
(not (eldoc-display-message-no-interference-p))
|
||||
this-command
|
||||
(not (eldoc--message-command-p last-command))))
|
||||
(;; If we do but nothing to report, clear the echo area.
|
||||
(null docs)
|
||||
(eldoc--message nil))
|
||||
(t
|
||||
;; Otherwise, establish some parameters.
|
||||
(let*
|
||||
;; Otherwise, establish some parameters.
|
||||
((width (1- (window-width (minibuffer-window))))
|
||||
(val (if (and (symbolp eldoc-echo-area-use-multiline-p)
|
||||
eldoc-echo-area-use-multiline-p)
|
||||
|
@ -462,43 +517,12 @@ Honor most of `eldoc-echo-area-use-multiline-p'."
|
|||
(float (truncate (* (frame-height) val)))
|
||||
(integer val)
|
||||
(t 1)))
|
||||
(things-reported-on)
|
||||
(request eldoc--last-request-state)
|
||||
single-doc single-doc-sym)
|
||||
;; Then, compose the contents of the `*eldoc*' buffer.
|
||||
(with-current-buffer (eldoc-doc-buffer)
|
||||
;; Set doc-buffer's `eldoc--last-request-state', too
|
||||
(setq eldoc--last-request-state request)
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer) (setq buffer-read-only t)
|
||||
(local-set-key "q" 'quit-window)
|
||||
(cl-loop for (docs . rest) on docs
|
||||
for (this-doc . plist) = docs
|
||||
for thing = (plist-get plist :thing)
|
||||
when thing do
|
||||
(cl-pushnew thing things-reported-on)
|
||||
(setq this-doc
|
||||
(concat
|
||||
(propertize (format "%s" thing)
|
||||
'face (plist-get plist :face))
|
||||
": "
|
||||
this-doc))
|
||||
do (insert this-doc)
|
||||
when rest do (insert "\n")))
|
||||
;; Rename the buffer.
|
||||
(when things-reported-on
|
||||
(rename-buffer (format "*eldoc for %s*"
|
||||
(mapconcat (lambda (s) (format "%s" s))
|
||||
things-reported-on
|
||||
", ")))))
|
||||
;; Finally, output to the echo area. I'm pretty sure nicer
|
||||
;; strategies can be used here, probably by splitting this
|
||||
;; function into some `eldoc-display-functions' special hook.
|
||||
(let ((echo-area-message
|
||||
(cond
|
||||
(;; We handle the `truncate-sym-name-if-fit' special
|
||||
;; case first, by checking if for a lot of special
|
||||
;; conditions.
|
||||
(;; To output to the echo area,We handle the
|
||||
;; `truncate-sym-name-if-fit' special case first, by
|
||||
;; checking if for a lot of special conditions.
|
||||
(and
|
||||
(eq 'truncate-sym-name-if-fit eldoc-echo-area-use-multiline-p)
|
||||
(null (cdr docs))
|
||||
|
@ -514,7 +538,11 @@ Honor most of `eldoc-echo-area-use-multiline-p'."
|
|||
;; display that, we have one extra line to use.
|
||||
(unless eldoc-display-truncation-message
|
||||
(setq available (1+ available)))
|
||||
(with-current-buffer (eldoc-doc-buffer)
|
||||
;; Else we format the *eldoc* buffer, then use some of
|
||||
;; its contents top section. I'm pretty sure smarter
|
||||
;; strategies can be used here that don't necessarily
|
||||
;; involve composing that entire buffer.
|
||||
(with-current-buffer (eldoc--format-doc-buffer docs)
|
||||
(cl-loop
|
||||
initially
|
||||
(goto-char (point-min))
|
||||
|
@ -543,11 +571,18 @@ Honor most of `eldoc-echo-area-use-multiline-p'."
|
|||
"..."))))))))
|
||||
((= available 1)
|
||||
;; Truncate "brutally." ; FIXME: use `eldoc-prefer-doc-buffer' too?
|
||||
(with-current-buffer (eldoc-doc-buffer)
|
||||
(with-current-buffer (eldoc--format-doc-buffer docs)
|
||||
(truncate-string-to-width
|
||||
(buffer-substring (goto-char (point-min)) (line-end-position 1)) width))))))
|
||||
(when echo-area-message
|
||||
(eldoc--message echo-area-message))))))
|
||||
(eldoc--message echo-area-message)))))))
|
||||
|
||||
(defun eldoc-display-in-buffer (docs interactive)
|
||||
"Display DOCS in a dedicated buffer.
|
||||
If INTERACTIVE is t, also display the buffer."
|
||||
(let ((buf (eldoc--format-doc-buffer docs)))
|
||||
(when interactive
|
||||
(display-buffer buf))))
|
||||
|
||||
(defun eldoc-documentation-default ()
|
||||
"Show first doc string for item at point.
|
||||
|
@ -709,19 +744,29 @@ have the following values:
|
|||
strings so far, as soon as possible."
|
||||
(funcall eldoc--make-callback method))
|
||||
|
||||
(defun eldoc--invoke-strategy ()
|
||||
(defun eldoc--invoke-strategy (interactive)
|
||||
"Invoke `eldoc-documentation-strategy' function.
|
||||
|
||||
If INTERACTIVE is non-nil, the request came directly from a user
|
||||
command, otherwise it came from ElDoc's idle
|
||||
timer, `eldoc-timer'.
|
||||
|
||||
That function's job is to run the `eldoc-documentation-functions'
|
||||
special hook, using the `run-hook' family of functions. ElDoc's
|
||||
built-in strategy functions play along with the
|
||||
`eldoc--make-callback' protocol, using it to produce callback to
|
||||
feed to the functgions of `eldoc-documentation-functions'.
|
||||
`eldoc--make-callback' protocol, using it to produce a callback
|
||||
argument to feed the functions that the user places in
|
||||
`eldoc-documentation-functions'. Whenever the strategy
|
||||
determines it has information to display to the user, this
|
||||
function passes responsibility to the functions in
|
||||
`eldoc-display-functions'.
|
||||
|
||||
Other third-party strategy functions do not use
|
||||
`eldoc--make-callback'. They must find some alternate way to
|
||||
produce callbacks to feed to `eldoc-documentation-function' and
|
||||
should endeavour to display the docstrings eventually produced."
|
||||
Other third-party values of `eldoc-documentation-strategy' should
|
||||
not use `eldoc--make-callback'. They must find some alternate
|
||||
way to produce callbacks to feed to
|
||||
`eldoc-documentation-function' and should endeavour to display
|
||||
the docstrings eventually produced, using
|
||||
`eldoc-display-functions'."
|
||||
(let* (;; How many callbacks have been created by the strategy
|
||||
;; function and passed to elements of
|
||||
;; `eldoc-documentation-functions'.
|
||||
|
@ -739,11 +784,12 @@ should endeavour to display the docstrings eventually produced."
|
|||
(push (cons pos (cons string plist)) docs-registered)))
|
||||
(display-doc
|
||||
()
|
||||
(eldoc--handle-docs
|
||||
(mapcar #'cdr
|
||||
(setq docs-registered
|
||||
(sort docs-registered
|
||||
(lambda (a b) (< (car a) (car b))))))))
|
||||
(run-hook-with-args
|
||||
'eldoc-display-functions (mapcar #'cdr
|
||||
(setq docs-registered
|
||||
(sort docs-registered
|
||||
(lambda (a b) (< (car a) (car b))))))
|
||||
interactive))
|
||||
(make-callback
|
||||
(method)
|
||||
(let ((pos (prog1 howmany (cl-incf howmany))))
|
||||
|
@ -786,22 +832,23 @@ should endeavour to display the docstrings eventually produced."
|
|||
(defun eldoc-print-current-symbol-info (&optional interactive)
|
||||
"Document thing at point."
|
||||
(interactive '(t))
|
||||
(let ((token (eldoc--request-state)))
|
||||
(let (token)
|
||||
(cond (interactive
|
||||
(eldoc--invoke-strategy))
|
||||
((not (eldoc--request-docs-p token))
|
||||
;; Erase the last message if we won't display a new one.
|
||||
(when eldoc-last-message
|
||||
(eldoc--message nil)))
|
||||
(t
|
||||
(eldoc--invoke-strategy t))
|
||||
((not (equal (setq token (eldoc--request-state))
|
||||
eldoc--last-request-state))
|
||||
(let ((non-essential t))
|
||||
(setq eldoc--last-request-state token)
|
||||
;; 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--invoke-strategy)))))))
|
||||
(eldoc--invoke-strategy nil)))))))
|
||||
|
||||
|
||||
;; This section only affects ElDoc output to the echo area, as in
|
||||
;; `eldoc-display-in-echo-area'.
|
||||
;;
|
||||
;; When point is in a sexp, the function args are not reprinted in the echo
|
||||
;; area after every possible interactive command because some of them print
|
||||
;; their own messages in the echo area; the eldoc functions would instantly
|
||||
|
@ -833,7 +880,6 @@ should endeavour to display the docstrings eventually produced."
|
|||
(apply #'eldoc-remove-command
|
||||
(all-completions name eldoc-message-commands))))
|
||||
|
||||
|
||||
;; Prime the command list.
|
||||
(eldoc-add-command-completions
|
||||
"back-to-indentation"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue