Skip to content

Instantly share code, notes, and snippets.

@sogaiu
Last active May 3, 2021 11:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sogaiu/49ef2dbd37d3b8319cc5e70dfc166326 to your computer and use it in GitHub Desktop.
Save sogaiu/49ef2dbd37d3b8319cc5e70dfc166326 to your computer and use it in GitHub Desktop.
edebug.el source notes
;; Install edebug read and eval functions.
(edebug-install-read-eval-functions)
;
(defun edebug-install-read-eval-functions ()
(interactive)
(add-function :around load-read-function #'edebug--read)
(advice-add 'eval-defun :override #'edebug-eval-defun))
;
;;; Redefine read and eval functions
;; read is redefined to maybe instrument forms.
;; eval-defun is redefined to check edebug-all-forms and edebug-all-defs.
(defun edebug--read (orig &optional stream)
"Read one Lisp expression as text from STREAM, return as Lisp object.
If STREAM is nil, use the value of `standard-input' (which see).
STREAM or the value of `standard-input' may be:
a buffer (read from point and advance it)
a marker (read from where it points and advance it)
a function (call it with no arguments for each character,
call it with a char as argument to push a char back)
a string (takes text from string, starting at the beginning)
t (read text line using minibuffer and use it).
This version, from Edebug, maybe instruments the expression. But the
STREAM must be the current buffer to do so. Whether it instruments is
also dependent on the values of the option `edebug-all-defs' and
the option `edebug-all-forms'."
(or stream (setq stream standard-input))
(if (eq stream (current-buffer))
(edebug-read-and-maybe-wrap-form)
(funcall (or orig #'read) stream)))
;
;; We should somehow arrange to be able to do this
;; without actually replacing the eval-defun command.
(defun edebug-eval-defun (edebug-it)
"Evaluate the top-level form containing point, or after point.
If the current defun is actually a call to `defvar', then reset the
variable using its initial value expression even if the variable
already has some other value. (Normally `defvar' does not change the
variable's value if it already has a value.) Treat `defcustom'
similarly. Reinitialize the face according to `defface' specification.
With a prefix argument, instrument the code for Edebug.
Setting option `edebug-all-defs' to a non-nil value reverses the meaning
of the prefix argument. Code is then instrumented when this function is
invoked without a prefix argument.
If acting on a `defun' for FUNCTION, and the function was instrumented,
`Edebug: FUNCTION' is printed in the minibuffer. If not instrumented,
just FUNCTION is printed.
If not acting on a `defun', the result of evaluation is displayed in
the minibuffer."
(interactive "P")
(let* ((edebugging (not (eq (not edebug-it) (not edebug-all-defs))))
(edebug-result)
(form
(let ((edebug-all-forms edebugging)
(edebug-all-defs (eq edebug-all-defs (not edebug-it))))
(edebug-read-top-level-form))))
;; This should be consistent with `eval-defun-1', but not the
;; same, since that gets a macroexpanded form.
(cond ((and (eq (car form) 'defvar)
(cdr-safe (cdr-safe form)))
;; Force variable to be bound.
(makunbound (nth 1 form)))
((and (eq (car form) 'defcustom)
(default-boundp (nth 1 form)))
;; Force variable to be bound.
;; FIXME: Shouldn't this use the :setter or :initializer?
(set-default (nth 1 form) (eval (nth 2 form) lexical-binding)))
((eq (car form) 'defface)
;; Reset the face.
(setq face-new-frame-defaults
(assq-delete-all (nth 1 form) face-new-frame-defaults))
(put (nth 1 form) 'face-defface-spec nil)
(put (nth 1 form) 'face-documentation (nth 3 form))
;; See comments in `eval-defun-1' for purpose of code below
(setq form (prog1 `(prog1 ,form
(put ',(nth 1 form) 'saved-face
',(get (nth 1 form) 'saved-face))
(put ',(nth 1 form) 'customized-face
,(nth 2 form)))
(put (nth 1 form) 'saved-face nil)))))
(setq edebug-result (eval (eval-sexp-add-defvars form) lexical-binding))
(if (not edebugging)
(prog1
(prin1 edebug-result)
(let ((str (eval-expression-print-format edebug-result)))
(if str (princ str))))
edebug-result)))
;;;
;; given the function definition:
;;
;; (defun my-fun-3 (z)
;; (/ z 3))
;;
;; examining behavior of edebug-make-form-wrapper (triggered via
;; edebug-defun), yielded sample values for:
;;
;; * form-data-entry
;; (my-fun-3
;; #<marker at 234 in *scratch*>
;; #<marker at 263 in *scratch*>)
;;
;; * edebug-offset-list
;; [22 26 29]
;;
;; * result (the return value of the function)
;; (
;; (defun my-fun-3 (z)
;; (edebug-enter 'my-fun-3
;; (list z)
;; #'(lambda nil
;; (edebug-after (edebug-before 0)
;; 2
;; (/ (edebug-after 0 1 z) 3)))))
;; )
;;
;; format-data-entry's value can be retrieved later via:
;;
;; (edebug-get-form-data-entry 234 263)
;;
;; apparently, the above pulls stuff out of edebug-form-data, which
;; (roughly) stores earlier values too:
;;
;; ((my-fun-3 #<marker at 234 in *scratch*> #<marker at 263 in *scratch*>)
;; (my-fun-2 #<marker at 188 in *scratch*> #<marker at 217 in *scratch*>)
;; (my-fun #<marker at 146 in *scratch*> #<marker at 173 in *scratch*>))
;;
;; according to docs, edebug-form-data is a list of entries where the first
;; element of an entry is a symbol. the symbol apparently "holds all
;; debug related information for the form on its property list"
;;
;; such info can be examined by evaluating (symbol-plist 'my-fun):
;;
;; (edebug-freq-count [1 1 1]
;; edebug-coverage [edebug-unknown edebug-unknown edebug-unknown]
;; edebug (#<marker at 146 in *scratch*>
;; nil
;; [20 24 27]
;; (#<window 3 on *scratch*> . 1))
;; edebug-behavior edebug)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment