helpful

a fork of Wilfred's helpful
git clone https://git.trogloxene.org/helpful.git
Log | Files | Refs | README

commit a7f762fa50dd1bbac9c546599f89e3bb037f43c7
parent 134b10848c6cc8529903562d2ab61bd347ee1da6
Author: Wilfred Hughes <me@wilfred.me.uk>
Date:   Tue, 12 Dec 2017 20:50:18 +0000

Allow enabling edebug from helpful buffers

Diffstat:
MCHANGELOG.md | 2++
Mhelpful.el | 76+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 73 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -1,5 +1,7 @@ # v0.4 +You can now enable edebug directly from helpful buffers! + Variables may be set from the helpful buffer, and booleans can be toggled. If a variable is a `defcustom`, we also offer Customize. diff --git a/helpful.el b/helpful.el @@ -50,6 +50,7 @@ (require 'find-func) (require 'nadvice) (require 'info-look) +(require 'edebug) (defvar-local helpful--sym nil) (defvar-local helpful--callable-p nil) @@ -157,6 +158,56 @@ This allows us to distinguish strings from symbols." "Disassemble the current symbol." (disassemble (button-get button 'symbol))) +(define-button-type 'helpful-edebug-button + 'action #'helpful--edebug + 'follow-link t + 'symbol nil + 'help-echo "Toggle edebug (re-evaluates definition)") + +(defun helpful--edebug-p (sym) + "Does function SYM have its definition patched by edebug?" + (let ((fn-def (indirect-function sym))) + ;; Edebug replaces function source code with a sexp that has + ;; `edebug-enter', `edebug-after' etc interleaved. This means the + ;; function is interpreted, so `indirect-function' returns a list. + (when (and (consp fn-def) (consp (cdr fn-def))) + (eq (car (-last-item fn-def)) 'edebug-enter)))) + +(defun helpful--can-edebug-p (sym callable-p) + "Can we use edebug with SYM?" + (and + ;; SYM must be a function. + callable-p + ;; The function cannot be a primitive, it must be defined in elisp. + (not (helpful--primitive-p sym callable-p)) + ;; We need to be able to find its definition, or we can't step + ;; through the source. + (-when-let ((buf . pos) (helpful--definition sym t)) + t))) + +(defun helpful--toggle-edebug (sym) + "Enable edebug when function SYM is called, +or disable if already enabled." + (-if-let ((buf . pos) (helpful--definition sym t)) + (with-current-buffer buf + (save-excursion + (save-restriction + (widen) + (goto-char pos) + + (let* ((should-edebug (not (helpful--edebug-p sym))) + (edebug-all-forms should-edebug) + (edebug-all-defs should-edebug) + (form (edebug-read-top-level-form))) + ;; Based on `edebug-eval-defun'. + (eval (eval-sexp-add-defvars form) lexical-binding))))) + (user-error "Could not find source for edebug"))) + +(defun helpful--edebug (button) + "Toggle edebug for the current symbol." + (helpful--toggle-edebug (button-get button 'symbol)) + (helpful-update)) + (define-button-type 'helpful-navigate-button 'action #'helpful--navigate 'path nil @@ -721,10 +772,25 @@ state of the current symbol." (format "This %s is advised." (if (macrop helpful--sym) "macro" "function")))) - (insert (helpful--heading "\n\nDebugging\n\n")) - (let ((can-disassemble - (and helpful--callable-p - (not primitive-p)))) + (let ((can-edebug + (helpful--can-edebug-p helpful--sym helpful--callable-p)) + (can-disassemble + (and helpful--callable-p (not primitive-p))) + (can-forget + (not (special-form-p helpful--sym)))) + (when (or can-edebug can-disassemble can-forget) + (insert (helpful--heading "\n\nDebugging\n"))) + (when can-edebug + (insert + (make-text-button + (if (helpful--edebug-p helpful--sym) + "Disable edebug" + "Enable edebug") + nil + :type 'helpful-edebug-button + 'symbol helpful--sym) + "\n")) + (when can-disassemble (insert (make-text-button @@ -732,7 +798,7 @@ state of the current symbol." :type 'helpful-disassemble-button 'symbol helpful--sym))) - (unless (special-form-p helpful--sym) + (when can-forget (when can-disassemble (insert " ")) (insert