From 3939b5f285d50631bba9197f5069d60799f44112 Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Thu, 11 Mar 2021 22:06:53 +0100 Subject: [PATCH 01/10] Expose buttons as `interactive' commands I often found it useful to view a symbol in the manual, jump to its source etc., but navigating to the corresponding buttons was cumbersome. As such, expose them as `interactive' commands: `helpful-view-in-manual', .... Refactor the "toggle tracing" code into a separate function, since it used by the toggle trace button and its corresponding `interactive' command. --- helpful.el | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/helpful.el b/helpful.el index 723746c..e34d96a 100644 --- a/helpful.el +++ b/helpful.el @@ -434,12 +434,16 @@ or disable if already enabled." 'symbol nil 'help-echo "Toggle function tracing") +(defun helpful--toggle-tracing (sym) + "Toggle `trace-function' for SYM." + (if (trace-is-traced sym) + (untrace-function sym) + (trace-function sym))) + (defun helpful--trace (button) "Toggle tracing for the current symbol." (let ((sym (button-get button 'symbol))) - (if (trace-is-traced sym) - (untrace-function sym) - (trace-function sym))) + (helpful--toggle-tracing sym)) (helpful-update)) (define-button-type 'helpful-navigate-button @@ -1115,6 +1119,46 @@ unescaping too." 'follow-link t 'help-echo "Follow this link") +(defun helpful--ensure () + "Ensure that the `helpful--sym' is available." + (unless helpful--sym + (user-error "Not in a *helpful* buffer"))) + +(defun helpful-view-in-manual () + "Open the manual for the current symbol." + (interactive) + (helpful--ensure) + (info-lookup 'symbol helpful--sym #'emacs-lisp-mode)) + +(defun helpful-goto-source () + "View the source code of the current symbol. +If the source file is no longer available, display it in a new +buffer." + (interactive) + (helpful--ensure) + (-let [(buf pos opened) (helpful--definition helpful--sym helpful--callable-p)] + (unless buf + (user-error "Could not locate source")) + (when pos + (with-current-buffer buf + (goto-char pos))) + (pop-to-buffer buf))) + +(defun helpful-toggle-edebug () + "Toggle edebug for the current symbol." + (interactive) + (helpful--ensure) + (unless helpful--callable-p + (user-error "Cannot `edebug' a variable")) + (helpful--toggle-edebug helpful--sym)) + +(defun helpful-toggle-tracing () + (interactive) + (helpful--ensure) + (unless helpful--callable-p + (user-error "Cannot trace a variable")) + (helpful--toggle-tracing helpful--sym)) + (defun helpful--propertize-links (docstring) "Convert URL links in docstrings to buttons." (replace-regexp-in-string From 3008abcf646fe9f7c9efbd18d12f3a974f26436e Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Thu, 11 Mar 2021 22:14:35 +0100 Subject: [PATCH 02/10] Add keybindings for the new button commands --- helpful.el | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/helpful.el b/helpful.el index e34d96a..c36ad4d 100644 --- a/helpful.el +++ b/helpful.el @@ -2903,6 +2903,11 @@ See also `helpful-max-buffers'." (define-key map (kbd "n") #'forward-button) (define-key map (kbd "p") #'backward-button) + + (define-key map (kbd "C-c m") #'helpful-view-in-manual) + (define-key map (kbd "C-c s") #'helpful-goto-source) + (define-key map (kbd "C-c e") #'helpful-toggle-edebug) + (define-key map (kbd "C-c t") #'helpful-toggle-tracing) map) "Keymap for `helpful-mode'.") From 2584a229a9f08c7a08c6a3e1e5c200da2cea182f Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Thu, 11 Mar 2021 22:17:22 +0100 Subject: [PATCH 03/10] Refactor: use `-doto' for `helpful-mode-map' --- helpful.el | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/helpful.el b/helpful.el index c36ad4d..76a5ca0 100644 --- a/helpful.el +++ b/helpful.el @@ -2894,21 +2894,20 @@ See also `helpful-max-buffers'." (kill-buffer buffer)))) (defvar helpful-mode-map - (let* ((map (make-sparse-keymap))) - (define-key map (kbd "g") #'helpful-update) - (define-key map (kbd "RET") #'helpful-visit-reference) + (-doto (make-sparse-keymap) + (define-key (kbd "g") #'helpful-update) + (define-key (kbd "RET") #'helpful-visit-reference) - (define-key map (kbd "TAB") #'forward-button) - (define-key map (kbd "") #'backward-button) + (define-key (kbd "TAB") #'forward-button) + (define-key (kbd "") #'backward-button) - (define-key map (kbd "n") #'forward-button) - (define-key map (kbd "p") #'backward-button) + (define-key (kbd "n") #'forward-button) + (define-key (kbd "p") #'backward-button) - (define-key map (kbd "C-c m") #'helpful-view-in-manual) - (define-key map (kbd "C-c s") #'helpful-goto-source) - (define-key map (kbd "C-c e") #'helpful-toggle-edebug) - (define-key map (kbd "C-c t") #'helpful-toggle-tracing) - map) + (define-key (kbd "C-c m") #'helpful-view-in-manual) + (define-key (kbd "C-c s") #'helpful-goto-source) + (define-key (kbd "C-c e") #'helpful-toggle-edebug) + (define-key (kbd "C-c t") #'helpful-toggle-tracing)) "Keymap for `helpful-mode'.") (declare-function bookmark-prop-get "bookmark" (bookmark prop)) From fb1c9e018ed58da0e13ff3b35e7de29f98cc3226 Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Thu, 11 Mar 2021 22:34:26 +0100 Subject: [PATCH 04/10] `helpful--ensure': use `derived-mode-p' `nil' is also a symbol. --- helpful.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpful.el b/helpful.el index 76a5ca0..5fedc1f 100644 --- a/helpful.el +++ b/helpful.el @@ -1120,8 +1120,8 @@ unescaping too." 'help-echo "Follow this link") (defun helpful--ensure () - "Ensure that the `helpful--sym' is available." - (unless helpful--sym + "Ensure that we are in a *helpful* buffer." + (unless (derived-mode-p 'helpful-mode) (user-error "Not in a *helpful* buffer"))) (defun helpful-view-in-manual () From 83cbcda802edceec47e2ed41360e9746ef8a78a2 Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Fri, 12 Mar 2021 07:55:12 +0100 Subject: [PATCH 05/10] Add set-value and customize commands --- helpful.el | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/helpful.el b/helpful.el index 5fedc1f..eb0b8fb 100644 --- a/helpful.el +++ b/helpful.el @@ -592,11 +592,9 @@ overrides that to include previously opened buffers." 'follow-link t 'help-echo "Set the value of this symbol") -(defun helpful--set (button) - "Set the value of this symbol." - (let* ((sym (button-get button 'symbol)) - (buf (button-get button 'buffer)) - (sym-value (helpful--sym-value sym buf)) +(defun helpful--set-1 (sym &optional buf) + "Set the value of SYM in BUF, or globally if it is nil." + (let* ((sym-value (helpful--sym-value sym buf)) ;; Inspired by `counsel-read-setq-expression'. (expr (minibuffer-with-setup-hook @@ -626,6 +624,10 @@ overrides that to include previously opened buffers." (eval-expression expr)) (helpful-update))) +(defun helpful--set (button) + "Set the value of this symbol." + (helpful--set-1 (button-get button 'symbol) (button-get button 'buffer))) + (define-button-type 'helpful-view-literal-button 'action #'helpful--view-literal 'help-echo "Toggle viewing as a literal") @@ -1159,6 +1161,20 @@ buffer." (user-error "Cannot trace a variable")) (helpful--toggle-tracing helpful--sym)) +(defun helpful-set-value () + (interactive) + (helpful--ensure) + (when helpful--callable-p + (user-error "Cannot set function")) + (helpful--set-1 helpful--sym helpful--associated-buffer)) + +(defun helpful-customize () + (interactive) + (helpful--ensure) + (when helpful--callable-p + (user-error "Cannot customize functions")) + (customize-variable helpful--sym)) + (defun helpful--propertize-links (docstring) "Convert URL links in docstrings to buttons." (replace-regexp-in-string @@ -2907,7 +2923,9 @@ See also `helpful-max-buffers'." (define-key (kbd "C-c m") #'helpful-view-in-manual) (define-key (kbd "C-c s") #'helpful-goto-source) (define-key (kbd "C-c e") #'helpful-toggle-edebug) - (define-key (kbd "C-c t") #'helpful-toggle-tracing)) + (define-key (kbd "C-c t") #'helpful-toggle-tracing) + (define-key (kbd "C-c =") #'helpful-set-value) + (define-key (kbd "C-c c") #'helpful-customize)) "Keymap for `helpful-mode'.") (declare-function bookmark-prop-get "bookmark" (bookmark prop)) From d9d816933890d029efcb95467262a7b4ffb63257 Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Fri, 12 Mar 2021 07:57:32 +0100 Subject: [PATCH 06/10] `helpful-toggle-tracing': update helpful buffer --- helpful.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helpful.el b/helpful.el index eb0b8fb..e372866 100644 --- a/helpful.el +++ b/helpful.el @@ -1159,7 +1159,8 @@ buffer." (helpful--ensure) (unless helpful--callable-p (user-error "Cannot trace a variable")) - (helpful--toggle-tracing helpful--sym)) + (helpful--toggle-tracing helpful--sym) + (helpful-update)) (defun helpful-set-value () (interactive) From b6b64e1d92957eb75027b83a6ba0b9cf94fcaeac Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Fri, 12 Mar 2021 08:04:28 +0100 Subject: [PATCH 07/10] Add `disassemble' command --- helpful.el | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/helpful.el b/helpful.el index e372866..6cf6437 100644 --- a/helpful.el +++ b/helpful.el @@ -1146,6 +1146,12 @@ buffer." (goto-char pos))) (pop-to-buffer buf))) +(defun helpful-disassemble () + "Disassemble the current symbol." + (interactive) + (helpful--ensure) + (disassemble helpful--sym)) + (defun helpful-toggle-edebug () "Toggle edebug for the current symbol." (interactive) @@ -2926,7 +2932,8 @@ See also `helpful-max-buffers'." (define-key (kbd "C-c e") #'helpful-toggle-edebug) (define-key (kbd "C-c t") #'helpful-toggle-tracing) (define-key (kbd "C-c =") #'helpful-set-value) - (define-key (kbd "C-c c") #'helpful-customize)) + (define-key (kbd "C-c c") #'helpful-customize) + (define-key (kbd "C-c d") #'helpful-disassemble)) "Keymap for `helpful-mode'.") (declare-function bookmark-prop-get "bookmark" (bookmark prop)) From 0f2058213f0ec3aa6a7f11f382e7fb8c6ea94c3a Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Fri, 12 Mar 2021 08:37:22 +0100 Subject: [PATCH 08/10] Forget, calles and show-all-references Store the source sexp in helpful buffers, since it is needed by show-callees. This should now be all buttons. --- helpful.el | 70 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/helpful.el b/helpful.el index 6cf6437..b6d4e23 100644 --- a/helpful.el +++ b/helpful.el @@ -57,6 +57,8 @@ (defvar-local helpful--sym nil) (defvar-local helpful--callable-p nil) +(defvar-local helpful--source-sexp nil + "Source-code sexp for the current symbol.") (defvar-local helpful--associated-buffer nil "The buffer being used when showing inspecting buffer-local variables.") @@ -312,12 +314,10 @@ Return SYM otherwise." 'follow-link t 'help-echo "Unbind this function") -;; TODO: it would be nice to optionally delete the source code too. -(defun helpful--forget (button) - "Unbind the current symbol." - (let* ((sym (button-get button 'symbol)) - (callable-p (button-get button 'callable-p)) - (kind (helpful--kind-name sym callable-p))) +(defun helpful--forget-1 (sym callable-p) + "Forge SYM interactively. +If CALLABLE-P is given, SYM is assumed to be a function." + (let ((kind (helpful--kind-name sym callable-p))) (when (yes-or-no-p (format "Forget %s %s?" kind sym)) (if callable-p (fmakunbound sym) @@ -325,6 +325,13 @@ Return SYM otherwise." (message "Forgot %s %s." kind sym) (kill-buffer (current-buffer))))) +;; TODO: it would be nice to optionally delete the source code too. +(defun helpful--forget (button) + "Unbind the current symbol." + (let* ((sym (button-get button 'symbol)) + (callable-p (button-get button 'callable-p))) + (helpful--forget-1 sym callable-p))) + (define-button-type 'helpful-c-source-directory 'action #'helpful--c-source-directory 'follow-link t @@ -645,17 +652,22 @@ overrides that to include previously opened buffers." 'follow-link t 'help-echo "Find all references to this symbol") +(defun helpful--all-references-1 (sym callable-p) + "Find all references of SYM. +If CALLABLE-P, SYM is a function or macro." + (cond + ((not callable-p) + (elisp-refs-variable sym)) + ((functionp sym) + (elisp-refs-function sym)) + ((macrop sym) + (elisp-refs-macro sym)))) + (defun helpful--all-references (button) "Find all the references to the symbol that this BUTTON represents." (let ((sym (button-get button 'symbol)) (callable-p (button-get button 'callable-p))) - (cond - ((not callable-p) - (elisp-refs-variable sym)) - ((functionp sym) - (elisp-refs-function sym)) - ((macrop sym) - (elisp-refs-macro sym))))) + (helpful--all-references-1 sym callable-p))) (define-button-type 'helpful-callees-button 'action #'helpful--show-callees @@ -675,11 +687,10 @@ overrides that to include previously opened buffers." 'callable-p t) "\n"))) -(defun helpful--show-callees (button) - "Find all the references to the symbol that this BUTTON represents." +(defun helpful--show-callees-1 (sym raw-source) + "Show all callees of SYM with RAW-SOURCE. +See `helpful--source-sexp' for RAW-SOURCE." (let* ((buf (get-buffer-create "*helpful callees*")) - (sym (button-get button 'symbol)) - (raw-source (button-get button 'source)) (source (if (stringp raw-source) (read raw-source) @@ -705,6 +716,11 @@ overrides that to include previously opened buffers." (helpful-mode)))) +(defun helpful--show-callees (button) + "Find all the references to the symbol that this BUTTON represents." + (helpful--show-callees-1 (button-get button 'symbol) + (button-get button 'source))) + (define-button-type 'helpful-manual-button 'action #'helpful--manual 'symbol nil @@ -1146,6 +1162,10 @@ buffer." (goto-char pos))) (pop-to-buffer buf))) +(defun helpful-forget () + "Forget the current helpful symbol." + (helpful--forget-1 helpful--sym helpful--callable-p)) + (defun helpful-disassemble () "Disassemble the current symbol." (interactive) @@ -1168,6 +1188,19 @@ buffer." (helpful--toggle-tracing helpful--sym) (helpful-update)) +(defun helpful-callees () + (interactive) + (helpful--ensure) + (unless (and helpful--callable-p + (not (helpful--primitive-p helpful--sym helpful--callable-p))) + (user-error "Must be a non-primtive function")) + (helpful--show-callees-1 helpful--sym helpful--source-sexp)) + +(defun helpful-all-references () + (interactive) + (helpful--ensure) + (helpful--all-references-1 helpful--sym helpful--callable-p)) + (defun helpful-set-value () (interactive) (helpful--ensure) @@ -2220,7 +2253,8 @@ state of the current symbol." (helpful--definition helpful--sym helpful--callable-p) '(nil nil nil))) (source (when look-for-src - (helpful--source helpful--sym helpful--callable-p buf pos))) + (->> (helpful--source helpful--sym helpful--callable-p buf pos) + (setq-local helpful--source-sexp)))) (source-path (when buf (buffer-file-name buf))) (references (helpful--calculate-references From 7a7dbacd78f16df8e351b3cf2f1b0f4b5ff9e664 Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Fri, 12 Mar 2021 18:57:11 +0100 Subject: [PATCH 09/10] Fix byte-compile: unused symbol --- helpful.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helpful.el b/helpful.el index b6d4e23..f6cd6da 100644 --- a/helpful.el +++ b/helpful.el @@ -1154,7 +1154,8 @@ If the source file is no longer available, display it in a new buffer." (interactive) (helpful--ensure) - (-let [(buf pos opened) (helpful--definition helpful--sym helpful--callable-p)] + (-let [(buf pos _opened) + (helpful--definition helpful--sym helpful--callable-p)] (unless buf (user-error "Could not locate source")) (when pos From 19503dceb2b34a38c1d6b982b74effdf6f461139 Mon Sep 17 00:00:00 2001 From: Nikita Bloshchanevich Date: Sun, 14 Mar 2021 17:33:46 +0100 Subject: [PATCH 10/10] Add "view literally" command --- helpful.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/helpful.el b/helpful.el index f6cd6da..f144c3c 100644 --- a/helpful.el +++ b/helpful.el @@ -639,12 +639,17 @@ overrides that to include previously opened buffers." 'action #'helpful--view-literal 'help-echo "Toggle viewing as a literal") -(defun helpful--view-literal (_button) - "Set the value of this symbol." +(defun helpful-view-literal () + "Toggle literal/pretty view." + (interactive) (setq helpful--view-literal (not helpful--view-literal)) (helpful-update)) +(defun helpful--view-literal (_button) + "Toggle literal/pretty view." + (helpful-view-literal)) + (define-button-type 'helpful-all-references-button 'action #'helpful--all-references 'symbol nil