diff options
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/button.el | 45 | ||||
| -rw-r--r-- | lisp/help-fns.el | 44 | ||||
| -rw-r--r-- | lisp/wid-edit.el | 57 |
4 files changed, 151 insertions, 0 deletions
| @@ -122,6 +122,11 @@ horizontal movements now stop at the edge of the board. | |||
| 122 | ** Autosaving via 'auto-save-visited-mode' can now be inhibited by | 122 | ** Autosaving via 'auto-save-visited-mode' can now be inhibited by |
| 123 | setting the variable 'auto-save-visited-mode' buffer-locally to nil. | 123 | setting the variable 'auto-save-visited-mode' buffer-locally to nil. |
| 124 | 124 | ||
| 125 | ** New commands to describe buttons and widgets have been added. | ||
| 126 | 'describe-widget' (on a widget) will pop up a help buffer and give a | ||
| 127 | description of the properties. Likewise 'describe-button' does the | ||
| 128 | same for a button. | ||
| 129 | |||
| 125 | 130 | ||
| 126 | * Changes in Specialized Modes and Packages in Emacs 28.1 | 131 | * Changes in Specialized Modes and Packages in Emacs 28.1 |
| 127 | 132 | ||
diff --git a/lisp/button.el b/lisp/button.el index d9c36a0375c..941b9fe720a 100644 --- a/lisp/button.el +++ b/lisp/button.el | |||
| @@ -555,6 +555,51 @@ Returns the button found." | |||
| 555 | (interactive "p\nd\nd") | 555 | (interactive "p\nd\nd") |
| 556 | (forward-button (- n) wrap display-message no-error)) | 556 | (forward-button (- n) wrap display-message no-error)) |
| 557 | 557 | ||
| 558 | (defun button--describe (properties) | ||
| 559 | "Describe a button's PROPERTIES (an alist) in a *Help* buffer. | ||
| 560 | This is a helper function for `button-describe', in order to be possible to | ||
| 561 | use `help-setup-xref'. | ||
| 562 | |||
| 563 | Each element of PROPERTIES should be of the form (PROPERTY . VALUE)." | ||
| 564 | (help-setup-xref (list #'button--describe properties) | ||
| 565 | (called-interactively-p 'interactive)) | ||
| 566 | (with-help-window (help-buffer) | ||
| 567 | (with-current-buffer (help-buffer) | ||
| 568 | (insert (format-message "This button's type is `%s'." | ||
| 569 | (alist-get 'type properties))) | ||
| 570 | (dolist (prop '(action mouse-action)) | ||
| 571 | (let ((name (symbol-name prop)) | ||
| 572 | (val (alist-get prop properties))) | ||
| 573 | (when (functionp val) | ||
| 574 | (insert "\n\n" | ||
| 575 | (propertize (capitalize name) 'face 'bold) | ||
| 576 | "\nThe " name " of this button is") | ||
| 577 | (if (symbolp val) | ||
| 578 | (progn | ||
| 579 | (insert (format-message " `%s',\nwhich is " val)) | ||
| 580 | (describe-function-1 val)) | ||
| 581 | (insert "\n") | ||
| 582 | (princ val)))))))) | ||
| 583 | |||
| 584 | (defun button-describe (&optional button-or-pos) | ||
| 585 | "Display a buffer with information about the button at point. | ||
| 586 | |||
| 587 | When called from Lisp, pass BUTTON-OR-POS as the button to describe, or a | ||
| 588 | buffer position where a button is present. If BUTTON-OR-POS is nil, the | ||
| 589 | button at point is the button to describe." | ||
| 590 | (interactive "d") | ||
| 591 | (let* ((button (cond ((integer-or-marker-p button-or-pos) | ||
| 592 | (button-at button-or-pos)) | ||
| 593 | ((null button-or-pos) (button-at (point))) | ||
| 594 | ((overlayp button-or-pos) button-or-pos))) | ||
| 595 | (props (and button | ||
| 596 | (mapcar (lambda (prop) | ||
| 597 | (cons prop (button-get button prop))) | ||
| 598 | '(type action mouse-action))))) | ||
| 599 | (when props | ||
| 600 | (button--describe props) | ||
| 601 | t))) | ||
| 602 | |||
| 558 | (provide 'button) | 603 | (provide 'button) |
| 559 | 604 | ||
| 560 | ;;; button.el ends here | 605 | ;;; button.el ends here |
diff --git a/lisp/help-fns.el b/lisp/help-fns.el index b9536470631..5a99103f6af 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el | |||
| @@ -1769,6 +1769,50 @@ documentation for the major and minor modes of that buffer." | |||
| 1769 | ;; For the sake of IELM and maybe others | 1769 | ;; For the sake of IELM and maybe others |
| 1770 | nil) | 1770 | nil) |
| 1771 | 1771 | ||
| 1772 | ;; Widgets. | ||
| 1773 | |||
| 1774 | (defvar describe-widget-functions | ||
| 1775 | '(button-describe widget-describe) | ||
| 1776 | "A list of functions for `describe-widget' to call. | ||
| 1777 | Each function should take one argument, a buffer position, and return | ||
| 1778 | non-nil if it described a widget at that position.") | ||
| 1779 | |||
| 1780 | ;;;###autoload | ||
| 1781 | (defun describe-widget (&optional pos) | ||
| 1782 | "Display a buffer with information about a widget. | ||
| 1783 | You can use this command to describe buttons (e.g., the links in a *Help* | ||
| 1784 | buffer), editable fields of the customization buffers, etc. | ||
| 1785 | |||
| 1786 | Interactively, click on a widget to describe it, or hit RET to describe the | ||
| 1787 | widget at point. | ||
| 1788 | |||
| 1789 | When called from Lisp, POS may be a buffer position or a mouse position list. | ||
| 1790 | |||
| 1791 | Calls each function of the list `describe-widget-functions' in turn, until | ||
| 1792 | one of them returns non-nil." | ||
| 1793 | (interactive | ||
| 1794 | (list | ||
| 1795 | (let ((key | ||
| 1796 | (read-key | ||
| 1797 | "Click on a widget, or hit RET to describe the widget at point"))) | ||
| 1798 | (cond ((eq key ?\C-m) (point)) | ||
| 1799 | ((and (mouse-event-p key) | ||
| 1800 | (eq (event-basic-type key) 'mouse-1) | ||
| 1801 | (equal (event-modifiers key) '(click))) | ||
| 1802 | (event-end key)) | ||
| 1803 | ((eq key ?\C-g) (signal 'quit nil)) | ||
| 1804 | (t (user-error "You didn't specify a widget")))))) | ||
| 1805 | (let (buf) | ||
| 1806 | ;; Allow describing a widget in a different window. | ||
| 1807 | (when (posnp pos) | ||
| 1808 | (setq buf (window-buffer (posn-window pos)) | ||
| 1809 | pos (posn-point pos))) | ||
| 1810 | (with-current-buffer (or buf (current-buffer)) | ||
| 1811 | (unless (cl-some (lambda (fun) (when (fboundp fun) (funcall fun pos))) | ||
| 1812 | describe-widget-functions) | ||
| 1813 | (message "No widget found at that position"))))) | ||
| 1814 | |||
| 1815 | |||
| 1772 | ;;; Replacements for old lib-src/ programs. Don't seem especially useful. | 1816 | ;;; Replacements for old lib-src/ programs. Don't seem especially useful. |
| 1773 | 1817 | ||
| 1774 | ;; Replaces lib-src/digest-doc.c. | 1818 | ;; Replaces lib-src/digest-doc.c. |
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el index 284fd1d6cbd..ea7e266e0d0 100644 --- a/lisp/wid-edit.el +++ b/lisp/wid-edit.el | |||
| @@ -577,6 +577,63 @@ respectively." | |||
| 577 | (if (and widget (funcall function widget maparg)) | 577 | (if (and widget (funcall function widget maparg)) |
| 578 | (setq overlays nil))))) | 578 | (setq overlays nil))))) |
| 579 | 579 | ||
| 580 | (defun widget-describe (&optional widget-or-pos) | ||
| 581 | "Describe the widget at point. | ||
| 582 | Displays a buffer with information about the widget (e.g., its actions) as well | ||
| 583 | as a link to browse all the properties of the widget. | ||
| 584 | |||
| 585 | This command resolves the indirection of widgets running the action of its | ||
| 586 | parents, so the real action executed can be known. | ||
| 587 | |||
| 588 | When called from Lisp, pass WIDGET-OR-POS as the widget to describe, | ||
| 589 | or a buffer position where a widget is present. If WIDGET-OR-POS is nil, | ||
| 590 | the widget at point is the widget to describe." | ||
| 591 | (interactive "d") | ||
| 592 | (require 'wid-browse) ; The widget-browse widget. | ||
| 593 | (let ((widget (if (widgetp widget-or-pos) | ||
| 594 | widget-or-pos | ||
| 595 | (widget-at widget-or-pos))) | ||
| 596 | props) | ||
| 597 | (when widget | ||
| 598 | (help-setup-xref (list #'widget-describe widget) | ||
| 599 | (called-interactively-p 'interactive)) | ||
| 600 | (setq props (list (cons 'action (widget--resolve-parent-action widget)) | ||
| 601 | (cons 'mouse-down-action | ||
| 602 | (widget-get widget :mouse-down-action)))) | ||
| 603 | (with-help-window (help-buffer) | ||
| 604 | (with-current-buffer (help-buffer) | ||
| 605 | (widget-insert "This widget's type is ") | ||
| 606 | (widget-create 'widget-browse :format "%[%v%]\n%d" | ||
| 607 | :doc (get (car widget) 'widget-documentation) | ||
| 608 | :help-echo "Browse this widget's properties" | ||
| 609 | widget) | ||
| 610 | (dolist (action '(action mouse-down-action)) | ||
| 611 | (let ((name (symbol-name action)) | ||
| 612 | (val (alist-get action props))) | ||
| 613 | (when (functionp val) | ||
| 614 | (widget-insert "\n\n" (propertize (capitalize name) 'face 'bold) | ||
| 615 | "'\nThe " name " of this widget is") | ||
| 616 | (if (symbolp val) | ||
| 617 | (progn (widget-insert " ") | ||
| 618 | (widget-create 'function-link :value val | ||
| 619 | :button-prefix "" :button-suffix "" | ||
| 620 | :help-echo "Describe this function")) | ||
| 621 | (widget-insert "\n") | ||
| 622 | (princ val))))))) | ||
| 623 | (widget-setup) | ||
| 624 | t))) | ||
| 625 | |||
| 626 | (defun widget--resolve-parent-action (widget) | ||
| 627 | "Resolve the real action of WIDGET up its inheritance chain. | ||
| 628 | Follow the WIDGET's parents, until its :action is no longer | ||
| 629 | `widget-parent-action', and return its value." | ||
| 630 | (let ((action (widget-get widget :action)) | ||
| 631 | (parent (widget-get widget :parent))) | ||
| 632 | (while (eq action 'widget-parent-action) | ||
| 633 | (setq parent (widget-get parent :parent) | ||
| 634 | action (widget-get parent :action))) | ||
| 635 | action)) | ||
| 636 | |||
| 580 | ;;; Images. | 637 | ;;; Images. |
| 581 | 638 | ||
| 582 | (defcustom widget-image-directory (file-name-as-directory | 639 | (defcustom widget-image-directory (file-name-as-directory |