From: Basil L. Contovounesios Date: Thu, 26 Sep 2019 23:04:33 +0000 (+0100) Subject: Further improve button.el support for help-echo X-Git-Tag: emacs-27.0.90~1304 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=0fc8177414801e428ca184e8a9ba8b79a291c15a;p=emacs.git Further improve button.el support for help-echo The last change to forward-button added support for help-echo values that are functions. This patch fixes the arguments passed to such functions and further adds support for help-echo values that are forms (bug#37515). * doc/lispref/display.texi (Button Properties): Fix description of help-echo button property. * lisp/button.el (button--help-echo): New function. (forward-button): Use it. (backward-button): Clarify help-echo reference in docstring. * test/lisp/button-tests.el (button--help-echo-string) (button--help-echo-form, button--help-echo-function): New tests. --- diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 494bf0d3f7e..61bd4ce8830 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -6607,14 +6607,23 @@ in the variable @code{button-map}, which defines @key{RET} and The button type. @xref{Button Types}. @item help-echo -@kindex help-index @r{(button property)} -A string displayed by the Emacs tool-tip help system; by default, -@code{"mouse-2, RET: Push this button"}. +@kindex help-echo @r{(button property)} +A string displayed by the Emacs tooltip help system; by default, +@code{"mouse-2, RET: Push this button"}. Alternatively, a function +that returns, or a form that evaluates to, a string to be displayed or +@code{nil}. For details see @ref{Text help-echo}. + +The function is called with three arguments, @var{window}, +@var{object}, and @var{pos}. The second argument, @var{object}, is +either the overlay that had the property (for overlay buttons), or the +buffer containing the button (for text property buttons). The other +arguments have the same meaning as for the special text property +@code{help-echo}. @item follow-link @kindex follow-link @r{(button property)} -The follow-link property, defining how a @key{mouse-1} click behaves -on this button, @xref{Clickable Text}. +The @code{follow-link} property, defining how a @key{mouse-1} click +behaves on this button, @xref{Clickable Text}. @item button @kindex button @r{(button property)} diff --git a/lisp/button.el b/lisp/button.el index 32efc2f95be..04e77ca904f 100644 --- a/lisp/button.el +++ b/lisp/button.el @@ -467,13 +467,22 @@ return t." (button-activate button use-mouse-action) t)))) +(defun button--help-echo (button) + "Evaluate BUTTON's `help-echo' property and return its value." + (let ((help (button-get button 'help-echo))) + (if (functionp help) + (let ((obj (if (overlayp button) button (current-buffer)))) + (funcall help (selected-window) obj (button-start button))) + (eval help lexical-binding)))) + (defun forward-button (n &optional wrap display-message no-error) "Move to the Nth next button, or Nth previous button if N is negative. If N is 0, move to the start of any button at point. If WRAP is non-nil, moving past either end of the buffer continues from the other end. -If DISPLAY-MESSAGE is non-nil, the button's help-echo string is displayed. -Any button with a non-nil `skip' property is skipped over. +If DISPLAY-MESSAGE is non-nil, the button's `help-echo' property +is displayed. Any button with a non-nil `skip' property is +skipped over. If NO-ERROR, return nil if no further buttons could be found instead of erroring out. @@ -506,13 +515,9 @@ Returns the button found." (unless (button-get button 'skip) (setq n (1- n))))))) (if (null button) - (if no-error - nil + (unless no-error (user-error (if wrap "No buttons!" "No more buttons"))) - (let ((msg (and display-message (button-get button 'help-echo)))) - (when (functionp msg) - (setq msg (funcall msg (selected-window) (current-buffer) - (button-start button)))) + (let ((msg (and display-message (button--help-echo button)))) (when msg (message "%s" msg))) button))) @@ -522,8 +527,9 @@ Returns the button found." If N is 0, move to the start of any button at point. If WRAP is non-nil, moving past either end of the buffer continues from the other end. -If DISPLAY-MESSAGE is non-nil, the button's help-echo string is displayed. -Any button with a non-nil `skip' property is skipped over. +If DISPLAY-MESSAGE is non-nil, the button's `help-echo' property +is displayed. Any button with a non-nil `skip' property is +skipped over. If NO-ERROR, return nil if no further buttons could be found instead of erroring out. diff --git a/test/lisp/button-tests.el b/test/lisp/button-tests.el index d54a992ab89..44a7ea6f6e5 100644 --- a/test/lisp/button-tests.el +++ b/test/lisp/button-tests.el @@ -37,4 +37,60 @@ (widget-create 'link "link widget") (should-not (button-at (1- (point)))))) +(ert-deftest button--help-echo-string () + "Test `button--help-echo' with strings." + (with-temp-buffer + ;; Text property buttons. + (let ((button (insert-text-button "text" 'help-echo "text help"))) + (should (equal (button--help-echo button) "text help"))) + ;; Overlay buttons. + (let ((button (insert-button "overlay" 'help-echo "overlay help"))) + (should (equal (button--help-echo button) "overlay help"))))) + +(ert-deftest button--help-echo-form () + "Test `button--help-echo' with forms." + (with-temp-buffer + ;; Test text property buttons with dynamic scoping. + (let* ((help (make-symbol "help")) + (form `(funcall (let ((,help "lexical form")) + (lambda () ,help)))) + (button (insert-text-button "text" 'help-echo form))) + (set help "dynamic form") + (should (equal (button--help-echo button) "dynamic form"))) + ;; Test overlay buttons with lexical scoping. + (setq lexical-binding t) + (let* ((help (make-symbol "help")) + (form `(funcall (let ((,help "lexical form")) + (lambda () ,help)))) + (button (insert-button "overlay" 'help-echo form))) + (set help "dynamic form") + (should (equal (button--help-echo button) "lexical form"))))) + +(ert-deftest button--help-echo-function () + "Test `button--help-echo' with functions." + (with-temp-buffer + ;; Text property buttons. + (let* ((owin (selected-window)) + (obuf (current-buffer)) + (opos (point)) + (help (lambda (win obj pos) + (should (eq win owin)) + (should (eq obj obuf)) + (should (= pos opos)) + "text function")) + (button (insert-text-button "text" 'help-echo help))) + (should (equal (button--help-echo button) "text function")) + ;; Overlay buttons. + (setq help (lambda (win obj pos) + (should (eq win owin)) + (should (overlayp obj)) + (should (eq obj button)) + (should (eq (overlay-buffer obj) obuf)) + (should (= (overlay-start obj) opos)) + (should (= pos opos)) + "overlay function")) + (setq opos (point)) + (setq button (insert-button "overlay" 'help-echo help)) + (should (equal (button--help-echo button) "overlay function"))))) + ;;; button-tests.el ends here