From bb58dc2ab253b92b4e5683136d12eb86a3473410 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" Date: Fri, 27 Sep 2019 00:04:33 +0100 Subject: [PATCH] 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. --- doc/lispref/display.texi | 19 +++++++++---- lisp/button.el | 26 +++++++++++------- test/lisp/button-tests.el | 56 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 15 deletions(-) diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 64b24f712a..d2074aa60c 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -6606,14 +6606,23 @@ Button Properties 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 9112e518b0..ae2488bac6 100644 --- a/lisp/button.el +++ b/lisp/button.el @@ -470,13 +470,22 @@ push-button (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. @@ -509,13 +518,9 @@ forward-button (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))) @@ -525,8 +530,9 @@ backward-button 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 d54a992ab8..44a7ea6f6e 100644 --- a/test/lisp/button-tests.el +++ b/test/lisp/button-tests.el @@ -37,4 +37,60 @@ button-at (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 -- 2.23.0