Compare commits

...

5 commits

Author SHA1 Message Date
João Távora
2563620303 Remove spurious display-buffer call in eldoc--echo-area-substring
* lisp/emacs-lisp/eldoc.el (eldoc--echo-area-substring): Remove
spurious display-buffer call.
2020-10-05 00:25:56 +01:00
João Távora
16db4323a2 Rework semantics of eldoc-echo-are-use-multiline-p
* lisp/emacs-lisp/eldoc.el (eldoc-echo-area-use-multiline-p): Rework
semantics.
(eldoc--echo-area-substring): New helper.
(eldoc--echo-area-prefer-doc-buffer-p): New helper.
(eldoc-display-in-echo-area): Rework using new helpers.
2020-10-04 19:31:02 +01:00
João Távora
25e1825ce5 Rename ElDoc user option controlling display of truncation notice
The new name makes it consistent with other variables controlling the
display of ElDoc documentation in the echo area.

Per bug#43543.

* etc/NEWS (Eldoc): Rename eldoc-display-truncation-message to
eldoc-echo-area-display-truncation-message.

* lisp/emacs-lisp/eldoc.el
(eldoc-echo-area-display-truncation-message): Rename from
eldoc-display-truncation-message.
(eldoc-display-in-echo-area): Use new variable name.
2020-10-04 12:19:47 +01:00
João Távora
b83468fb08 Rework eldoc-echo-area-prefer-doc-buffer (bug#42532)
* lisp/emacs-lisp/eldoc.el:
(eldoc-echo-area-prefer-doc-buffer): Rename from
eldoc-echo-area-prefer-doc-buffer
(eldoc-display-in-echo-area): Rework to honour
eldoc-echo-area-prefer-doc-buffer.
2020-10-03 19:47:36 +01:00
João Távora
22c7c686d4 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.
2020-10-03 19:44:22 +01:00
2 changed files with 208 additions and 152 deletions

View file

@ -591,7 +591,7 @@ definition.
** ElDoc
+++
*** New user option 'eldoc-display-truncation-message'.
*** New user option 'eldoc-echo-area-display-truncation-message'.
If non-nil (the default), eldoc will display a message saying
something like "(Documentation truncated. Use `M-x eldoc-doc-buffer'
to see rest)" when a message has been truncated. If nil, truncated
@ -605,6 +605,13 @@ may arrange for it to be produced asynchronously. The results of all
doc string functions are accessible to the user through the user
option 'eldoc-documentation-strategy'.
*** New hook 'eldoc-display-functions'.
This hook is intended to be used for displaying doc string. The
functions receive the docstrings composed according to
`eldoc-documentation-strategy' and are tasked with displaying it to
the user. Examples of such functions would use the echo area, a
separate buffer or a tooltip.
+++
*** New user option 'eldoc-documentation-strategy'.
The built-in choices available for this user option let users compose

View file

@ -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
@ -67,7 +67,7 @@ If this variable is set to 0, no idle time is required."
Changing the value requires toggling `eldoc-mode'."
:type 'boolean)
(defcustom eldoc-display-truncation-message t
(defcustom eldoc-echo-area-display-truncation-message t
"If non-nil, provide verbose help when a message has been truncated.
If nil, truncated messages will just have \"...\" appended."
:type 'boolean
@ -96,19 +96,22 @@ Note that this variable has no effect, unless
If value is t, never attempt to truncate messages, even if the
echo area must be resized to fit.
If value is a number (integer or floating point), it has the
semantics of `max-mini-window-height', constraining the resizing
for ElDoc purposes only.
If the value is a positive number, it is used to calculate a
number of logical lines of documentation that ElDoc is allowed to
put in the echo area. If a positive integer, the number is used
directly, while a float specified the number of lines as a
proporting of the echo area frame's height.
Any resizing respects `max-mini-window-height'.
If value is any non-nil symbol other than t, the part of the doc
string that represents the symbol's name may be truncated if it
will enable the rest of the doc string to fit on a single line,
without resizing the echo area.
If value is the symbol `truncate-sym-name-if-fit' t, the part of
the doc string that represents a symbol's name may be truncated
if it will enable the rest of the doc string to fit on a single
line, without resizing the echo area.
If value is nil, a doc string is always truncated to fit in a
single line of display in the echo area."
single line of display in the echo area.
Any resizing of the echo area aditionally respects
`max-mini-window-height'."
:type '(radio (const :tag "Always" t)
(float :tag "Fraction of frame height" 0.25)
(integer :tag "Number of lines" 5)
@ -117,12 +120,13 @@ single line of display in the echo area."
symbol names if it will\ enable argument list to fit on one
line" truncate-sym-name-if-fit)))
(defcustom eldoc-prefer-doc-buffer nil
(defcustom eldoc-echo-area-prefer-doc-buffer nil
"Prefer ElDoc's documentation buffer if it is showing in some frame.
If this variable's value is t and a piece of documentation needs
to be truncated to fit in the echo area, do so if ElDoc's
documentation buffer is not already showing, since the buffer
always holds the full documentation."
If this variable's value is t ElDoc will skip showing
documentation in the echo area if this buffer (given by
`eldoc-doc-buffer') is current in some window. If the value is
the symbol `maybe', then the echo area is only skipped if the
documentation doesn't fit there."
:type 'boolean)
(defface eldoc-highlight-function-argument
@ -350,40 +354,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 ()
@ -420,8 +410,9 @@ documentation, followed by an optional list of keyword-value
pairs of the form (:KEY VALUE :KEY2 VALUE2...). 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;
being reported on. The functions in `eldoc-display-functions'
can elect to remove this information depending on space
constraints;
* `:face', VALUE is a symbol designating a face to use when
displaying `:thing''s value.
@ -432,27 +423,118 @@ 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
documention. 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--echo-area-substring (available)
"Given AVAILABLE lines, get buffer substring to display in echo area.
Helper for `eldoc-display-in-echo-area'."
(let ((start (prog1 (progn
(goto-char (point-min))
(skip-chars-forward " \t\n")
(point))
(goto-char (line-end-position available))
(skip-chars-backward " \t\n")))
(truncated (save-excursion
(skip-chars-forward " \t\n")
(not (eobp)))))
(cond ((and truncated
(> available 1)
eldoc-echo-area-display-truncation-message)
(goto-char (line-end-position 0))
(concat (buffer-substring start (point))
(format
"\n(Documentation truncated. Use `%s' to see rest)"
(substitute-command-keys "\\[eldoc-doc-buffer]"))))
(t
(buffer-substring start (point))))))
(defun eldoc--echo-area-prefer-doc-buffer-p (truncatedp)
"Tell if display in the echo area should be skipped.
Helper for `eldoc-display-in-echo-area'."
(and (or (eq eldoc-echo-area-prefer-doc-buffer t)
(and truncatedp
(eq eldoc-echo-area-prefer-doc-buffer
'maybe)))
(get-buffer-window eldoc--doc-buffer)))
(defun eldoc-display-in-echo-area (docs _interactive)
"Display DOCS in echo area.
Honor `eldoc-echo-area-use-multiline-p' and
`eldoc-echo-area-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)
@ -461,44 +543,13 @@ Honor most of `eldoc-echo-area-use-multiline-p'."
(available (cl-typecase val
(float (truncate (* (frame-height) val)))
(integer val)
(t 1)))
(things-reported-on)
(request eldoc--last-request-state)
(t 'just-one-line)))
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 for a lot of special conditions.
(and
(eq 'truncate-sym-name-if-fit eldoc-echo-area-use-multiline-p)
(null (cdr docs))
@ -509,45 +560,32 @@ Honor most of `eldoc-echo-area-use-multiline-p'."
(not (string-match "\n" single-doc))
(> (+ (length single-doc) (length single-doc-sym) 2) width))
single-doc)
((> available 1)
;; The message takes one extra line, so if we don't
;; display that, we have one extra line to use.
(unless eldoc-display-truncation-message
(setq available (1+ available)))
(with-current-buffer (eldoc-doc-buffer)
(cl-loop
initially
(goto-char (point-min))
(goto-char (line-end-position (1+ available)))
for truncated = nil then t
for needed
= (let ((truncate-lines message-truncate-lines))
(count-screen-lines (point-min) (point) t
(minibuffer-window)))
while (> needed (if truncated (1- available) available))
do (goto-char (line-end-position (if truncated 0 -1)))
(while (and (not (bobp)) (bolp)) (goto-char (line-end-position 0)))
finally
(unless (and truncated
eldoc-prefer-doc-buffer
(get-buffer-window eldoc--doc-buffer))
(cl-return
(concat
(buffer-substring (point-min) (point))
(and
truncated
(if eldoc-display-truncation-message
(format
"\n(Documentation truncated. Use `%s' to see rest)"
(substitute-command-keys "\\[eldoc-doc-buffer]"))
"..."))))))))
((= available 1)
;; Truncate "brutally." ; FIXME: use `eldoc-prefer-doc-buffer' too?
(with-current-buffer (eldoc-doc-buffer)
(truncate-string-to-width
(buffer-substring (goto-char (point-min)) (line-end-position 1)) width))))))
((and (numberp available)
(cl-plusp available))
;; Else, given a positive number of logical lines, we
;; format the *eldoc* buffer, using as most of its
;; contents as we know will fit.
(with-current-buffer (eldoc--format-doc-buffer docs)
(eldoc--echo-area-substring available)))
(t ;; this is the "truncate brutally" situation
(let ((string
(with-current-buffer (eldoc--format-doc-buffer docs)
(buffer-substring (goto-char (point-min))
(line-end-position 1)))))
(if (> (length string) width) ; truncation to happen
(unless (eldoc--echo-area-prefer-doc-buffer-p t)
(truncate-string-to-width string width))
(unless (eldoc--echo-area-prefer-doc-buffer-p nil)
string)))))))
(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 +747,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 +787,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 +835,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 +883,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"