Mercurial > hg > xemacs-beta
diff lisp/edebug/edebug.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | b82b59fe008d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/edebug/edebug.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,4534 @@ +;;; edebug.el --- a source-level debugger for Emacs Lisp + +;; Copyright (C) 1988,'89,'90,'91,'92,'93,'94,'95 Free Software Foundation, Inc + +;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu> +;; Keywords: lisp, tools, maint + +;; LCD Archive Entry: +;; edebug|Daniel LaLiberte|liberte@cs.uiuc.edu +;; |A source level debugger for Emacs Lisp. +;; |$Date: 1996/12/18 03:33:28 $|$Revision: 1.1.1.1 $|~/modes/edebug.el| + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;;;; Commentary: + +;;; This minor mode allows programmers to step through Emacs Lisp +;;; source code while executing functions. You can also set +;;; breakpoints, trace (stopping at each expression), evaluate +;;; expressions as if outside Edebug, reevaluate and display a list of +;;; expressions, trap errors normally caught by debug, and display a +;;; debug style backtrace. + +;;;; Installation +;;; ============= + +;;; Put edebug.el in some directory in your load-path and +;;; byte-compile it. Also read the beginning of edebug-epoch.el, +;;; cl-specs.el, and edebug-cl-read.el if they apply to you. + +;;; Unless you are using Emacs 19 which is already set up to use Edebug, +;;; put the following forms in your .emacs file. +;;; (define-key emacs-lisp-mode-map "\C-xx" 'edebug-eval-top-level-form) +;;; (autoload 'edebug-eval-top-level-form "edebug") + +;;; If you wish to change the default edebug global command prefix, change: +;;; (setq edebug-global-prefix "\C-xX") + +;;; Other options, are described in the manual. + +;;; In previous versions of Edebug, users were directed to set +;;; `debugger' to `edebug-debug'. This is no longer necessary +;;; since Edebug automatically sets it whenever Edebug is active. + +;;;; Minimal Instructions +;;; ===================== + +;;; First evaluate a defun with C-xx, then run the function. Step +;;; through the code with SPC, mark breakpoints with b, go until a +;;; breakpoint is reached with g, and quit execution with q. Use the +;;; "?" command in edebug to describe other commands. See edebug.tex +;;; or the Emacs 19 Lisp Reference Manual for more instructions. + +;;; Send me your enhancements, ideas, bugs, or fixes. +;;; For bugs, you can call edebug-submit-bug-report if you have reporter.el. +;;; There is an edebug mailing list if you want to keep up +;;; with the latest developments. Requests to: edebug-request@cs.uiuc.edu + +;;; Daniel LaLiberte 217-398-4114 +;;; University of Illinois, Urbana-Champaign +;;; Department of Computer Science +;;; 1304 W Springfield +;;; Urbana, IL 61801 + +;;; uiucdcs!liberte +;;; liberte@cs.uiuc.edu + +;;; =============================== +;;; For the early revision history, see edebug-history. + +(defconst edebug-version + (let ((raw-version "$Revision: 1.1.1.1 $")) + (substring raw-version (string-match "[0-9.]*" raw-version) + (match-end 0)))) + +(require 'backquote) + +;; Emacs 18 doesnt have defalias. +(eval-and-compile + (or (fboundp 'defalias) (fset 'defalias 'fset))) + + +;;;; Bug reporting +;;; ============== + +(defconst edebug-maintainer-address "liberte@cs.uiuc.edu") + +(defun edebug-submit-bug-report () + "Submit, via mail, a bug report on edebug." + (interactive) + (require 'reporter) + (and (y-or-n-p "Do you really want to submit a report on edebug? ") + (reporter-submit-bug-report + edebug-maintainer-address + (concat "edebug.el " edebug-version) + (list 'edebug-setup-hook + 'edebug-all-defs + 'edebug-all-forms + 'edebug-eval-macro-args + 'edebug-stop-before-symbols + 'edebug-save-windows + 'edebug-save-displayed-buffer-points + 'edebug-initial-mode + 'edebug-trace + 'edebug-test-coverage + 'edebug-continue-kbd-macro + 'edebug-print-length + 'edebug-print-level + 'edebug-print-circle + )))) + + +;;;; Options +;;; =============================== + +(defvar edebug-setup-hook nil + "*Functions to call before edebug is used. +Each time it is set to a new value, Edebug will call those functions +once and then `edebug-setup-hook' is reset to nil. You could use this +to load up Edebug specifications associated with a package you are +using but only when you also use Edebug.") + +(defvar edebug-all-defs nil + "*If non-nil, evaluation of any defining forms will instrument for Edebug. +This applies to `eval-defun', `eval-region', `eval-buffer', and +`eval-current-buffer'. `eval-region' is also called by +`eval-last-sexp', and `eval-print-last-sexp'. + +You can use the command `edebug-all-defs' to toggle the value of this +variable. You may wish to make it local to each buffer with +\(make-local-variable 'edebug-all-defs) in your +`emacs-lisp-mode-hook'.") + +(defvar edebug-all-forms nil + "*Non-nil evaluation of all forms will instrument for Edebug. +This doesn't apply to loading or evaluations in the minibuffer. +Use the command `edebug-all-forms' to toggle the value of this option.") + +(defvar edebug-eval-macro-args nil + "*Non-nil means all macro call arguments may be evaluated. +If this variable is nil, the default, Edebug will *not* wrap +macro call arguments as if they will be evaluated. +For each macro, a `edebug-form-spec' overrides this option. +So to specify exceptions for macros that have some arguments evaluated +and some not, you should specify an `edebug-form-spec'. + +This option is going away soon.") + +(defvar edebug-stop-before-symbols nil + "*Non-nil causes Edebug to stop before symbols as well as after. +In any case, a breakpoint or interrupt may stop before a symbol. + +This option is going away soon.") + +(defvar edebug-save-windows t + "*If non-nil, Edebug saves and restores the window configuration. +That takes some time, so if your program does not care what happens to +the window configurations, it is better to set this variable to nil. + +If the value is a list, only the listed windows are saved and +restored. + +`edebug-toggle-save-windows' may be used to change this variable.") + +(defvar edebug-save-displayed-buffer-points nil + "*If non-nil, save and restore point in all displayed buffers. + +Saving and restoring point in other buffers is necessary if you are +debugging code that changes the point of a buffer which is displayed +in a non-selected window. If Edebug or the user then selects the +window, the buffer's point will be changed to the window's point. + +Saving and restoring point in all buffers is expensive, since it +requires selecting each window twice, so enable this only if you need +it.") + +(defvar edebug-initial-mode 'step + "*Initial execution mode for Edebug, if non-nil. If this variable +is non-@code{nil}, it specifies the initial execution mode for Edebug +when it is first activated. Possible values are step, next, go, +Go-nonstop, trace, Trace-fast, continue, and Continue-fast.") + +(defvar edebug-trace nil + "*Non-nil means display a trace of function entry and exit. +Tracing output is displayed in a buffer named `*edebug-trace*', one +function entry or exit per line, indented by the recursion level. + +You can customize by replacing functions `edebug-print-trace-before' +and `edebug-print-trace-after'.") + +(defvar edebug-test-coverage nil + "*If non-nil, Edebug tests coverage of all expressions debugged. +This is done by comparing the result of each expression +with the previous result. Coverage is considered OK if two different +results are found. + +Use `edebug-display-freq-count' to display the frequency count and +coverage information for a definition.") + +(defvar edebug-continue-kbd-macro nil + "*If non-nil, continue defining or executing any keyboard macro. +Use this with caution since it is not debugged.") + + +(defvar edebug-print-length 50 + "*Default value of `print-length' to use while printing results in Edebug.") +(defvar edebug-print-level 50 + "*Default value of `print-level' to use while printing results in Edebug.") +(defvar edebug-print-circle t + "*Default value of `print-circle' to use while printing results in Edebug.") + +(defvar edebug-unwrap-results nil + "*Non-nil if Edebug should unwrap results of expressions. +This is useful when debugging macros where the results of expressions +are instrumented expressions. But don't do this when results might be +circular or an infinite loop will result.") + +(defvar edebug-on-error t + "*Value bound to `debug-on-error' while Edebug is active. + +If `debug-on-error' is non-nil, that value is still used. + +If the value is a list of signal names, Edebug will stop when any of +these errors are signaled from Lisp code whether or not the signal is +handled by a `condition-case'. This option is useful for debugging +signals that *are* handled since they would otherwise be missed. +After execution is resumed, the error is signaled again.") + +(defvar edebug-on-quit t + "*Value bound to `debug-on-quit' while Edebug is active.") + +(defvar edebug-global-break-condition nil + "*If non-nil, an expression to test for at every stop point. +If the result is non-nil, then break. Errors are ignored.") + + +;;;; Form spec utilities. +;;; =============================== + +;;;###autoload +(defmacro def-edebug-spec (symbol spec) + "Set the edebug-form-spec property of SYMBOL according to SPEC. +Both SYMBOL and SPEC are unevaluated. The SPEC can be 0, t, a symbol +\(naming a function), or a list." + (` (put (quote (, symbol)) 'edebug-form-spec (quote (, spec))))) + +(defmacro def-edebug-form-spec (symbol spec-form) + "For compatibility with old version. Use `def-edebug-spec' instead." + (message "Obsolete: use def-edebug-spec instead.") + (def-edebug-spec symbol (eval spec-form))) + +(defun get-edebug-spec (symbol) + ;; Get the spec of symbol resolving all indirection. + (let ((edebug-form-spec (get symbol 'edebug-form-spec)) + indirect) + (while (and (symbolp edebug-form-spec) + (setq indirect (get edebug-form-spec 'edebug-form-spec))) + ;; (edebug-trace "indirection: %s" edebug-form-spec) + (setq edebug-form-spec indirect)) + edebug-form-spec + )) + + +;;;; Utilities +;;; =============================== + +;; Define edebug-gensym - from old cl.el +(defvar edebug-gensym-index 0 + "Integer used by `edebug-gensym' to produce new names.") + +(defun edebug-gensym (&optional prefix) + "Generate a fresh uninterned symbol. +There is an optional argument, PREFIX. PREFIX is the +string that begins the new name. Most people take just the default, +except when debugging needs suggest otherwise." + (if (null prefix) + (setq prefix "G")) + (let ((newsymbol nil) + (newname "")) + (while (not newsymbol) + (setq newname (concat prefix (int-to-string edebug-gensym-index))) + (setq edebug-gensym-index (+ edebug-gensym-index 1)) + (if (not (intern-soft newname)) + (setq newsymbol (make-symbol newname)))) + newsymbol)) + +;; Only used by CL-like code. +(defun edebug-keywordp (object) + "Return t if OBJECT is a keyword. +A keyword is a symbol that starts with `:'." + (and (symbolp object) + (= ?: (aref (symbol-name object) 0)))) + +(defun edebug-lambda-list-keywordp (object) + "Return t if OBJECT is a lambda list keyword. +A lambda list keyword is a symbol that starts with `&'." + (and (symbolp object) + (= ?& (aref (symbol-name object) 0)))) + + +(defun edebug-last-sexp () + ;; Return the last sexp before point in current buffer. + ;; Assumes Emacs Lisp syntax is active. + (car + (read-from-string + (buffer-substring + (save-excursion + (forward-sexp -1) + (point)) + (point))))) + +(defun edebug-window-list () + "Return a list of windows, in order of `next-window'." + ;; This doesnt work for epoch. + (let* ((first-window (selected-window)) + (window-list (list first-window)) + (next (next-window first-window))) + (while (not (eq next first-window)) + (setq window-list (cons next window-list)) + (setq next (next-window next))) + (nreverse window-list))) + +(defun edebug-window-live-p (window) + "Return non-nil if WINDOW is visible." + (let* ((first-window (selected-window)) + (next (next-window first-window t))) + (while (not (or (eq next window) + (eq next first-window))) + (setq next (next-window next t))) + (eq next window))) + +;; Not used. +'(defun edebug-two-window-p () + "Return t if there are two windows." + (and (not (one-window-p)) + (eq (selected-window) + (next-window (next-window (selected-window)))))) + +(defsubst edebug-lookup-function (object) + (while (and (symbolp object) (fboundp object)) + (setq object (symbol-function object))) + object) + +(defun edebug-macrop (object) + "Return the macro named by OBJECT, or nil if it is not a macro." + (setq object (edebug-lookup-function object)) + (if (and (listp object) + (eq 'macro (car object)) + (edebug-functionp (cdr object))) + object)) + +(defun edebug-functionp (object) + "Returns the function named by OBJECT, or nil if it is not a function." + (setq object (edebug-lookup-function object)) + (if (or (subrp object) + (byte-code-function-p object) + (and (listp object) + (eq (car object) 'lambda) + (listp (car (cdr object))))) + object)) + +(defun edebug-sort-alist (alist function) + ;; Return the ALIST sorted with comparison function FUNCTION. + ;; This uses 'sort so the sorting is destructive. + (sort alist (function + (lambda (e1 e2) + (funcall function (car e1) (car e2)))))) + +;;(def-edebug-spec edebug-save-restriction t) + +;; Not used. If it is used, def-edebug-spec must be defined before use. +'(defmacro edebug-save-restriction (&rest body) + "Evaluate BODY while saving the current buffers restriction. +BODY may change buffer outside of current restriction, unlike +save-restriction. BODY may change the current buffer, +and the restriction will be restored to the original buffer, +and the current buffer remains current. +Return the result of the last expression in BODY." + (` (let ((edebug:s-r-beg (point-min-marker)) + (edebug:s-r-end (point-max-marker))) + (unwind-protect + (progn (,@ body)) + (save-excursion + (set-buffer (marker-buffer edebug:s-r-beg)) + (narrow-to-region edebug:s-r-beg edebug:s-r-end)))))) + +;;;; Display +;;; ============ + +(defconst edebug-trace-buffer "*edebug-trace*" + "Name of the buffer to put trace info in.") + +(defun edebug-pop-to-buffer (buffer &optional window) + ;; Like pop-to-buffer, but select window where BUFFER was last shown. + ;; Select WINDOW if it provided and it still exists. Otherwise, + ;; if buffer is currently shown in several windows, choose one. + ;; Otherwise, find a new window, possibly splitting one. + (setq window (if (and (windowp window) (edebug-window-live-p window) + (eq (window-buffer window) buffer)) + window + (if (eq (window-buffer (selected-window)) buffer) + (selected-window) + (edebug-get-buffer-window buffer)))) + (if window + (select-window window) + (if (one-window-p) + (split-window)) + ;; (message "next window: %s" (next-window)) (sit-for 1) + (if (eq (get-buffer-window edebug-trace-buffer) (next-window)) + ;; Dont select trace window + nil + (select-window (next-window)))) + (set-window-buffer (selected-window) buffer) + (set-window-hscroll (selected-window) 0);; should this be?? + ;; Selecting the window does not set the buffer until command loop. + ;;(set-buffer buffer) + ) + + +(defun edebug-get-displayed-buffer-points () + ;; Return a list of buffer point pairs, for all displayed buffers. + (save-excursion + (let* ((first-window (selected-window)) + (next (next-window first-window)) + (buffer-point-list nil) + buffer) + (while (not (eq next first-window)) + (set-buffer (setq buffer (window-buffer next))) + (setq buffer-point-list + (cons (cons buffer (point)) buffer-point-list)) + (setq next (next-window next))) + buffer-point-list))) + + +(defun edebug-set-buffer-points (buffer-points) + ;; Restore the buffer-points created by edebug-get-displayed-buffer-points. + (let ((current-buffer (current-buffer))) + (mapcar (function (lambda (buf-point) + (if (buffer-name (car buf-point)) ; still exists + (progn + (set-buffer (car buf-point)) + (goto-char (cdr buf-point)))))) + buffer-points) + (set-buffer current-buffer))) + +(defun edebug-current-windows (which-windows) + ;; Get either a full window configuration or some window information. + (if (listp which-windows) + (mapcar (function (lambda (window) + (if (edebug-window-live-p window) + (list window + (window-buffer window) + (window-point window) + (window-start window) + (window-hscroll window))))) + which-windows) + (current-window-configuration))) + +(defun edebug-set-windows (window-info) + ;; Set either a full window configuration or some window information. + (if (listp window-info) + (mapcar (function + (lambda (one-window-info) + (if one-window-info + (apply (function + (lambda (window buffer point start hscroll) + (if (edebug-window-live-p window) + (progn + (set-window-buffer window buffer) + (set-window-point window point) + (set-window-start window start) + (set-window-hscroll window hscroll))))) + one-window-info)))) + window-info) + (set-window-configuration window-info))) + +(defalias 'edebug-get-buffer-window 'get-buffer-window) +(defalias 'edebug-sit-for 'sit-for) +(defalias 'edebug-input-pending-p 'input-pending-p) + + +;;;; 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. + +;;; Use the Lisp version of eval-region. +(require 'eval-reg "eval-reg") + +;; Save the original read function +(or (fboundp 'edebug-original-read) + (defalias 'edebug-original-read (symbol-function 'read))) + +(defun edebug-read (&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 instuments is +also dependent on the values of `edebug-all-defs' and +`edebug-all-forms'." + (or stream (setq stream standard-input)) + (if (eq stream (current-buffer)) + (edebug-read-and-maybe-wrap-form) + (edebug-original-read stream))) + +(or (fboundp 'edebug-original-eval-defun) + (defalias 'edebug-original-eval-defun (symbol-function 'eval-defun))) + +(defun edebug-eval-defun (edebug-it) + "Evaluate the top-level form containing point, or after point. + +This version, from Edebug, has the following differences: With a +prefix argument instrument the code for Edebug. If `edebug-all-defs' is +non-nil, then the code is instrumented *unless* there is a prefix +argument. If instrumenting, it prints: `Edebug: FUNCTIONNAME'. +Otherwise, it prints in the minibuffer." + (interactive "P") + (let ((edebugging (not (eq (not edebug-it) (not edebug-all-defs)))) + (edebug-result)) + (setq edebug-result + (eval + (let ((edebug-all-forms edebugging) + (edebug-all-defs (eq edebug-all-defs (not edebug-it)))) + (edebug-read-top-level-form)))) + (if (not edebugging) + (princ edebug-result) + edebug-result))) + + +;;;###autoload +(defalias 'edebug-defun 'edebug-eval-top-level-form) + +;;;###autoload +(defun edebug-eval-top-level-form () + "Evaluate a top level form, such as a defun or defmacro. +This is like `eval-defun', but the code is always instrumented for Edebug. +Print its name in the minibuffer and leave point where it is, +or if an error occurs, leave point after it with mark at the original point." + (interactive) + (eval + ;; Bind edebug-all-forms only while reading, not while evaling + ;; but this causes problems while edebugging edebug. + (let ((edebug-all-forms t) + (edebug-all-defs t)) + (edebug-read-top-level-form)))) + + +(defun edebug-read-top-level-form () + (let ((starting-point (point))) + (end-of-defun) + (beginning-of-defun) + (prog1 + (edebug-read-and-maybe-wrap-form) + ;; Recover point, but only if no error occurred. + (goto-char starting-point)))) + + +;; Compatibility with old versions. +(defalias 'edebug-all-defuns 'edebug-all-defs) + +(defun edebug-all-defs () + "Toggle edebugging of all definitions." + (interactive) + (setq edebug-all-defs (not edebug-all-defs)) + (message "Edebugging all definitions is %s." + (if edebug-all-defs "on" "off"))) + + +(defun edebug-all-forms () + "Toggle edebugging of all forms." + (interactive) + (setq edebug-all-forms (not edebug-all-forms)) + (message "Edebugging all forms is %s." + (if edebug-all-forms "on" "off"))) + + +(defun edebug-install-read-eval-functions () + (interactive) + ;; Don't install if already installed. + (if (eq (symbol-function 'read) 'edebug-read) nil + (elisp-eval-region-install) + (defalias 'read 'edebug-read) + (defalias 'eval-defun 'edebug-eval-defun))) + +(defun edebug-uninstall-read-eval-functions () + (interactive) + (elisp-eval-region-uninstall) + (defalias 'read (symbol-function 'edebug-original-read)) + (defalias 'eval-defun (symbol-function 'edebug-original-eval-defun))) + + +;;;; Edebug internal data +;;; =============================== + +;;; The internal data that is needed for edebugging is kept in the +;;; buffer-local variable `edebug-form-data'. + +;; XEmacs change? +(defconst edebug-form-data nil) +(make-variable-buffer-local 'edebug-form-data) + +;; A list of entries associating symbols with buffer regions. +;; This is an automatic buffer local variable. Each entry looks like: +;; @code{(@var{symbol} @var{begin-marker} @var{end-marker}). The markers +;; are at the beginning and end of an entry level form and @var{symbol} is +;; a symbol that holds all edebug related information for the form on its +;; property list. + +;; In the future, the symbol will be irrelevant and edebug data will +;; be stored in the definitions themselves rather than in the property +;; list of a symbol. + +(defun edebug-make-form-data-entry (symbol begin end) + (list symbol begin end)) + +(defsubst edebug-form-data-name (entry) + (car entry)) + +(defsubst edebug-form-data-begin (entry) + (nth 1 entry)) + +(defsubst edebug-form-data-end (entry) + (nth 2 entry)) + +(defsubst edebug-set-form-data-entry (entry name begin end) + (setcar entry name);; in case name is changed + (set-marker (nth 1 entry) begin) + (set-marker (nth 2 entry) end)) + +(defun edebug-get-form-data-entry (pnt &optional end-point) + ;; Find the edebug form data entry which is closest to PNT. + ;; If END-POINT is supplied, match must be exact. + ;; Return `nil' if none found. + (let ((rest edebug-form-data) + closest-entry + (closest-dist 999999)) ;; need maxint here + (while (and rest (< 0 closest-dist)) + (let* ((entry (car rest)) + (begin (edebug-form-data-begin entry)) + (dist (- pnt begin))) + (setq rest (cdr rest)) + (if (and (<= 0 dist) + (< dist closest-dist) + (or (not end-point) + (= end-point (edebug-form-data-end entry))) + (<= pnt (edebug-form-data-end entry))) + (setq closest-dist dist + closest-entry entry)))) + closest-entry)) + +;; Also need to find all contained entries, +;; and find an entry given a symbol, which should be just assq. + +(defun edebug-form-data-symbol () +;; Return the edebug data symbol of the form where point is in. +;; If point is not inside a edebuggable form, cause error. + (or (edebug-form-data-name (edebug-get-form-data-entry (point))) + (error "Not inside instrumented form"))) + +(defun edebug-make-top-form-data-entry (new-entry) + ;; Make NEW-ENTRY the first element in the `edebug-form-data' list. + (edebug-clear-form-data-entry new-entry) + (setq edebug-form-data (cons new-entry edebug-form-data))) + +(defun edebug-clear-form-data-entry (entry) +;; If non-nil, clear ENTRY out of the form data. +;; Maybe clear the markers and delete the symbol's edebug property? + (if entry + (progn + ;; Instead of this, we could just find all contained forms. + ;; (put (car entry) 'edebug nil) ; + ;; (mapcar 'edebug-clear-form-data-entry ; dangerous + ;; (get (car entry) 'edebug-dependents)) + ;; (set-marker (nth 1 entry) nil) + ;; (set-marker (nth 2 entry) nil) + (setq edebug-form-data (delq entry edebug-form-data))))) + + +;;;; Parser utilities +;;; =============================== + + +(defun edebug-syntax-error (&rest args) + ;; Signal an invalid-read-syntax with ARGS. + (signal 'invalid-read-syntax args)) + + +(defconst edebug-read-syntax-table + ;; Lookup table for significant characters indicating the class of the + ;; token that follows. This is not a \"real\" syntax table. + (let ((table (make-vector 256 'symbol)) + (i 0)) + (while (< i ?!) + (aset table i 'space) + (setq i (1+ i))) + (aset table ?\( 'lparen) + (aset table ?\) 'rparen) + (aset table ?\' 'quote) + (aset table ?\" 'string) + (aset table ?\? 'char) + (aset table ?\[ 'lbracket) + (aset table ?\] 'rbracket) + (aset table ?\. 'dot) + (aset table ?\# 'hash) + ;; We treat numbers as symbols, because of confusion with -, -1, and 1-. + ;; We dont care about any other chars since they wont be seen. + table)) + +(defun edebug-next-token-class () + ;; Move to the next token and return its class. We only care about + ;; lparen, rparen, dot, quote, string, char, vector, or symbol. + (edebug-skip-whitespace) + (aref edebug-read-syntax-table (following-char))) + + +(defun edebug-skip-whitespace () + ;; Leave point before the next token, skipping white space and comments. + (skip-chars-forward " \t\r\n\f") + (while (= (following-char) ?\;) + ;; \r is counted as a comment terminator to support selective display. + (skip-chars-forward "^\n\r") ; skip the comment + (skip-chars-forward " \t\r\n\f"))) + + +;; Mostly obsolete reader; still used in one case. + +(defun edebug-read-sexp () + ;; Read one sexp from the current buffer starting at point. + ;; Leave point immediately after it. A sexp can be a list or atom. + ;; An atom is a symbol (or number), character, string, or vector. + ;; This works for reading anything legitimate, but it + ;; is gummed up by parser inconsistencies (bugs?) + (let ((class (edebug-next-token-class))) + (cond + ;; read goes one too far if a (possibly quoted) string or symbol + ;; is immediately followed by non-whitespace. + ((eq class 'symbol) (prog1 + (edebug-original-read (current-buffer)) + (if (not (eq (aref edebug-read-syntax-table + (preceding-char)) 'symbol)) + (forward-char -1)))) + ((eq class 'string) (prog1 + (edebug-original-read (current-buffer)) + (if (/= (preceding-char) ?\") + (forward-char -1)))) + ((eq class 'quote) (forward-char 1) + (list 'quote (edebug-read-sexp))) + (t ; anything else, just read it. + (edebug-original-read (current-buffer)))))) + + +;;;; Offsets for reader +;;; ============================== + +;; Define a structure to represent offset positions of expressions. +;; Each offset structure looks like: (before . after) for constituents, +;; or for structures that have elements: (before <subexpressions> . after) +;; where the <subexpressions> are the offset structures for subexpressions +;; including the head of a list. +(defconst edebug-offsets nil) + +;; Stack of offset structures in reverse order of the nesting. +;; This is used to get back to previous levels. +(defconst edebug-offsets-stack nil) +(defconst edebug-current-offset nil) ; Top of the stack, for convenience. + +;; We must store whether we just read a list with a dotted form that +;; is itself a list. This structure will be condensed, so the offsets +;; must also be condensed. +(defconst edebug-read-dotted-list nil) + +(defsubst edebug-initialize-offsets () + ;; Reinitialize offset recording. + (setq edebug-current-offset nil)) + +(defun edebug-store-before-offset (point) + ;; Add a new offset pair with POINT as the before offset. + (let ((new-offset (list point))) + (if edebug-current-offset + (setcdr edebug-current-offset + (cons new-offset (cdr edebug-current-offset))) + ;; Otherwise, we are at the top level, so initialize. + (setq edebug-offsets new-offset + edebug-offsets-stack nil + edebug-read-dotted-list nil)) + ;; Cons the new offset to the front of the stack. + (setq edebug-offsets-stack (cons new-offset edebug-offsets-stack) + edebug-current-offset new-offset) + )) + +(defun edebug-store-after-offset (point) + ;; Finalize the current offset struct by reversing it and + ;; store POINT as the after offset. + (if (not edebug-read-dotted-list) + ;; Just reverse the offsets of all subexpressions. + (setcdr edebug-current-offset (nreverse (cdr edebug-current-offset))) + + ;; We just read a list after a dot, which will be abbreviated out. + (setq edebug-read-dotted-list nil) + ;; Drop the corresponding offset pair. + ;; That is, nconc the reverse of the rest of the offsets + ;; with the cdr of last offset. + (setcdr edebug-current-offset + (nconc (nreverse (cdr (cdr edebug-current-offset))) + (cdr (car (cdr edebug-current-offset)))))) + + ;; Now append the point using nconc. + (setq edebug-current-offset (nconc edebug-current-offset point)) + ;; Pop the stack. + (setq edebug-offsets-stack (cdr edebug-offsets-stack) + edebug-current-offset (car edebug-offsets-stack))) + +(defun edebug-ignore-offset () + ;; Ignore the last created offset pair. + (setcdr edebug-current-offset (cdr (cdr edebug-current-offset)))) + +(def-edebug-spec edebug-storing-offsets (form body)) +(put 'edebug-storing-offsets 'lisp-indent-hook 1) + +(defmacro edebug-storing-offsets (point &rest body) + (` (unwind-protect + (progn + (edebug-store-before-offset (, point)) + (,@ body)) + (edebug-store-after-offset (point))))) + + +;;;; Reader for Emacs Lisp. +;;; ========================================== +;; Uses edebug-next-token-class (and edebug-skip-whitespace) above. + +(defconst edebug-read-alist + '((symbol . edebug-read-symbol) + (lparen . edebug-read-list) + (string . edebug-read-string) + (quote . edebug-read-quote) + (lbracket . edebug-read-vector) + (hash . edebug-read-function) + )) + +(defun edebug-read-storing-offsets (stream) + (let ((class (edebug-next-token-class)) + func + edebug-read-dotted-list) ; see edebug-store-after-offset + (edebug-storing-offsets (point) + (if (setq func (assq class edebug-read-alist)) + (funcall (cdr func) stream) + ;; anything else, just read it. + (edebug-original-read stream)) + ))) + +(defun edebug-read-symbol (stream) + (prog1 + (edebug-original-read stream) + ;; loses for escaped chars + (if (not (eq (aref edebug-read-syntax-table + (preceding-char)) 'symbol)) + (forward-char -1)))) + +(defun edebug-read-string (stream) + (prog1 + (edebug-original-read stream) + (if (/= (preceding-char) ?\") + (forward-char -1)))) + +(defun edebug-read-quote (stream) + ;; Turn 'thing into (quote thing) + (forward-char 1) + (list + (edebug-storing-offsets (point) 'quote) + (edebug-read-storing-offsets stream))) + +(defun edebug-read-function (stream) + ;; Turn #'thing into (function thing) + (forward-char 1) + (if (/= ?\' (following-char)) (edebug-syntax-error "Bad char")) + (forward-char 1) + (list + (edebug-storing-offsets (point) + (if (featurep 'cl) 'function* 'function)) + (edebug-read-storing-offsets stream))) + +(defun edebug-read-list (stream) + (forward-char 1) ; skip \( + (prog1 + (let ((elements)) + (while (not (memq (edebug-next-token-class) '(rparen dot))) + (setq elements (cons (edebug-read-storing-offsets stream) elements))) + (setq elements (nreverse elements)) + (if (eq 'dot (edebug-next-token-class)) + (let (dotted-form) + (forward-char 1) ; skip \. + (setq dotted-form (edebug-read-storing-offsets stream)) + elements (nconc elements dotted-form) + (if (not (eq (edebug-next-token-class) 'rparen)) + (edebug-syntax-error "Expected `)'")) + (setq edebug-read-dotted-list (listp dotted-form)) + )) + elements) + (forward-char 1) ; skip \) + )) + +(defun edebug-read-vector (stream) + (forward-char 1) ; skip \[ + (prog1 + (let ((elements)) + (while (not (eq 'rbracket (edebug-next-token-class))) + (setq elements (cons (edebug-read-storing-offsets stream) elements))) + (apply 'vector (nreverse elements))) + (forward-char 1) ; skip \] + )) + + + +;;;; Cursors for traversal of list and vector elements with offsets. +;;;==================================================================== + +(defvar edebug-dotted-spec nil) + +(defun edebug-new-cursor (expressions offsets) + ;; Return a new cursor for EXPRESSIONS with OFFSETS. + (if (vectorp expressions) + (setq expressions (append expressions nil))) + (cons expressions offsets)) + +(defsubst edebug-set-cursor (cursor expressions offsets) + ;; Set the CURSOR's EXPRESSIONS and OFFSETS to the given. + ;; Return the cursor. + (setcar cursor expressions) + (setcdr cursor offsets) + cursor) + +'(defun edebug-copy-cursor (cursor) + ;; Copy the cursor using the same object and offsets. + (cons (car cursor) (cdr cursor))) + +(defsubst edebug-cursor-expressions (cursor) + (car cursor)) +(defsubst edebug-cursor-offsets (cursor) + (cdr cursor)) + +(defsubst edebug-empty-cursor (cursor) + ;; Return non-nil if CURSOR is empty - meaning no more elements. + (null (car cursor))) + +(defsubst edebug-top-element (cursor) + ;; Return the top element at the cursor. + ;; Assumes not empty. + (car (car cursor))) + +(defun edebug-top-element-required (cursor &rest error) + ;; Check if a dotted form is required. + (if edebug-dotted-spec (edebug-no-match cursor "Dot expected.")) + ;; Check if there is at least one more argument. + (if (edebug-empty-cursor cursor) (apply 'edebug-no-match cursor error)) + ;; Return that top element. + (edebug-top-element cursor)) + +(defsubst edebug-top-offset (cursor) + ;; Return the top offset pair corresponding to the top element. + (car (cdr cursor))) + +(defun edebug-move-cursor (cursor) + ;; Advance and return the cursor to the next element and offset. + ;; throw no-match if empty before moving. + ;; This is a violation of the cursor encapsulation, but + ;; there is plenty of that going on while matching. + ;; The following test should always fail. + (if (edebug-empty-cursor cursor) + (edebug-no-match cursor "Not enough arguments.")) + (setcar cursor (cdr (car cursor))) + (setcdr cursor (cdr (cdr cursor))) + cursor) + + +(defun edebug-before-offset (cursor) + ;; Return the before offset of the cursor. + ;; If there is nothing left in the offsets, + ;; return one less than the offset itself, + ;; which is the after offset for a list. + (let ((offset (edebug-cursor-offsets cursor))) + (if (consp offset) + (car (car offset)) + (1- offset)))) + +(defun edebug-after-offset (cursor) + ;; Return the after offset of the cursor object. + (let ((offset (edebug-top-offset cursor))) + (while (consp offset) + (setq offset (cdr offset))) + offset)) + +;;;; The Parser +;;; =============================== + +;;; The top level function for parsing forms is +;;; edebug-read-and-maybe-wrap-form; it calls all the rest. It checks the +;;; syntax a bit and leaves point at any error it finds, but otherwise +;;; should appear to work like eval-defun. + +;;; The basic plan is to surround each expression with a call to +;;; the edebug debugger together with indexes into a table of positions of +;;; all expressions. Thus an expression "exp" becomes: + +;;; (edebug-after (edebug-before 1) 2 exp) + +;;; When this is evaluated, first point is moved to the beginning of +;;; exp at offset 1 of the current function. The expression is +;;; evaluated, which may cause more edebug calls, and then point is +;;; moved to offset 2 after the end of exp. + +;;; The highest level expressions of the function are wrapped in a call to +;;; edebug-enter, which supplies the function name and the actual +;;; arguments to the function. See functions edebug-enter, edebug-before, +;;; and edebug-after for more details. + +;; Dynamically bound vars, left unbound, but globally declared. +;; This is to quiet the byte compiler. + +;; Window data of the highest definition being wrapped. +;; This data is shared by all embedded definitions. +(defvar edebug-top-window-data) + +(defvar edebug-&optional) +(defvar edebug-&rest) +(defvar edebug-gate nil) ;; whether no-match forces an error. + +(defconst edebug-def-name nil) ; name of definition, used by interactive-form +(defconst edebug-old-def-name nil) ; previous name of containing definition. + +(defconst edebug-error-point nil) +(defconst edebug-best-error nil) + + +(defun edebug-read-and-maybe-wrap-form () + ;; Read a form and wrap it with edebug calls, if the conditions are right. + ;; Here we just catch any no-match not caught below and signal an error. + + ;; Run the setup hook. + (run-hooks 'edebug-setup-hook) + (setq edebug-setup-hook nil) + + (let (result + edebug-top-window-data + edebug-def-name;; make sure it is locally nil + ;; I dont like these here!! + edebug-&optional + edebug-&rest + edebug-gate + edebug-best-error + edebug-error-point + no-match + ;; Do this once here instead of several times. + (max-lisp-eval-depth (+ 800 max-lisp-eval-depth)) + (max-specpdl-size (+ 2000 max-specpdl-size))) + (setq no-match + (catch 'no-match + (setq result (edebug-read-and-maybe-wrap-form1)) + nil)) + (if no-match + (apply 'edebug-syntax-error no-match)) + result)) + + +(defun edebug-read-and-maybe-wrap-form1 () + (let (spec + def-kind + defining-form-p + def-name + ;; These offset things dont belong here, but to support recursive + ;; calls to edebug-read, they need to be here. + edebug-offsets + edebug-offsets-stack + edebug-current-offset ; reset to nil + ) + (save-excursion + (if (and (eq 'lparen (edebug-next-token-class)) + (eq 'symbol (progn (forward-char 1) (edebug-next-token-class)))) + ;; Find out if this is a defining form from first symbol + (setq def-kind (edebug-original-read (current-buffer)) + spec (and (symbolp def-kind) (get-edebug-spec def-kind)) + defining-form-p (and (listp spec) + (eq '&define (car spec))) + ;; This is incorrect in general!! But OK most of the time. + def-name (if (and defining-form-p + (eq 'name (car (cdr spec))) + (eq 'symbol (edebug-next-token-class))) + (edebug-original-read (current-buffer)))))) +;;;(message "all defs: %s all forms: %s" edebug-all-defs edebug-all-forms) + (cond + (defining-form-p + (if (or edebug-all-defs edebug-all-forms) + ;; If it is a defining form and we are edebugging defs, + ;; then let edebug-list-form start it. + (let ((cursor (edebug-new-cursor + (list (edebug-read-storing-offsets (current-buffer))) + (list edebug-offsets)))) + (car + (edebug-make-form-wrapper + cursor + (edebug-before-offset cursor) + (1- (edebug-after-offset cursor)) + (list (cons (symbol-name def-kind) (cdr spec)))))) + + ;; Not edebugging this form, so reset the symbol's edebug + ;; property to be just a marker at the definition's source code. + ;; This only works for defs with simple names. + (put def-name 'edebug (point-marker)) + ;; Also nil out dependent defs. + '(mapcar (function + (lambda (def) + (put def-name 'edebug nil))) + (get def-name 'edebug-dependents)) + (edebug-read-sexp))) + + ;; If all forms are being edebugged, explicitly wrap it. + (edebug-all-forms + (let ((cursor (edebug-new-cursor + (list (edebug-read-storing-offsets (current-buffer))) + (list edebug-offsets)))) + (edebug-make-form-wrapper + cursor + (edebug-before-offset cursor) + (edebug-after-offset cursor) + nil))) + + ;; Not a defining form, and not edebugging. + (t (edebug-read-sexp))) + )) + + +(defvar edebug-def-args) ; args of defining form. +(defvar edebug-def-interactive) ; is it an emacs interactive function? +(defvar edebug-inside-func) ;; whether code is inside function context. +;; Currently def-form sets this to nil; def-body sets it to t. + +(defun edebug-interactive-p-name () + ;; Return a unique symbol for the variable used to store the + ;; status of interactive-p for this function. + (intern (format "edebug-%s-interactive-p" edebug-def-name))) + + +(defun edebug-wrap-def-body (forms) + "Wrap the FORMS of a definition body." + (if edebug-def-interactive + (` (let (((, (edebug-interactive-p-name)) + (interactive-p))) + (, (edebug-make-enter-wrapper forms)))) + (edebug-make-enter-wrapper forms))) + + +(defun edebug-make-enter-wrapper (forms) + ;; Generate the enter wrapper for some forms of a definition. + ;; This is not to be used for the body of other forms, e.g. `while', + ;; since it wraps the list of forms with a call to `edebug-enter'. + ;; Uses the dynamically bound vars edebug-def-name and edebug-def-args. + ;; Do this after parsing since that may find a name. + (setq edebug-def-name + (or edebug-def-name edebug-old-def-name (edebug-gensym "edebug-anon"))) + (` (edebug-enter + (quote (, edebug-def-name)) + (, (if edebug-inside-func + (` (list (,@ + ;; Doesnt work with more than one def-body!! + ;; But the list will just be reversed. + (nreverse edebug-def-args)))) + 'nil)) + (function (lambda () (,@ forms))) + ))) + + +(defvar edebug-form-begin-marker) ; the mark for def being instrumented + +(defvar edebug-offset-index) ; the next available offset index. +(defvar edebug-offset-list) ; the list of offset positions. + +(defun edebug-inc-offset (offset) + ;; modifies edebug-offset-index and edebug-offset-list + ;; accesses edebug-func-marc and buffer point + (prog1 + edebug-offset-index + (setq edebug-offset-list (cons (- offset edebug-form-begin-marker) + edebug-offset-list) + edebug-offset-index (1+ edebug-offset-index)))) + + +(defun edebug-make-before-and-after-form (before-index form after-index) + ;; Return the edebug form for the current function at offset BEFORE-INDEX + ;; given FORM. Looks like: + ;; (edebug-after (edebug-before BEFORE-INDEX) AFTER-INDEX FORM) + ;; Also increment the offset index for subsequent use. + ;; if (not edebug-stop-before-symbols) and form is a symbol, + ;; then dont call edebug-before. + (list 'edebug-after + (list 'edebug-before before-index) + after-index form)) + +(defun edebug-make-after-form (form after-index) + ;; Like edebug-make-before-and-after-form, but only after. + (list 'edebug-after 0 after-index form)) + + +(defun edebug-unwrap (sexp) + "Return the unwrapped SEXP or return it as is if it is not wrapped. +The SEXP might be the result of wrapping a body, which is a list of +expressions; a `progn' form will be returned enclosing these forms." + (if (consp sexp) + (cond + ((eq 'edebug-after (car sexp)) + (nth 3 sexp)) + ((eq 'edebug-enter (car sexp)) + (let ((forms (nthcdr 2 (nth 1 (nth 3 sexp))))) + (if (> (length forms) 1) + (cons 'progn forms) ;; could return (values forms) instead. + (car forms)))) + (t sexp);; otherwise it is not wrapped, so just return it. + ) + sexp)) + +(defun edebug-unwrap* (sexp) + "Return the sexp recursively unwrapped." + (let ((new-sexp (edebug-unwrap sexp))) + (while (not (eq sexp new-sexp)) + (setq sexp new-sexp + new-sexp (edebug-unwrap sexp))) + (if (consp new-sexp) + (mapcar 'edebug-unwrap* new-sexp) + new-sexp))) + + +(defun edebug-defining-form (cursor form-begin form-end speclist) + ;; Process the defining form, starting outside the form. + ;; The speclist is a generated list spec that looks like: + ;; (("def-symbol" defining-form-spec-sans-&define)) + ;; Skip the first offset. + (edebug-set-cursor cursor (edebug-cursor-expressions cursor) + (cdr (edebug-cursor-offsets cursor))) + (edebug-make-form-wrapper + cursor + form-begin (1- form-end) + speclist)) + +(defun edebug-make-form-wrapper (cursor form-begin form-end + &optional speclist) + ;; Wrap a form, usually a defining form, but any evaluated one. + ;; If speclist is non-nil, this is being called by edebug-defining-form. + ;; Otherwise it is being called from edebug-read-and-maybe-wrap-form1. + ;; This is a hack, but I havent figured out a simpler way yet. + (let* ((form-data-entry (edebug-get-form-data-entry form-begin form-end)) + ;; Set this marker before parsing. + (edebug-form-begin-marker + (if form-data-entry + (edebug-form-data-begin form-data-entry) + ;; Buffer must be current-buffer for this to work: + (set-marker (make-marker) form-begin)))) + + (let (edebug-offset-list + (edebug-offset-index 0) + result + ;; For definitions. + ;; (edebug-containing-def-name edebug-def-name) + ;; Get name from form-data, if any. + (edebug-old-def-name (edebug-form-data-name form-data-entry)) + edebug-def-name + edebug-def-args + edebug-def-interactive + edebug-inside-func;; whether wrapped code executes inside a function. + ) + + (setq result + (if speclist + (edebug-match cursor speclist) + + ;; else wrap as an enter-form. + (edebug-make-enter-wrapper (list (edebug-form cursor))))) + + ;; Set the name here if it was not set by edebug-make-enter-wrapper. + (setq edebug-def-name + (or edebug-def-name edebug-old-def-name (edebug-gensym "edebug-anon"))) + + ;; Add this def as a dependent of containing def. Buggy. + '(if (and edebug-containing-def-name + (not (get edebug-containing-def-name 'edebug-dependents))) + (put edebug-containing-def-name 'edebug-dependents + (cons edebug-def-name + (get edebug-containing-def-name + 'edebug-dependents)))) + + ;; Create a form-data-entry or modify existing entry's markers. + ;; In the latter case, pointers to the entry remain eq. + (if (not form-data-entry) + (setq form-data-entry + (edebug-make-form-data-entry + edebug-def-name + edebug-form-begin-marker + ;; Buffer must be current-buffer. + (set-marker (make-marker) form-end) + )) + (edebug-set-form-data-entry + form-data-entry edebug-def-name ;; in case name is changed + form-begin form-end)) + + ;; (message "defining: %s" edebug-def-name) (sit-for 2) + (edebug-make-top-form-data-entry form-data-entry) + (message "Edebug: %s" edebug-def-name) + ;;(debug edebug-def-name) + + ;; Destructively reverse edebug-offset-list and make vector from it. + (setq edebug-offset-list (vconcat (nreverse edebug-offset-list))) + + ;; Side effects on the property list of edebug-def-name. + (edebug-clear-frequency-count edebug-def-name) + (edebug-clear-coverage edebug-def-name) + + ;; Set up the initial window data. + (if (not edebug-top-window-data) ;; if not already set, do it now. + (let ((window ;; Find the best window for this buffer. + (or (get-buffer-window (current-buffer)) + (selected-window)))) + (setq edebug-top-window-data + (cons window (window-start window))))) + + ;; Store the edebug data in symbol's property list. + (put edebug-def-name 'edebug + ;; A struct or vector would be better here!! + (list edebug-form-begin-marker + nil ; clear breakpoints + edebug-offset-list + edebug-top-window-data + )) + result + ))) + + +(defun edebug-clear-frequency-count (name) + ;; Create initial frequency count vector. + ;; For each stop point, the counter is incremented each time it is visited. + (put name 'edebug-freq-count + (make-vector (length edebug-offset-list) 0))) + + +(defun edebug-clear-coverage (name) + ;; Create initial coverage vector. + ;; Only need one per expression, but it is simpler to use stop points. + (put name 'edebug-coverage + (make-vector (length edebug-offset-list) 'unknown))) + + +(defun edebug-form (cursor) + ;; Return the instrumented form for the following form. + ;; Add the point offsets to the edebug-offset-list for the form. + (let* ((form (edebug-top-element-required cursor "Expected form")) + (offset (edebug-top-offset cursor))) + (prog1 + (cond + ((consp form) + ;; The first offset for a list form is for the list form itself. + (if (eq 'quote (car form)) + form + (let* ((head (car form)) + (spec (and (symbolp head) (get-edebug-spec head))) + (new-cursor (edebug-new-cursor form offset))) + ;; Find out if this is a defining form from first symbol. + ;; An indirect spec would not work here, yet. + (if (and (consp spec) (eq '&define (car spec))) + (edebug-defining-form + new-cursor + (car offset);; before the form + (edebug-after-offset cursor) + (cons (symbol-name head) (cdr spec))) + ;; Wrap a regular form. + (edebug-make-before-and-after-form + (edebug-inc-offset (car offset)) + (edebug-list-form new-cursor) + ;; After processing the list form, the new-cursor is left + ;; with the offset after the form. + (edebug-inc-offset (edebug-cursor-offsets new-cursor)))) + ))) + + ((symbolp form) + (cond + ;; Check for constant symbols that dont get wrapped. + ((or (memq form '(t nil)) + (and (fboundp 'edebug-keywordp) (edebug-keywordp form))) + form) + + ;; This option may go away. + (edebug-stop-before-symbols + (edebug-make-before-and-after-form + (edebug-inc-offset (car offset)) + form + (edebug-inc-offset (cdr offset)) + )) + + (t ;; just a variable + (edebug-make-after-form form (edebug-inc-offset (cdr offset)))))) + + ;; Anything else is self-evaluating. + (t form)) + (edebug-move-cursor cursor)))) + + +(defsubst edebug-forms (cursor) (edebug-match cursor '(&rest form))) +(defsubst edebug-sexps (cursor) (edebug-match cursor '(&rest sexp))) + +(defsubst edebug-list-form-args (head cursor) + ;; Process the arguments of a list form given that head of form is a symbol. + ;; Helper for edebug-list-form + (let ((spec (get-edebug-spec head))) + (cond + (spec + (cond + ((consp spec) + ;; It is a speclist. + (let (edebug-best-error + edebug-error-point);; This may not be needed. + (edebug-match-sublist cursor spec))) + ((eq t spec) (edebug-forms cursor)) + ((eq 0 spec) (edebug-sexps cursor)) + ((symbolp spec) (funcall spec cursor));; Not used by edebug, + ; but leave it in for compatibility. + )) + ;; No edebug-form-spec provided. + ((edebug-macrop head) + (if edebug-eval-macro-args + (edebug-forms cursor) + (edebug-sexps cursor))) + (t ;; Otherwise it is a function call. + (edebug-forms cursor))))) + + +(defun edebug-list-form (cursor) + ;; Return an instrumented form built from the list form. + ;; The after offset will be left in the cursor after processing the form. + (let ((head (edebug-top-element-required cursor "Expected elements")) + ;; Prevent backtracking whenever instrumenting. + (edebug-gate t) + ;; A list form is never optional because it matches anything. + (edebug-&optional nil) + (edebug-&rest nil)) + ;; Skip the first offset. + (edebug-set-cursor cursor (edebug-cursor-expressions cursor) + (cdr (edebug-cursor-offsets cursor))) + (cond + ((null head) nil) ; () is legal. + + ((symbolp head) + (cond + ((null head) + (edebug-syntax-error "nil head")) + ((eq head 'interactive-p) + ;; Special case: replace (interactive-p) with variable + (setq edebug-def-interactive 'check-it) + (edebug-move-cursor cursor) + (edebug-interactive-p-name)) + (t + (cons head (edebug-list-form-args + head (edebug-move-cursor cursor)))))) + + ((consp head) + (if (and (listp head) (eq (car head) ',)) + (edebug-match cursor '(("," def-form) body)) + ;; Process anonymous function and args. + ;; This assumes no anonymous macros. + (edebug-match-specs cursor '(lambda-expr body) 'edebug-match-specs))) + + (t (edebug-syntax-error + "Head of list form must be a symbol or lambda expression."))) + )) + + +;;;; Matching of specs. +;;; =================== + +(defvar edebug-after-dotted-spec nil) + +(defvar edebug-matching-depth 0) ;; initial value +(defconst edebug-max-depth 150) ;; maximum number of matching recursions. + + +;;;; Failure to match +;;; ================== +;; This throws to no-match, if there are higher alternatives. +;; Otherwise it signals an error. The place of the error is found +;; with the two before- and after-offset functions. + +(defun edebug-no-match (cursor &rest edebug-args) + ;; Throw a no-match, or signal an error immediately if gate is active. + ;; Remember this point in case we need to report this error. + (setq edebug-error-point (or edebug-error-point + (edebug-before-offset cursor)) + edebug-best-error (or edebug-best-error edebug-args)) + (if (and edebug-gate (not edebug-&optional)) + (progn + (if edebug-error-point + (goto-char edebug-error-point)) + (apply 'edebug-syntax-error edebug-args)) + (funcall 'throw 'no-match edebug-args))) + + +(defun edebug-match (cursor specs) + ;; Top level spec matching function. + ;; Used also at each lower level of specs. + (let (edebug-&optional + edebug-&rest + edebug-best-error + edebug-error-point + (edebug-gate edebug-gate) ;; locally bound to limit effect + ) + (edebug-match-specs cursor specs 'edebug-match-specs))) + + +(defun edebug-match-one-spec (cursor spec) + ;; Match one spec, which is not a keyword &-spec. + (cond + ((symbolp spec) (edebug-match-symbol cursor spec)) + ((vectorp spec) (edebug-match cursor (append spec nil))) + ((stringp spec) (edebug-match-string cursor spec)) + ((listp spec) (edebug-match-list cursor spec)) + )) + + +(defun edebug-match-specs (cursor specs remainder-handler) + ;; Append results of matching the list of specs. + ;; The first spec is handled and the remainder-handler handles the rest. + (let ((edebug-matching-depth + (if (> edebug-matching-depth edebug-max-depth) + (error "too deep - perhaps infinite loop in spec?") + (1+ edebug-matching-depth)))) + (cond + ((null specs) nil) + + ;; Is the spec dotted? + ((atom specs) + (let ((edebug-dotted-spec t));; Containing spec list was dotted. + (edebug-match-specs cursor (list specs) remainder-handler))) + + ;; Is the form dotted? + ((not (listp (edebug-cursor-expressions cursor)));; allow nil + (if (not edebug-dotted-spec) + (edebug-no-match cursor "Dotted spec required.")) + ;; Cancel dotted spec and dotted form. + (let ((edebug-dotted-spec) + (this-form (edebug-cursor-expressions cursor)) + (this-offset (edebug-cursor-offsets cursor))) + ;; Wrap the form in a list, (by changing the cursor??)... + (edebug-set-cursor cursor (list this-form) this-offset) + ;; and process normally, then unwrap the result. + (car (edebug-match-specs cursor specs remainder-handler)))) + + (t;; Process normally. + (let* ((spec (car specs)) + (rest) + (first-char (and (symbolp spec) (aref (symbol-name spec) 0)))) + ;;(message "spec = %s first char = %s" spec first-char) (sit-for 1) + (nconc + (cond + ((eq ?& first-char);; "&" symbols take all following specs. + (funcall (get-edebug-spec spec) cursor (cdr specs))) + ((eq ?: first-char);; ":" symbols take one following spec. + (setq rest (cdr (cdr specs))) + (funcall (get-edebug-spec spec) cursor (car (cdr specs)))) + (t;; Any other normal spec. + (setq rest (cdr specs)) + (edebug-match-one-spec cursor spec))) + (funcall remainder-handler cursor rest remainder-handler))))))) + + +;; Define specs for all the symbol specs with functions used to process them. +;; Perhaps we shouldnt be doing this with edebug-form-specs since the +;; user may want to define macros or functions with the same names. +;; We could use an internal obarray for these primitive specs. + +(mapcar + (function (lambda (pair) + (put (car pair) 'edebug-form-spec (cdr pair)))) + '((&optional . edebug-match-&optional) + (&rest . edebug-match-&rest) + (&or . edebug-match-&or) + (form . edebug-match-form) + (sexp . edebug-match-sexp) + (body . edebug-match-body) + (&define . edebug-match-&define) + (name . edebug-match-name) + (:name . edebug-match-colon-name) + (arg . edebug-match-arg) + (def-body . edebug-match-def-body) + (def-form . edebug-match-def-form) + ;; Less frequently used: + ;; (function . edebug-match-function) + (lambda-expr . edebug-match-lambda-expr) + (¬ . edebug-match-¬) + (&key . edebug-match-&key) + (place . edebug-match-place) + (gate . edebug-match-gate) + ;; (nil . edebug-match-nil) not this one - special case it. + )) + +(defun edebug-match-symbol (cursor symbol) + ;; Match a symbol spec. + (let* ((spec (get-edebug-spec symbol))) + (cond + (spec + (if (consp spec) + ;; It is an indirect spec. + (edebug-match cursor spec) + ;; Otherwise it should be the symbol name of a function. + ;; There could be a bug here - maybe need to do edebug-match bindings. + (funcall spec cursor))) + + ((null symbol) ;; special case this. + (edebug-match-nil cursor)) + + ((fboundp symbol) ; is it a predicate? + (let ((sexp (edebug-top-element-required cursor "Expected" symbol))) + ;; Special case for edebug-`. + (if (and (listp sexp) (eq (car sexp) ',)) + (edebug-match cursor '(("," def-form))) + (if (not (funcall symbol sexp)) + (edebug-no-match cursor symbol "failed")) + (edebug-move-cursor cursor) + (list sexp)))) + (t (error "%s is not a form-spec or function" symbol)) + ))) + + +(defun edebug-match-sexp (cursor) + (list (prog1 (edebug-top-element-required cursor "Expected sexp") + (edebug-move-cursor cursor)))) + +(defun edebug-match-form (cursor) + (list (edebug-form cursor))) + +(defalias 'edebug-match-place 'edebug-match-form) + ;; Currently identical to edebug-match-form. + ;; This is for common lisp setf-style place arguments. + +(defsubst edebug-match-body (cursor) (edebug-forms cursor)) + +(defun edebug-match-&optional (cursor specs) + ;; Keep matching until one spec fails. + (edebug-&optional-wrapper cursor specs 'edebug-&optional-wrapper)) + +(defun edebug-&optional-wrapper (cursor specs remainder-handler) + (let (result + (edebug-&optional specs) + (edebug-gate nil) + (this-form (edebug-cursor-expressions cursor)) + (this-offset (edebug-cursor-offsets cursor))) + (if (null (catch 'no-match + (setq result + (edebug-match-specs cursor specs remainder-handler)) + ;; Returning nil means no no-match was thrown. + nil)) + result + ;; no-match, but don't fail; just reset cursor and return nil. + (edebug-set-cursor cursor this-form this-offset) + nil))) + + +(defun edebug-&rest-wrapper (cursor specs remainder-handler) + (if (null specs) (setq specs edebug-&rest)) + ;; Reuse the &optional handler with this as the remainder handler. + (edebug-&optional-wrapper cursor specs remainder-handler)) + +(defun edebug-match-&rest (cursor specs) + ;; Repeatedly use specs until failure. + (let ((edebug-&rest specs) ;; remember these + edebug-best-error + edebug-error-point) + (edebug-&rest-wrapper cursor specs 'edebug-&rest-wrapper))) + + +(defun edebug-match-&or (cursor specs) + ;; Keep matching until one spec succeeds, and return its results. + ;; If none match, fail. + ;; This needs to be optimized since most specs spend time here. + (let ((original-specs specs) + (this-form (edebug-cursor-expressions cursor)) + (this-offset (edebug-cursor-offsets cursor))) + (catch 'matched + (while specs + (catch 'no-match + (throw 'matched + (let (edebug-gate ;; only while matching each spec + edebug-best-error + edebug-error-point) + ;; Doesnt support e.g. &or symbolp &rest form + (edebug-match-one-spec cursor (car specs))))) + ;; Match failed, so reset and try again. + (setq specs (cdr specs)) + ;; Reset the cursor for the next match. + (edebug-set-cursor cursor this-form this-offset)) + ;; All failed. + (apply 'edebug-no-match cursor "Expected one of" original-specs)) + )) + + +(defun edebug-match-¬ (cursor specs) + ;; If any specs match, then fail + (if (null (catch 'no-match + (let ((edebug-gate nil)) + (save-excursion + (edebug-match-&or cursor specs))) + nil)) + ;; This means something matched, so it is a no match. + (edebug-no-match cursor "Unexpected")) + ;; This means nothing matched, so it is OK. + nil) ;; So, return nothing + + +(def-edebug-spec &key edebug-match-&key) + +(defun edebug-match-&key (cursor specs) + ;; Following specs must look like (<name> <spec>) ... + ;; where <name> is the name of a keyword, and spec is its spec. + ;; This really doesnt save much over the expanded form and takes time. + (edebug-match-&rest + cursor + (cons '&or + (mapcar (function (lambda (pair) + (vector (format ":%s" (car pair)) + (car (cdr pair))))) + specs)))) + + +(defun edebug-match-gate (cursor) + ;; Simply set the gate to prevent backtracking at this level. + (setq edebug-gate t) + nil) + + +(defun edebug-match-list (cursor specs) + ;; The spec is a list, but what kind of list, and what context? + (if edebug-dotted-spec + ;; After dotted spec but form did not contain dot, + ;; so match list spec elements as if spliced in. + (prog1 + (let ((edebug-dotted-spec)) + (edebug-match-specs cursor specs 'edebug-match-specs)) + ;; If it matched, really clear the dotted-spec flag. + (setq edebug-dotted-spec nil)) + (let ((spec (car specs)) + (form (edebug-top-element-required cursor "Expected" specs))) + (cond + ((eq 'quote spec) + (let ((spec (car (cdr specs)))) + (cond + ((symbolp spec) + ;; Special case: spec quotes a symbol to match. + ;; Change in future. Use "..." instead. + (if (not (eq spec form)) + (edebug-no-match cursor "Expected" spec)) + (edebug-move-cursor cursor) + (setq edebug-gate t) + form) + (t + (error "Bad spec: %s" specs))))) + + ((listp form) + (prog1 + (list (edebug-match-sublist + ;; First offset is for the list form itself. + ;; Treat nil as empty list. + (edebug-new-cursor form (cdr (edebug-top-offset cursor))) + specs)) + (edebug-move-cursor cursor))) + + ((and (eq 'vector spec) (vectorp form)) + ;; Special case: match a vector with the specs. + (let ((result (edebug-match-sublist + (edebug-new-cursor + form (cdr (edebug-top-offset cursor))) + (cdr specs)))) + (edebug-move-cursor cursor) + (list (apply 'vector result)))) + + (t (edebug-no-match cursor "Expected" specs))) + ))) + + +(defun edebug-match-sublist (cursor specs) + ;; Match a sublist of specs. + (let (edebug-&optional + ;;edebug-best-error + ;;edebug-error-point + ) + (prog1 + ;; match with edebug-match-specs so edebug-best-error is not bound. + (edebug-match-specs cursor specs 'edebug-match-specs) + (if (not (edebug-empty-cursor cursor)) + (if edebug-best-error + (apply 'edebug-no-match cursor edebug-best-error) + ;; A failed &rest or &optional spec may leave some args. + (edebug-no-match cursor "Failed matching" specs) + ))))) + + +(defun edebug-match-string (cursor spec) + (let ((sexp (edebug-top-element-required cursor "Expected" spec))) + (if (not (eq (intern spec) sexp)) + (edebug-no-match cursor "Expected" spec) + ;; Since it matched, failure means immediate error, unless &optional. + (setq edebug-gate t) + (edebug-move-cursor cursor) + (list sexp) + ))) + +(defun edebug-match-nil (cursor) + ;; There must be nothing left to match a nil. + (if (not (edebug-empty-cursor cursor)) + (edebug-no-match cursor "Unmatched argument(s)") + nil)) + + +(defun edebug-match-function (cursor) + (error "Use function-form instead of function in edebug spec")) + +(defun edebug-match-&define (cursor specs) + ;; Match a defining form. + ;; Normally, &define is interpretted specially other places. + ;; This should only be called inside of a spec list to match the remainder + ;; of the current list. e.g. ("lambda" &define args def-body) + (edebug-make-form-wrapper + cursor + (edebug-before-offset cursor) + ;; Find the last offset in the list. + (let ((offsets (edebug-cursor-offsets cursor))) + (while (consp offsets) (setq offsets (cdr offsets))) + offsets) + specs)) + +(defun edebug-match-lambda-expr (cursor) + ;; The expression must be a function. + ;; This will match any list form that begins with a symbol + ;; that has an edebug-form-spec beginning with &define. In + ;; practice, only lambda expressions should be used. + ;; I could add a &lambda specification to avoid confusion. + (let* ((sexp (edebug-top-element-required + cursor "Expected lambda expression")) + (offset (edebug-top-offset cursor)) + (head (and (consp sexp) (car sexp))) + (spec (and (symbolp head) (get-edebug-spec head))) + (edebug-inside-func nil)) + ;; Find out if this is a defining form from first symbol. + (if (and (consp spec) (eq '&define (car spec))) + (prog1 + (list + (edebug-defining-form + (edebug-new-cursor sexp offset) + (car offset);; before the sexp + (edebug-after-offset cursor) + (cons (symbol-name head) (cdr spec)))) + (edebug-move-cursor cursor)) + (edebug-no-match cursor "Expected lambda expression") + ))) + + +(defun edebug-match-name (cursor) + ;; Set the edebug-def-name bound in edebug-defining-form. + (let ((name (edebug-top-element-required cursor "Expected name"))) + ;; Maybe strings and numbers could be used. + (if (not (symbolp name)) + (edebug-no-match cursor "Symbol expected for name of definition")) + (setq edebug-def-name + (if edebug-def-name + ;; Construct a new name by appending to previous name. + (intern (format "%s@%s" edebug-def-name name)) + name)) + (edebug-move-cursor cursor) + (list name))) + +(defun edebug-match-colon-name (cursor spec) + ;; Set the edebug-def-name to the spec. + (setq edebug-def-name + (if edebug-def-name + ;; Construct a new name by appending to previous name. + (intern (format "%s@%s" edebug-def-name spec)) + spec)) + nil) + +(defun edebug-match-arg (cursor) + ;; set the def-args bound in edebug-defining-form + (let ((edebug-arg (edebug-top-element-required cursor "Expected arg"))) + (if (or (not (symbolp edebug-arg)) + (edebug-lambda-list-keywordp edebug-arg)) + (edebug-no-match cursor "Bad argument:" edebug-arg)) + (edebug-move-cursor cursor) + (setq edebug-def-args (cons edebug-arg edebug-def-args)) + (list edebug-arg))) + +(defun edebug-match-def-form (cursor) + ;; Like form but the form is wrapped in edebug-enter form. + ;; The form is assumed to be executing outside of the function context. + ;; This is a hack for now, since a def-form might execute inside as well. + ;; Not to be used otherwise. + (let ((edebug-inside-func nil)) + (list (edebug-make-enter-wrapper (list (edebug-form cursor)))))) + +(defun edebug-match-def-body (cursor) + ;; Like body but body is wrapped in edebug-enter form. + ;; The body is assumed to be executing inside of the function context. + ;; Not to be used otherwise. + (let ((edebug-inside-func t)) + (list (edebug-wrap-def-body (edebug-forms cursor))))) + + +;;;; Edebug Form Specs +;;; ========================================================== +;;; See cl-specs.el for common lisp specs. + +;;;;* Spec for def-edebug-spec +;;; Out of date. + +(defun edebug-spec-p (object) + "Return non-nil if OBJECT is a symbol with an edebug-form-spec property." + (and (symbolp object) + (get object 'edebug-form-spec))) + +(def-edebug-spec def-edebug-spec + ;; Top level is different from lower levels. + (&define :name edebug-spec name + &or "nil" edebug-spec-p "t" "0" (&rest edebug-spec))) + +(def-edebug-spec edebug-spec-list + ;; A list must have something in it, or it is nil, a symbolp + ((edebug-spec . [&or nil edebug-spec]))) + +(def-edebug-spec edebug-spec + (&or + (vector &rest edebug-spec) ; matches a vector + ("vector" &rest edebug-spec) ; matches a vector spec + ("quote" symbolp) + edebug-spec-list + stringp + [edebug-lambda-list-keywordp &rest edebug-spec] + ;; [edebug-keywordp gate edebug-spec] ;; need edebug-keywordp for this. + edebug-spec-p ;; Including all the special ones e.g. form. + symbolp;; a predicate + )) + + +;;;;* Emacs special forms and some functions. + +;; quote expects only one argument, although it allows any number. +(def-edebug-spec quote sexp) + +;; The standard defining forms. +(def-edebug-spec defconst defvar) +(def-edebug-spec defvar (symbolp &optional form stringp)) + +(def-edebug-spec defun + (&define name lambda-list + [&optional stringp] + [&optional ("interactive" interactive)] + def-body)) +(def-edebug-spec defmacro + (&define name lambda-list def-body)) + +(def-edebug-spec arglist lambda-list) ;; denegrated - use lambda-list. + +(def-edebug-spec lambda-list + (([&rest arg] + [&optional ["&optional" arg &rest arg]] + &optional ["&rest" arg] + ))) + +(def-edebug-spec interactive + (&optional &or stringp def-form)) + +;; A function-form is for an argument that may be a function or a form. +;; This specially recognizes anonymous functions quoted with quote. +(def-edebug-spec function-form + ;; form at the end could also handle "function", + ;; but recognize it specially to avoid wrapping function forms. + (&or ([&or "quote" "function"] &or symbolp lambda-expr) form)) + +;; function expects a symbol or a lambda or macro expression +;; A macro is allowed by Emacs. +(def-edebug-spec function (&or symbolp lambda-expr)) + +;; lambda is a macro in emacs 19. +(def-edebug-spec lambda (&define lambda-list + [&optional stringp] + [&optional ("interactive" interactive)] + def-body)) + +;; A macro expression is a lambda expression with "macro" prepended. +(def-edebug-spec macro (&define "lambda" lambda-list def-body)) + +;; (def-edebug-spec anonymous-form ((&or ["lambda" lambda] ["macro" macro]))) + +;; Standard functions that take function-forms arguments. +(def-edebug-spec mapcar (function-form form)) +(def-edebug-spec mapconcat (function-form form form)) +(def-edebug-spec mapatoms (function-form &optional form)) +(def-edebug-spec apply (function-form &rest form)) +(def-edebug-spec funcall (function-form &rest form)) + +(def-edebug-spec let + ((&rest &or (symbolp &optional form) symbolp) + body)) + +(def-edebug-spec let* let) + +(def-edebug-spec setq (&rest symbolp form)) +(def-edebug-spec setq-default setq) + +(def-edebug-spec cond (&rest (&rest form))) + +(def-edebug-spec condition-case + (symbolp + form + &rest (symbolp body))) + + +(def-edebug-spec \` (backquote-form)) + +;; Supports quotes inside backquotes, +;; but only at the top level inside unquotes. +(def-edebug-spec backquote-form + (&or + ([&or "," ",@"] &or ("quote" backquote-form) form) + (backquote-form &rest backquote-form) + ;; If you use dotted forms in backquotes, replace the previous line + ;; with the following. This takes quite a bit more stack space, however. + ;; (backquote-form . [&or nil backquote-form]) + (vector &rest backquote-form) + sexp)) + +;; Special version of backquote that instruments backquoted forms +;; destined to be evaluated, usually as the result of a +;; macroexpansion. Backquoted code can only have unquotes (, and ,@) +;; in places where list forms are allowed, and predicates. If the +;; backquote is used in a macro, unquoted code that come from +;; arguments must be instrumented, if at all, with def-form not def-body. + +;; We could assume that all forms (not nested in other forms) +;; in arguments of macros should be def-forms, whether or not the macros +;; are defined with edebug-` but this would be expensive. + +;; ,@ might have some problems. + +(defalias 'edebug-\` '\`) ;; same macro as regular backquote. +(def-edebug-spec edebug-\` (def-form)) + +;; Assume immediate quote in unquotes mean backquote at next higher level. +(def-edebug-spec , (&or ("quote" edebug-`) def-form)) +(def-edebug-spec ,@ (&define ;; so (,@ form) is never wrapped. + &or ("quote" edebug-`) def-form)) + +;; New byte compiler. +(def-edebug-spec defsubst defun) +(def-edebug-spec dont-compile t) +(def-edebug-spec eval-when-compile t) +(def-edebug-spec eval-and-compile t) + +;; Anything else? + + +;;==================== +;; Some miscellaneous specs for macros in public packages. +;; Send me yours. + +;; advice.el by Hans Chalupsky (hans@cs.buffalo.edu) + +(def-edebug-spec ad-dolist ((symbolp form &optional form) body)) +(def-edebug-spec defadvice + (&define name ;; thing being advised. + (name ;; class is [&or "before" "around" "after" + ;; "activation" "deactivation"] + name ;; name of advice + &rest sexp ;; optional position and flags + ) + [&optional stringp] + [&optional ("interactive" interactive)] + def-body)) + + +;;;; The debugger itself +;;; =============================== + +(defvar edebug-active nil) ;; Non-nil when edebug is active + +;;; add minor-mode-alist entry +(or (assq 'edebug-active minor-mode-alist) + (setq minor-mode-alist (cons (list 'edebug-active " *Debugging*") + minor-mode-alist))) + +(defvar edebug-stack nil) +;; Stack of active functions evaluated via edebug. +;; Should be nil at the top level. + +(defvar edebug-stack-depth -1) +;; Index of last edebug-stack item. + +(defvar edebug-offset-indices nil) +;; Stack of offset indices of visited edebug sexps. +;; Should be nil at the top level. +;; Each function adds one cons. Top is modified with setcar. + + +(defvar edebug-entered nil + ;; Non-nil if edebug has already been entered at this recursive edit level. + ;; This should stay nil at the top level. + ) + +;; Should these be options? +(defconst edebug-debugger 'edebug + ;; Name of function to use for debugging when error or quit occurs. + ;; Set this to 'debug if you want to debug edebug. + ) + + +;; Dynamically bound variables, declared globally but left unbound. +(defvar edebug-function) ; the function being executed. change name!! +(defvar edebug-args) ; the arguments of the function +(defvar edebug-data) ; the edebug data for the function +(defvar edebug-value) ; the result of the expression +(defvar edebug-after-index) +(defvar edebug-def-mark) ; the mark for the definition +(defvar edebug-freq-count) ; the count of expression visits. +(defvar edebug-coverage) ; the coverage results of each expression of function. + +(defvar edebug-buffer) ; which buffer the function is in. +(defvar edebug-result) ; the result of the function call returned by body +(defvar edebug-outside-executing-macro) +(defvar edebug-outside-defining-kbd-macro) + +(defvar edebug-execution-mode 'step) ; Current edebug mode set by user. +(defvar edebug-next-execution-mode nil) ; Use once instead of initial mode. + +(defvar edebug-outside-debug-on-error) ; the value of debug-on-error outside +(defvar edebug-outside-debug-on-quit) ; the value of debug-on-quit outside + +(defvar edebug-outside-pre-command-hook) +(defvar edebug-outside-post-command-hook) +(defvar edebug-outside-post-command-idle-hook) + +;; Emacs 19 +(defvar pre-command-hook nil) +(defvar post-command-hook nil) +(defvar post-command-idle-hook nil) + +(defvar cl-lexical-debug) ;; Defined in cl.el + +;;; Handling signals +;;; ================= + +(if (not (fboundp 'edebug-original-signal)) + (defalias 'edebug-original-signal (symbol-function 'signal))) +;; We should use advise for this!! + +(defun edebug-signal (edebug-signal-name edebug-signal-data) + "Signal an error. Args are SIGNAL-NAME, and associated DATA. +A signal name is a symbol with an `error-conditions' property +that is a list of condition names. +A handler for any of those names will get to handle this signal. +The symbol `error' should always be one of them. + +DATA should be a list. Its elements are printed as part of the error message. +If the signal is handled, DATA is made available to the handler. +See `condition-case'. + +This is the Edebug replacement for the standard `signal'. It should +only be active while Edebug is. It checks `debug-on-error' to see +whether it should call the debugger. When execution is resumed, the +error is signaled again." + (if (and (listp debug-on-error) (memq edebug-signal-name debug-on-error)) + (edebug 'error (cons edebug-signal-name edebug-signal-data))) + ;; If we reach here without another non-local exit, then send signal again. + ;; i.e. the signal is not continuable, yet. + (edebug-original-signal edebug-signal-name edebug-signal-data)) + + +;;; Entering Edebug +;;; ================== + +(defun edebug-enter (edebug-function edebug-args edebug-body) + ;; Entering FUNC. The arguments are ARGS, and the body is BODY. + ;; Setup edebug variables and evaluate BODY. This function is called + ;; when a function evaluated with edebug-eval-top-level-form is entered. + ;; Return the result of BODY. + + ;; Is this the first time we are entering edebug since + ;; lower-level recursive-edit command? + ;; More precisely, this tests whether Edebug is currently active. + (if (not edebug-entered) + (let ((edebug-entered t) + ;; Binding max-lisp-eval-depth here is OK, + ;; but not inside an unwind-protect. + ;; Doing it here also keeps it from growing too large. + (max-lisp-eval-depth (+ 100 max-lisp-eval-depth)) ; too much?? + (max-specpdl-size (+ 200 max-specpdl-size)) + + (debugger edebug-debugger) ; only while edebug is active. + (edebug-outside-debug-on-error debug-on-error) + (edebug-outside-debug-on-quit debug-on-quit) + ;; Binding these may not be the right thing to do. + ;; We want to allow the global values to be changed. + (debug-on-error (or debug-on-error edebug-on-error)) + (debug-on-quit edebug-on-quit) + + ;; Lexical bindings must be uncompiled for this to work. + (cl-lexical-debug t) + + ;; Save the outside value of executing macro. (here??) + (edebug-outside-executing-macro executing-macro) + (edebug-outside-pre-command-hook pre-command-hook) + (edebug-outside-post-command-hook post-command-hook) + (edebug-outside-post-command-idle-hook post-command-idle-hook)) + (unwind-protect + (let (;; Don't keep reading from an executing kbd macro + ;; within edebug unless edebug-continue-kbd-macro is + ;; non-nil. Again, local binding may not be best. + (executing-macro + (if edebug-continue-kbd-macro executing-macro)) + + ;; Disable command hooks. This is essential when + ;; a hook function is instrumented - to avoid infinite loop. + ;; This may be more than we need, however. + (pre-command-hook nil) + (post-command-hook nil) + (post-command-idle-hook nil)) + (setq edebug-execution-mode (or edebug-next-execution-mode + edebug-initial-mode + edebug-execution-mode) + edebug-next-execution-mode nil) + ;; Bind signal to edebug-signal only while Edebug is active. + (fset 'signal 'edebug-signal) + (unwind-protect + (edebug-enter edebug-function edebug-args edebug-body) + (fset 'signal (symbol-function 'edebug-original-signal)))) + ;; Reset global variables in case outside value was changed. + (setq executing-macro edebug-outside-executing-macro + pre-command-hook edebug-outside-pre-command-hook + post-command-hook edebug-outside-post-command-hook + post-command-idle-hook edebug-outside-post-command-idle-hook + ))) + + (let* ((edebug-data (get edebug-function 'edebug)) + (edebug-def-mark (car edebug-data)) ; mark at def start + (edebug-freq-count (get edebug-function 'edebug-freq-count)) + (edebug-coverage (get edebug-function 'edebug-coverage)) + (edebug-buffer (marker-buffer edebug-def-mark)) + + (edebug-stack (cons edebug-function edebug-stack)) + (edebug-offset-indices (cons 0 edebug-offset-indices)) + ) + (if (get edebug-function 'edebug-on-entry) + (progn + (setq edebug-execution-mode 'step) + (if (eq (get edebug-function 'edebug-on-entry) 'temp) + (put edebug-function 'edebug-on-entry nil)))) + (if edebug-trace + (edebug-enter-trace edebug-body) + (funcall edebug-body)) + ))) + + +(defun edebug-enter-trace (edebug-body) + (let ((edebug-stack-depth (1+ edebug-stack-depth)) + edebug-result) + (edebug-print-trace-before + (format "%s args: %s" edebug-function edebug-args)) + (prog1 (setq edebug-result (funcall edebug-body)) + (edebug-print-trace-after + (format "%s result: %s" edebug-function edebug-result))))) + +(def-edebug-spec edebug-tracing (form body)) + +(defmacro edebug-tracing (msg &rest body) + "Print MSG in *edebug-trace* before and after evaluating BODY. +The result of BODY is also printed." + (` (let ((edebug-stack-depth (1+ edebug-stack-depth)) + edebug-result) + (edebug-print-trace-before (, msg)) + (prog1 (setq edebug-result (progn (,@ body))) + (edebug-print-trace-after + (format "%s result: %s" (, msg) edebug-result)))))) + +(defun edebug-print-trace-before (msg) + "Function called to print trace info before expression evaluation. +MSG is printed after `::::{ '." + (edebug-trace-display + edebug-trace-buffer "%s{ %s" (make-string edebug-stack-depth ?\:) msg)) + +(defun edebug-print-trace-after (msg) + "Function called to print trace info after expression evaluation. +MSG is printed after `::::} '." + (edebug-trace-display + edebug-trace-buffer "%s} %s" (make-string edebug-stack-depth ?\:) msg)) + + + +(defun edebug-slow-before (edebug-before-index) + ;; Debug current function given BEFORE position. + ;; Called from functions compiled with edebug-eval-top-level-form. + ;; Return the before index. + (setcar edebug-offset-indices edebug-before-index) + + ;; Increment frequency count + (aset edebug-freq-count edebug-before-index + (1+ (aref edebug-freq-count edebug-before-index))) + + (if (or (not (memq edebug-execution-mode '(Go-nonstop next))) + (edebug-input-pending-p)) + (edebug-debugger edebug-before-index 'before nil)) + edebug-before-index) + +(defun edebug-fast-before (edebug-before-index) + ;; Do nothing. + ) + +(defun edebug-slow-after (edebug-before-index edebug-after-index edebug-value) + ;; Debug current function given AFTER position and VALUE. + ;; Called from functions compiled with edebug-eval-top-level-form. + ;; Return VALUE. + (setcar edebug-offset-indices edebug-after-index) + + ;; Increment frequency count + (aset edebug-freq-count edebug-after-index + (1+ (aref edebug-freq-count edebug-after-index))) + (if edebug-test-coverage (edebug-update-coverage)) + + (if (and (eq edebug-execution-mode 'Go-nonstop) + (not (edebug-input-pending-p))) + ;; Just return result. + edebug-value + (edebug-debugger edebug-after-index 'after edebug-value) + )) + +(defun edebug-fast-after (edebug-before-index edebug-after-index edebug-value) + ;; Do nothing but return the value. + edebug-value) + +(defun edebug-run-slow () + (defalias 'edebug-before 'edebug-slow-before) + (defalias 'edebug-after 'edebug-slow-after)) + +;; This is not used, yet. +(defun edebug-run-fast () + (defalias 'edebug-before 'edebug-fast-before) + (defalias 'edebug-after 'edebug-fast-after)) + +(edebug-run-slow) + + +(defun edebug-update-coverage () + (let ((old-result (aref edebug-coverage edebug-after-index))) + (cond + ((eq 'ok-coverage old-result)) + ((eq 'unknown old-result) + (aset edebug-coverage edebug-after-index edebug-value)) + ;; Test if a different result. + ((not (eq edebug-value old-result)) + (aset edebug-coverage edebug-after-index 'ok-coverage))))) + + +;; Dynamically declared unbound variables. +(defvar edebug-arg-mode) ; the mode, either before, after, or error +(defvar edebug-breakpoints) +(defvar edebug-break-data) ; break data for current function. +(defvar edebug-break) ; whether a break occurred. +(defvar edebug-global-break) ; whether a global break occurred. +(defvar edebug-break-condition) ; whether the breakpoint is conditional. + +(defvar edebug-break-result nil) +(defvar edebug-global-break-result nil) + + +(defun edebug-debugger (edebug-offset-index edebug-arg-mode edebug-value) + ;; Check breakpoints and pending input. + ;; If edebug display should be updated, call edebug-display. + ;; Return edebug-value. + (let* (;; This needs to be here since breakpoints may be changed. + (edebug-breakpoints (car (cdr edebug-data))) ; list of breakpoints + (edebug-break-data (assq edebug-offset-index edebug-breakpoints)) + (edebug-break-condition (car (cdr edebug-break-data))) + (edebug-global-break + (if edebug-global-break-condition + (condition-case nil + (setq edebug-global-break-result + (eval edebug-global-break-condition)) + (error nil)))) + (edebug-break)) + +;;; (edebug-trace "exp: %s" edebug-value) + ;; Test whether we should break. + (setq edebug-break + (or edebug-global-break + (and edebug-break-data + (or (not edebug-break-condition) + (setq edebug-break-result + (eval edebug-break-condition)))))) + (if (and edebug-break + (nth 2 edebug-break-data)) ; is it temporary? + ;; Delete the breakpoint. + (setcdr edebug-data + (cons (delq edebug-break-data edebug-breakpoints) + (cdr (cdr edebug-data))))) + + ;; Display if mode is not go, continue, or Continue-fast + ;; or break, or input is pending, + (if (or (not (memq edebug-execution-mode '(go continue Continue-fast))) + edebug-break + (edebug-input-pending-p)) + (edebug-display)) ; <--------------- display + + edebug-value + )) + + +;; window-start now stored with each function. +;;(defvar edebug-window-start nil) +;; Remember where each buffers' window starts between edebug calls. +;; This is to avoid spurious recentering. +;; Does this still need to be buffer-local?? +;;(setq-default edebug-window-start nil) +;;(make-variable-buffer-local 'edebug-window-start) + + +;; Dynamically declared unbound vars +(defvar edebug-point) ; the point in edebug buffer +(defvar edebug-outside-buffer) ; the current-buffer outside of edebug +(defvar edebug-outside-point) ; the point outside of edebug +(defvar edebug-outside-mark) ; the mark outside of edebug +(defvar edebug-window-data) ; window and window-start for current function +(defvar edebug-outside-windows) ; outside window configuration +(defvar edebug-eval-buffer) ; for the evaluation list. +(defvar edebug-outside-o-a-p) ; outside overlay-arrow-position +(defvar edebug-outside-o-a-s) ; outside overlay-arrow-string +(defvar edebug-outside-c-i-e-a) ; outside cursor-in-echo-area + +(defvar edebug-eval-list nil) ;; List of expressions to evaluate. + +(defvar edebug-previous-result nil) ;; Last result returned. + +;; Emacs 19 adds an arg to mark and mark-marker. +(defalias 'edebug-mark 'mark) +(defalias 'edebug-mark-marker 'mark-marker) + + +(defun edebug-display () + ;; Setup windows for edebug, determine mode, maybe enter recursive-edit. + ;; Uses local variables of edebug-enter, edebug-before, edebug-after + ;; and edebug-debugger. + (let ((edebug-active t) ; for minor mode alist + edebug-stop ; should we enter recursive-edit + (edebug-point (+ edebug-def-mark + (aref (nth 2 edebug-data) edebug-offset-index))) + edebug-buffer-outside-point ; current point in edebug-buffer + ;; window displaying edebug-buffer + (edebug-window-data (nth 3 edebug-data)) + (edebug-outside-window (selected-window)) + (edebug-outside-buffer (current-buffer)) + (edebug-outside-point (point)) + (edebug-outside-mark (edebug-mark)) + edebug-outside-windows ; window or screen configuration + edebug-buffer-points + + edebug-eval-buffer ; declared here so we can kill it below + (edebug-eval-result-list (and edebug-eval-list + (edebug-eval-result-list))) + edebug-trace-window + edebug-trace-window-start + + (edebug-outside-o-a-p overlay-arrow-position) + (edebug-outside-o-a-s overlay-arrow-string) + (edebug-outside-c-i-e-a cursor-in-echo-area)) + (unwind-protect + (let ((overlay-arrow-position overlay-arrow-position) + (overlay-arrow-string overlay-arrow-string) + (cursor-in-echo-area nil) + ;; any others?? + ) + (if (not (buffer-name edebug-buffer)) + (let ((debug-on-error nil)) + (error "Buffer defining %s not found" edebug-function))) + + (if (eq 'after edebug-arg-mode) + ;; Compute result string now before windows are modified. + (edebug-compute-previous-result edebug-value)) + + (if edebug-save-windows + ;; Save windows now before we modify them. + (setq edebug-outside-windows + (edebug-current-windows edebug-save-windows))) + + (if edebug-save-displayed-buffer-points + (setq edebug-buffer-points (edebug-get-displayed-buffer-points))) + + ;; First move the edebug buffer point to edebug-point + ;; so that window start doesnt get changed when we display it. + ;; I dont know if this is going to help. + ;;(set-buffer edebug-buffer) + ;;(goto-char edebug-point) + + ;; If edebug-buffer is not currently displayed, + ;; first find a window for it. + (edebug-pop-to-buffer edebug-buffer (car edebug-window-data)) + (setcar edebug-window-data (selected-window)) + + ;; Now display eval list, if any. + ;; This is done after the pop to edebug-buffer + ;; so that buffer-window correspondence is correct after quitting. + (edebug-eval-display edebug-eval-result-list) + ;; The evaluation list better not have deleted edebug-window-data. + (select-window (car edebug-window-data)) + (set-buffer edebug-buffer) + + (setq edebug-buffer-outside-point (point)) + (goto-char edebug-point) + + (if (eq 'before edebug-arg-mode) + ;; Check whether positions are up-to-date. + ;; This assumes point is never before symbol. + (if (not (memq (following-char) '(?\( ?\# ?\` ))) + (let ((debug-on-error nil)) + (error "Source has changed - reevaluate definition of %s" + edebug-function) + ))) + + (setcdr edebug-window-data + (edebug-adjust-window (cdr edebug-window-data))) + + ;; Test if there is input, not including keyboard macros. + (if (edebug-input-pending-p) + (progn + (setq edebug-execution-mode 'step + edebug-stop t) + (edebug-stop) + ;; (discard-input) ; is this unfriendly?? + )) + ;; Now display arrow based on mode. + (edebug-overlay-arrow) + + (cond + ((eq 'error edebug-arg-mode) + ;; Display error message + (setq edebug-execution-mode 'step) + (edebug-overlay-arrow) + (beep) + (if (eq 'quit (car edebug-value)) + (message "Quit") + (edebug-report-error edebug-value))) + (edebug-break + (cond + (edebug-global-break + (message "Global Break: %s => %s" + edebug-global-break-condition + edebug-global-break-result)) + (edebug-break-condition + (message "Break: %s => %s" + edebug-break-condition + edebug-break-result)) + ((not (eq edebug-execution-mode 'Continue-fast)) + (message "Break")) + (t))) + + (t (message ""))) + + (if (eq 'after edebug-arg-mode) + (progn + ;; Display result of previous evaluation. + (if (and edebug-break + (not (eq edebug-execution-mode 'Continue-fast))) + (sit-for 1)) ; Show break message. + (edebug-previous-result))) + + (cond + (edebug-break + (cond + ((eq edebug-execution-mode 'continue) (edebug-sit-for 1)) + ((eq edebug-execution-mode 'Continue-fast) (edebug-sit-for 0)) + (t (setq edebug-stop t)))) + ;; not edebug-break + ((eq edebug-execution-mode 'trace) + (edebug-sit-for 1)) ; Force update and pause. + ((eq edebug-execution-mode 'Trace-fast) + (edebug-sit-for 0)) ; Force update and continue. + ) + + (unwind-protect + (if (or edebug-stop + (memq edebug-execution-mode '(step next)) + (eq edebug-arg-mode 'error)) + (progn + ;; (setq edebug-execution-mode 'step) + ;; (edebug-overlay-arrow) ; this doesnt always show up. + (edebug-recursive-edit))) ; <---------- Recursive edit + + ;; Reset the edebug-window-data to whatever it is now. + (let ((window (if (eq (window-buffer) edebug-buffer) + (selected-window) + (edebug-get-buffer-window edebug-buffer)))) + ;; Remember window-start for edebug-buffer, if still displayed. + (if window + (progn + (setcar edebug-window-data window) + (setcdr edebug-window-data (window-start window))))) + + ;; Save trace window point before restoring outside windows. + ;; Could generalize this for other buffers. + (setq edebug-trace-window (get-buffer-window edebug-trace-buffer)) + (if edebug-trace-window + (setq edebug-trace-window-start + (and edebug-trace-window + (window-start edebug-trace-window)))) + + ;; Restore windows before continuing. + (if edebug-save-windows + (progn + (edebug-set-windows edebug-outside-windows) + + ;; Restore displayed buffer points. + ;; Needed even if restoring windows because + ;; window-points are not restored. (should they be??) + (if edebug-save-displayed-buffer-points + (edebug-set-buffer-points edebug-buffer-points)) + + ;; Unrestore trace window's window-point. + (if edebug-trace-window + (set-window-start edebug-trace-window + edebug-trace-window-start)) + + ;; Unrestore edebug-buffer's window-start, if displayed. + (let ((window (car edebug-window-data))) + (if (and window (edebug-window-live-p window) + (eq (window-buffer) edebug-buffer)) + (progn + (set-window-start window (cdr edebug-window-data) + 'no-force) + ;; Unrestore edebug-buffer's window-point. + ;; Needed in addition to setting the buffer point + ;; - otherwise quitting doesnt leave point as is. + ;; But this causes point to not be restored at times. + ;; Also, it may not be a visible window. + ;; (set-window-point window edebug-point) + ))) + + ;; Unrestore edebug-buffer's point. Rerestored below. + ;; (goto-char edebug-point) ;; in edebug-buffer + ) + ;; Since we may be in a save-excursion, in case of quit, + ;; reselect the outside window only. + ;; Only needed if we are not recovering windows?? + (if (edebug-window-live-p edebug-outside-window) + (select-window edebug-outside-window)) + ) ; if edebug-save-windows + + ;; Restore current buffer always, in case application needs it. + (set-buffer edebug-outside-buffer) + ;; Restore point, and mark. + ;; Needed even if restoring windows because + ;; that doesnt restore point and mark in the current buffer. + ;; But dont restore point if edebug-buffer is current buffer. + (if (not (eq edebug-buffer edebug-outside-buffer)) + (goto-char edebug-outside-point)) + (if (marker-buffer (edebug-mark-marker)) + ;; Does zmacs-regions need to be nil while doing set-marker? + (set-marker (edebug-mark-marker) edebug-outside-mark)) + ) ; unwind-protect + ;; None of the following is done if quit or signal occurs. + + ;; Restore edebug-buffer's outside point. + ;; (edebug-trace "restore edebug-buffer point: %s" + ;; edebug-buffer-outside-point) + (let ((current-buffer (current-buffer))) + (set-buffer edebug-buffer) + (goto-char edebug-buffer-outside-point) + (set-buffer current-buffer)) + ;; ... nothing more. + ) + ;; Reset global variables to outside values in case they were changed. + (setq + overlay-arrow-position edebug-outside-o-a-p + overlay-arrow-string edebug-outside-o-a-s + cursor-in-echo-area edebug-outside-c-i-e-a) + ))) + + +(defvar edebug-number-of-recursions 0) +;; Number of recursive edits started by edebug. +;; Should be 0 at the top level. + +(defvar edebug-recursion-depth 0) +;; Value of recursion-depth when edebug was called. + +;; Dynamically declared unbound vars +(defvar edebug-outside-match-data) ; match data outside of edebug +(defvar edebug-backtrace-buffer) ; each recursive edit gets its own +(defvar edebug-inside-windows) +(defvar edebug-interactive-p) + +(defvar edebug-outside-map) +(defvar edebug-outside-standard-output) +(defvar edebug-outside-standard-input) +(defvar edebug-outside-last-command-char) +(defvar edebug-outside-last-command) +(defvar edebug-outside-this-command) +(defvar edebug-outside-last-input-char) + +;; Note: here we have defvars for variables that are +;; built-in in certain versions. +;; Each defvar makes a difference +;; in versions where the variable is *not* built-in. + +;; Emacs 18 +(defvar edebug-outside-unread-command-char) + +;; XEmacs +(defvar edebug-outside-unread-command-event) ;; like unread-command-events +(defvar unread-command-event nil) + +;; Emacs 19. +(defvar edebug-outside-last-command-event) +(defvar edebug-outside-unread-command-events) +(defvar edebug-outside-last-input-event) +(defvar edebug-outside-last-event-frame) +(defvar edebug-outside-last-nonmenu-event) +(defvar edebug-outside-track-mouse) + +;; Disable byte compiler warnings about unread-command-char and -event +;; (maybe works with byte-compile-version 2.22 at least) +(defvar edebug-unread-command-char-warning) +(defvar edebug-unread-command-event-warning) +(eval-when-compile + (setq edebug-unread-command-char-warning + (get 'unread-command-char 'byte-obsolete-variable)) + (put 'unread-command-char 'byte-obsolete-variable nil) + (setq edebug-unread-command-event-warning + (get 'unread-command-event 'byte-obsolete-variable)) + (put 'unread-command-event 'byte-obsolete-variable nil)) + +(defun edebug-recursive-edit () + ;; Start up a recursive edit inside of edebug. + ;; The current buffer is the edebug-buffer, which is put into edebug-mode. + ;; Assume that none of the variables below are buffer-local. + (let ((edebug-buffer-read-only buffer-read-only) + ;; match-data must be done in the outside buffer + (edebug-outside-match-data + (save-excursion ; might be unnecessary now?? + (set-buffer edebug-outside-buffer) ; in case match buffer different + (match-data))) + + ;;(edebug-number-of-recursions (1+ edebug-number-of-recursions)) + (edebug-recursion-depth (recursion-depth)) + edebug-entered ; bind locally to nil + (edebug-interactive-p nil) ; again non-interactive + edebug-backtrace-buffer ; each recursive edit gets its own + ;; The window configuration may be saved and restored + ;; during a recursive-edit + edebug-inside-windows + + (edebug-outside-map (current-local-map)) + + (edebug-outside-standard-output standard-output) + (edebug-outside-standard-input standard-input) + (edebug-outside-defining-kbd-macro defining-kbd-macro) + + (edebug-outside-last-command-char last-command-char) + (edebug-outside-last-command last-command) + (edebug-outside-this-command this-command) + (edebug-outside-last-input-char last-input-char) + + ;; XEmacs: added the boundp checks + (edebug-outside-unread-command-char + (and (boundp 'unread-command-char) unread-command-char)) + + (edebug-outside-last-input-event + (and (boundp 'last-input-event) last-input-event)) + (edebug-outside-last-command-event + (and (boundp 'last-command-event) last-command-event)) + (edebug-outside-unread-command-event + (and (boundp 'unread-command-event) unread-command-event)) + (edebug-outside-unread-command-events + (and (boundp 'unread-command-events) unread-command-events)) + (edebug-outside-last-event-frame + (and (boundp 'last-event-frame) last-event-frame)) + (edebug-outside-last-nonmenu-event + (and (boundp 'last-nonmenu-event) last-nonmenu-event)) + (edebug-outside-track-mouse + (and (boundp 'track-mouse) track-mouse)) + ) + + (unwind-protect + (let ( + ;; Declare global values local but using the same global value. + ;; We could set these to the values for previous edebug call. + (last-command-char last-command-char) + (last-command last-command) + (this-command this-command) + (last-input-char last-input-char) + + ;; Assume no edebug command sets unread-command-char. + (unread-command-char -1) + + ;; More for Emacs 19 + (last-input-event nil) + (last-command-event nil) + (unread-command-event nil);; XEmacs + (unread-command-events nil) + (last-event-frame nil) + (last-nonmenu-event nil) + (track-mouse nil) + + ;; Bind again to outside values. + (debug-on-error edebug-outside-debug-on-error) + (debug-on-quit edebug-outside-debug-on-quit) + + ;; Don't keep defining a kbd macro. + (defining-kbd-macro + (if edebug-continue-kbd-macro defining-kbd-macro)) + + ;; others?? + ) + + (if (fboundp 'zmacs-deactivate-region);; for XEmacs + (zmacs-deactivate-region)) + (if (and (eq edebug-execution-mode 'go) + (not (memq edebug-arg-mode '(after error)))) + (message "Break")) + + (setq buffer-read-only t) + (fset 'signal (symbol-function 'edebug-original-signal)) + + (edebug-mode) + (unwind-protect + (recursive-edit) ; <<<<<<<<<< Recursive edit + + ;; Do the following, even if quit occurs. + (fset 'signal 'edebug-signal) + (if edebug-backtrace-buffer + (kill-buffer edebug-backtrace-buffer)) + ;; Could be an option to keep eval display up. + (if edebug-eval-buffer (kill-buffer edebug-eval-buffer)) + + ;; Remember selected-window after recursive-edit. + ;; (setq edebug-inside-window (selected-window)) + + (store-match-data edebug-outside-match-data) + + ;; Recursive edit may have changed buffers, + ;; so set it back before exiting let. + (if (buffer-name edebug-buffer) ; if it still exists + (progn + (set-buffer edebug-buffer) + (if (memq edebug-execution-mode '(go Go-nonstop)) + (edebug-overlay-arrow)) + (setq buffer-read-only edebug-buffer-read-only) + (use-local-map edebug-outside-map) + ) + ;; gotta have a buffer to let its buffer local variables be set + (get-buffer-create " bogus edebug buffer")) + ));; inner let + + ;; Reset global vars to outside values, in case they have been changed. + (setq + last-command-char edebug-outside-last-command-char + last-command-event edebug-outside-last-command-event + last-command edebug-outside-last-command + this-command edebug-outside-this-command + unread-command-char edebug-outside-unread-command-char + unread-command-event edebug-outside-unread-command-event + unread-command-events edebug-outside-unread-command-events + last-input-char edebug-outside-last-input-char + last-input-event edebug-outside-last-input-event + last-event-frame edebug-outside-last-event-frame + last-nonmenu-event edebug-outside-last-nonmenu-event + track-mouse edebug-outside-track-mouse + + standard-output edebug-outside-standard-output + standard-input edebug-outside-standard-input + defining-kbd-macro edebug-outside-defining-kbd-macro + )) + )) + + +;;; Display related functions +;;; =============================== + +(defun edebug-adjust-window (old-start) + ;; If pos is not visible, adjust current window to fit following context. +;;; (message "window: %s old-start: %s window-start: %s pos: %s" +;;; (selected-window) old-start (window-start) (point)) (sit-for 5) + (if (not (pos-visible-in-window-p)) + (progn + ;; First try old-start + (if old-start + (set-window-start (selected-window) old-start)) + (if (not (pos-visible-in-window-p)) + (progn +;; (message "resetting window start") (sit-for 2) + (set-window-start + (selected-window) + (save-excursion + (forward-line + (if (< (point) (window-start)) -1 ; one line before if in back + (- (/ (window-height) 2)) ; center the line moving forward + )) + (beginning-of-line) + (point))))))) + (window-start)) + + + +(defconst edebug-arrow-alist + '((Continue-fast . "=") + (Trace-fast . "-") + (continue . ">") + (trace . "->") + (step . "=>") + (next . "=>") + (go . "<>") + (Go-nonstop . "..") ; not used + ) + "Association list of arrows for each edebug mode.") + +(defun edebug-overlay-arrow () + ;; Set up the overlay arrow at beginning-of-line in current buffer. + ;; The arrow string is derived from edebug-arrow-alist and + ;; edebug-execution-mode. + (let ((pos (save-excursion (beginning-of-line) (point)))) + (setq overlay-arrow-string + (cdr (assq edebug-execution-mode edebug-arrow-alist))) + (setq overlay-arrow-position (make-marker)) + (set-marker overlay-arrow-position pos (current-buffer)))) + + +(defun edebug-toggle-save-all-windows () + "Toggle the saving and restoring of all windows. +Also, each time you toggle it on, the inside and outside window +configurations become the same as the current configuration." + (interactive) + (setq edebug-save-windows (not edebug-save-windows)) + (if edebug-save-windows + (setq edebug-inside-windows + (setq edebug-outside-windows + (edebug-current-windows + edebug-save-windows)))) + (message "Window saving is %s for all windows." + (if edebug-save-windows "on" "off"))) + +(defmacro edebug-changing-windows (&rest body) + (` (let ((window (selected-window))) + (setq edebug-inside-windows (edebug-current-windows t)) + (edebug-set-windows edebug-outside-windows) + (,@ body) ;; Code to change edebug-save-windows + (setq edebug-outside-windows (edebug-current-windows + edebug-save-windows)) + ;; Problem: what about outside windows that are deleted inside? + (edebug-set-windows edebug-inside-windows)))) + +(defun edebug-toggle-save-selected-window () + "Toggle the saving and restoring of the selected window. +Also, each time you toggle it on, the inside and outside window +configurations become the same as the current configuration." + (interactive) + (cond + ((eq t edebug-save-windows) + ;; Save all outside windows except the selected one. + ;; Remove (selected-window) from outside-windows. + (edebug-changing-windows + (setq edebug-save-windows (delq window (edebug-window-list))))) + + ((memq (selected-window) edebug-save-windows) + (setq edebug-outside-windows + (delq (assq (selected-window) edebug-outside-windows) + edebug-outside-windows)) + (setq edebug-save-windows + (delq (selected-window) edebug-save-windows))) + (t ; Save a new window. + (edebug-changing-windows + (setq edebug-save-windows (cons window edebug-save-windows))))) + + (message "Window saving is %s for %s." + (if (memq (selected-window) edebug-save-windows) + "on" "off") + (selected-window))) + +(defun edebug-toggle-save-windows (arg) + "Toggle the saving and restoring of windows. +With prefix, toggle for just the selected window. +Otherwise, toggle for all windows." + (interactive "P") + (if arg + (edebug-toggle-save-selected-window) + (edebug-toggle-save-all-windows))) + + +(defun edebug-where () + "Show the debug windows and where we stopped in the program." + (interactive) + (if (not edebug-active) + (error "Edebug is not active")) + ;; Restore the window configuration to what it last was inside. + ;; But it is not always set. - experiment + ;;(if edebug-inside-windows + ;; (edebug-set-windows edebug-inside-windows)) + (edebug-pop-to-buffer edebug-buffer) + (goto-char edebug-point)) + +(defun edebug-view-outside () + "Change to the outside window configuration." + (interactive) + (if (not edebug-active) + (error "Edebug is not active")) + (setq edebug-inside-windows + (edebug-current-windows edebug-save-windows)) + (edebug-set-windows edebug-outside-windows) + (goto-char edebug-outside-point) + (message "Window configuration outside of Edebug. Return with %s" + (substitute-command-keys "\\<global-map>\\[edebug-where]"))) + + +(defun edebug-bounce-point (arg) + "Bounce the point in the outside current buffer. +If prefix arg is supplied, sit for that many seconds before returning. +The default is one second." + (interactive "p") + (if (not edebug-active) + (error "Edebug is not active")) + (save-excursion + ;; If the buffer's currently displayed, avoid set-window-configuration. + (save-window-excursion + (edebug-pop-to-buffer edebug-outside-buffer) + (goto-char edebug-outside-point) + (message "Current buffer: %s Point: %s Mark: %s" + (current-buffer) (point) + (if (marker-buffer (edebug-mark-marker)) + (marker-position (edebug-mark-marker)) "<not set>")) + (edebug-sit-for arg) + (edebug-pop-to-buffer edebug-buffer (car edebug-window-data))))) + + +;; Joe Wells, here is a start at your idea of adding a buffer to the internal +;; display list. Still need to use this list in edebug-display. + +'(defvar edebug-display-buffer-list nil + "List of buffers that edebug will display when it is active.") + +'(defun edebug-display-buffer (buffer) + "Toggle display of a buffer inside of edebug." + (interactive "bBuffer: ") + (let ((already-displaying (memq buffer edebug-display-buffer-list))) + (setq edebug-display-buffer-list + (if already-displaying + (delq buffer edebug-display-buffer-list) + (cons buffer edebug-display-buffer-list))) + (message "Displaying %s %s" buffer + (if already-displaying "off" "on")))) + + +;;; Breakpoint related functions +;;; =============================== + +(defun edebug-find-stop-point () + ;; Return (function . index) of the nearest edebug stop point. + (let* ((edebug-def-name (edebug-form-data-symbol)) + (edebug-data + (let ((data (get edebug-def-name 'edebug))) + (if (or (null data) (markerp data)) + (error "%s is not instrumented for Edebug" edebug-def-name)) + data)) ; we could do it automatically, if data is a marker. + ;; pull out parts of edebug-data. + (edebug-def-mark (car edebug-data)) + ;; (edebug-breakpoints (car (cdr edebug-data))) + + (offset-vector (nth 2 edebug-data)) + (offset (- (save-excursion + (if (looking-at "[ \t]") + ;; skip backwards until non-whitespace, or bol + (skip-chars-backward " \t")) + (point)) + edebug-def-mark)) + len i) + ;; the offsets are in order so we can do a linear search + (setq len (length offset-vector)) + (setq i 0) + (while (and (< i len) (> offset (aref offset-vector i))) + (setq i (1+ i))) + (if (and (< i len) + (<= offset (aref offset-vector i))) + ;; return the relevant info + (cons edebug-def-name i) + (message "Point is not on an expression in %s." + edebug-def-name) + ))) + + +(defun edebug-next-breakpoint () + "Move point to the next breakpoint, or first if none past point." + (interactive) + (let ((edebug-stop-point (edebug-find-stop-point))) + (if edebug-stop-point + (let* ((edebug-def-name (car edebug-stop-point)) + (index (cdr edebug-stop-point)) + (edebug-data (get edebug-def-name 'edebug)) + + ;; pull out parts of edebug-data + (edebug-def-mark (car edebug-data)) + (edebug-breakpoints (car (cdr edebug-data))) + (offset-vector (nth 2 edebug-data)) + breakpoint) + (if (not edebug-breakpoints) + (message "No breakpoints in this function.") + (let ((breaks edebug-breakpoints)) + (while (and breaks + (<= (car (car breaks)) index)) + (setq breaks (cdr breaks))) + (setq breakpoint + (if breaks + (car breaks) + ;; goto the first breakpoint + (car edebug-breakpoints))) + (goto-char (+ edebug-def-mark + (aref offset-vector (car breakpoint)))) + + (message (concat (if (nth 2 breakpoint) + "Temporary " "") + (if (car (cdr breakpoint)) + (format "Condition: %s" + (edebug-safe-prin1-to-string + (car (cdr breakpoint)))) + ""))) + )))))) + + +(defun edebug-modify-breakpoint (flag &optional condition temporary) + "Modify the breakpoint for the form at point or after it according +to FLAG: set if t, clear if nil. Then move to that point. +If CONDITION or TEMPORARY are non-nil, add those attributes to +the breakpoint. " + (let ((edebug-stop-point (edebug-find-stop-point))) + (if edebug-stop-point + (let* ((edebug-def-name (car edebug-stop-point)) + (index (cdr edebug-stop-point)) + (edebug-data (get edebug-def-name 'edebug)) + + ;; pull out parts of edebug-data + (edebug-def-mark (car edebug-data)) + (edebug-breakpoints (car (cdr edebug-data))) + (offset-vector (nth 2 edebug-data)) + present) + ;; delete it either way + (setq present (assq index edebug-breakpoints)) + (setq edebug-breakpoints (delq present edebug-breakpoints)) + (if flag + (progn + ;; add it to the list and resort + (setq edebug-breakpoints + (edebug-sort-alist + (cons + (list index condition temporary) + edebug-breakpoints) '<)) + (if condition + (message "Breakpoint set in %s with condition: %s" + edebug-def-name condition) + (message "Breakpoint set in %s" edebug-def-name))) + (if present + (message "Breakpoint unset in %s" edebug-def-name) + (message "No breakpoint here"))) + + (setcar (cdr edebug-data) edebug-breakpoints) + (goto-char (+ edebug-def-mark (aref offset-vector index))) + )))) + +(defun edebug-set-breakpoint (arg) + "Set the breakpoint of nearest sexp. +With prefix argument, make it a temporary breakpoint." + (interactive "P") + (edebug-modify-breakpoint t nil arg)) + +(defun edebug-unset-breakpoint () + "Clear the breakpoint of nearest sexp." + (interactive) + (edebug-modify-breakpoint nil)) + + +;; For emacs 18, no read-expression-history +(defun edebug-set-conditional-breakpoint (arg condition) + "Set a conditional breakpoint at nearest sexp. +The condition is evaluated in the outside context. +With prefix argument, make it a temporary breakpoint." + ;; (interactive "P\nxCondition: ") + (interactive + (list + current-prefix-arg + ;; Edit previous condition as follows, but it is cumbersome: + (let ((edebug-stop-point (edebug-find-stop-point))) + (if edebug-stop-point + (let* ((edebug-def-name (car edebug-stop-point)) + (index (cdr edebug-stop-point)) + (edebug-data (get edebug-def-name 'edebug)) + (edebug-breakpoints (car (cdr edebug-data))) + (edebug-break-data (assq index edebug-breakpoints)) + (edebug-break-condition (car (cdr edebug-break-data)))) + (read-minibuffer + (format "Condition in %s: " edebug-def-name) + (if edebug-break-condition + (format "%s" edebug-break-condition) + (format "")))))))) + (edebug-modify-breakpoint t condition arg)) + + +(defun edebug-set-global-break-condition (expression) + (interactive (list (read-minibuffer + "Global Condition: " + (format "%s" edebug-global-break-condition)))) + (setq edebug-global-break-condition expression)) + + +;;; Mode switching functions +;;; =============================== + +(defun edebug-set-mode (mode shortmsg msg) + ;; Set the edebug mode to MODE. + ;; Display SHORTMSG, or MSG if not within edebug. + (if (eq (1+ edebug-recursion-depth) (recursion-depth)) + (progn + (setq edebug-execution-mode mode) + (message shortmsg) + ;; Continue execution + (exit-recursive-edit)) + ;; This is not terribly useful!! + (setq edebug-next-execution-mode mode) + (message msg))) + + +(defalias 'edebug-step-through-mode 'edebug-step-mode) + +(defun edebug-step-mode () + "Proceed to next stop point." + (interactive) + (edebug-set-mode 'step "" "Edebug will stop at next stop point.")) + +(defun edebug-next-mode () + "Proceed to next `after' stop point." + (interactive) + (edebug-set-mode 'next "" "Edebug will stop after next eval.")) + +(defun edebug-go-mode (arg) + "Go, evaluating until break. +With prefix ARG, set temporary break at current point and go." + (interactive "P") + (if arg + (edebug-set-breakpoint t)) + (edebug-set-mode 'go "Go..." "Edebug will go until break.")) + +(defun edebug-Go-nonstop-mode () + "Go, evaluating without debugging." + (interactive) + (edebug-set-mode 'Go-nonstop "Go-Nonstop..." + "Edebug will not stop at breaks.")) + + +(defun edebug-trace-mode () + "Begin trace mode." + (interactive) + (edebug-set-mode 'trace "Tracing..." "Edebug will trace with pause.")) + +(defun edebug-Trace-fast-mode () + "Trace with no wait at each step." + (interactive) + (edebug-set-mode 'Trace-fast + "Trace fast..." "Edebug will trace without pause.")) + +(defun edebug-continue-mode () + "Begin continue mode." + (interactive) + (edebug-set-mode 'continue "Continue..." + "Edebug will pause at breakpoints.")) + +(defun edebug-Continue-fast-mode () + "Trace with no wait at each step." + (interactive) + (edebug-set-mode 'Continue-fast "Continue fast..." + "Edebug will stop and go at breakpoints.")) + +;; ------------------------------------------------------------ +;; The following use the mode changing commands and breakpoints. + + +(defun edebug-goto-here () + "Proceed to this stop point." + (interactive) + (edebug-go-mode t)) + + +(defun edebug-stop () + "Stop execution and do not continue. +Useful for exiting from trace or continue loop." + (interactive) + (message "Stop")) + + +'(defun edebug-forward () + "Proceed to the exit of the next expression to be evaluated." + (interactive) + (edebug-set-mode + 'forward "Forward" + "Edebug will stop after exiting the next expression.")) + + +(defun edebug-forward-sexp (arg) + "Proceed from the current point to the end of the ARGth sexp ahead. +If there are not ARG sexps ahead, then do edebug-step-out." + (interactive "p") + (condition-case nil + (let ((parse-sexp-ignore-comments t)) + ;; Call forward-sexp repeatedly until done or failure. + (forward-sexp arg) + (edebug-go-mode t)) + (error + (edebug-step-out) + ))) + +(defun edebug-step-out () + "Proceed from the current point to the end of the containing sexp. +If there is no containing sexp that is not the top level defun, +go to the end of the last sexp, or if that is the same point, then step." + (interactive) + (condition-case nil + (let ((parse-sexp-ignore-comments t)) + (up-list 1) + (save-excursion + ;; Is there still a containing expression? + (up-list 1)) + (edebug-go-mode t)) + (error + ;; At top level - 1, so first check if there are more sexps at this level. + (let ((start-point (point))) +;; (up-list 1) + (down-list -1) + (if (= (point) start-point) + (edebug-step-mode) ; No more at this level, so step. + (edebug-go-mode t) + ))))) + +(defun edebug-instrument-function (func) + ;; Func should be a function symbol. + ;; Return the function symbol, or nil if not instrumented. + (let ((func-marker)) + (setq func-marker (get func 'edebug)) + (cond + ((markerp func-marker) + ;; It is uninstrumented, so instrument it. + (save-excursion + (set-buffer (marker-buffer func-marker)) + (goto-char func-marker) + (edebug-eval-top-level-form) + func)) + ((consp func-marker) + (message "%s is already instrumented." func) + func) + (t + ;; We could try harder, e.g. do a tags search. + (error "Don't know where %s is defined" func) + nil)))) + +(defun edebug-instrument-callee () + "Instrument the definition of the function or macro about to be called. +Do this when stopped before the form or it will be too late. +One side effect of using this command is that the next time the +function or macro is called, Edebug will be called there as well." + (interactive) + (if (not (looking-at "\(")) + (error "You must be before a list form") + (let ((func + (save-excursion + (down-list 1) + (if (looking-at "\(") + (edebug-form-data-name + (edebug-get-form-data-entry (point))) + (edebug-original-read (current-buffer)))))) + (edebug-instrument-function func)))) + + +(defun edebug-step-in () + "Step into the definition of the function or macro about to be called. +This first does `edebug-instrument-callee' to ensure that it is +instrumented. Then it does `edebug-on-entry' and switches to `go' mode." + (interactive) + (let ((func (edebug-instrument-callee))) + (if func + (progn + (edebug-on-entry func 'temp) + (edebug-go-mode nil))))) + +(defun edebug-on-entry (function &optional flag) + "Cause Edebug to stop when FUNCTION is called. +With prefix argument, make this temporary so it is automatically +cancelled the first time the function is entered." + (interactive "aEdebug on entry to: \nP") + ;; Could store this in the edebug data instead. + (put function 'edebug-on-entry (if flag 'temp t))) + +(defun cancel-edebug-on-entry (function) + (interactive "aEdebug on entry to: ") + (put function 'edebug-on-entry nil)) + + +(if (not (fboundp 'edebug-original-debug-on-entry)) + (fset 'edebug-original-debug-on-entry (symbol-function 'debug-on-entry))) +'(fset 'debug-on-entry 'edebug-debug-on-entry) ;; Should we do this? +;; Also need edebug-cancel-debug-on-entry + +'(defun edebug-debug-on-entry (function) + "Request FUNCTION to invoke debugger each time it is called. +If the user continues, FUNCTION's execution proceeds. +Works by modifying the definition of FUNCTION, +which must be written in Lisp, not predefined. +Use `cancel-debug-on-entry' to cancel the effect of this command. +Redefining FUNCTION also does that. + +This version is from Edebug. If the function is instrumented for +Edebug, it calls `edebug-on-entry'" + (interactive "aDebug on entry (to function): ") + (let ((func-data (get function 'edebug))) + (if (or (null func-data) (markerp func-data)) + (edebug-original-debug-on-entry function) + (edebug-on-entry function)))) + + +(defun edebug-top-level-nonstop () + "Set mode to Go-nonstop, and exit to top-level. +This is useful for exiting even if unwind-protect code may be executed." + (interactive) + (setq edebug-execution-mode 'Go-nonstop) + (top-level)) + + +;;(defun edebug-exit-out () +;; "Go until the current function exits." +;; (interactive) +;; (edebug-set-mode 'exiting "Exit...")) + + +;;; ----------------------------------------------------------------- +;;; The following initial mode setting definitions are not used yet. + +'(defconst edebug-initial-mode-alist + '((edebug-Continue-fast . Continue-fast) + (edebug-Trace-fast . Trace-fast) + (edebug-continue . continue) + (edebug-trace . trace) + (edebug-go . go) + (edebug-step-through . step) + (edebug-Go-nonstop . Go-nonstop) + ) + "Association list between commands and the modes they set.") + + +'(defun edebug-set-initial-mode () + "Ask for the initial mode of the enclosing function. +The mode is requested via the key that would be used to set the mode in +edebug-mode." + (interactive) + (let* ((this-function (edebug-which-function)) + (keymap (if (eq edebug-mode-map (current-local-map)) + edebug-mode-map)) + (old-mode (or (get this-function 'edebug-initial-mode) + edebug-initial-mode)) + (key (read-key-sequence + (format + "Change initial edebug mode for %s from %s (%s) to (enter key): " + this-function + old-mode + (where-is-internal + (car (rassq old-mode edebug-initial-mode-alist)) + keymap 'firstonly + )))) + (mode (cdr (assq (key-binding key) edebug-initial-mode-alist))) + ) + (if (and mode + (or (get this-function 'edebug-initial-mode) + (not (eq mode edebug-initial-mode)))) + (progn + (put this-function 'edebug-initial-mode mode) + (message "Initial mode for %s is now: %s" + this-function mode)) + (error "Key must map to one of the mode changing commands") + ))) + + +;;; Evaluation of expressions +;;; =============================== + +(def-edebug-spec edebug-outside-excursion t) + +(defmacro edebug-outside-excursion (&rest body) + "Evaluate an expression list in the outside context. +Return the result of the last expression." + (` (save-excursion ; of current-buffer + (if edebug-save-windows + (progn + ;; After excursion, we will + ;; restore to current window configuration. + (setq edebug-inside-windows + (edebug-current-windows edebug-save-windows)) + ;; Restore outside windows. + (edebug-set-windows edebug-outside-windows))) + + (set-buffer edebug-buffer) ; why? + ;; (use-local-map edebug-outside-map) + (store-match-data edebug-outside-match-data) + ;; Restore outside context. + (let (;; (edebug-inside-map (current-local-map)) ;; restore map?? + (last-command-char edebug-outside-last-command-char) + (last-command-event edebug-outside-last-command-event) + (last-command edebug-outside-last-command) + (this-command edebug-outside-this-command) + (unread-command-char edebug-outside-unread-command-char) + (unread-command-event edebug-outside-unread-command-event) + (unread-command-events edebug-outside-unread-command-events) + (last-input-char edebug-outside-last-input-char) + (last-input-event edebug-outside-last-input-event) + (last-event-frame edebug-outside-last-event-frame) + (last-nonmenu-event edebug-outside-last-nonmenu-event) + (track-mouse edebug-outside-track-mouse) + (standard-output edebug-outside-standard-output) + (standard-input edebug-outside-standard-input) + + (executing-macro edebug-outside-executing-macro) + (defining-kbd-macro edebug-outside-defining-kbd-macro) + (pre-command-hook edebug-outside-pre-command-hook) + (post-command-hook edebug-outside-post-command-hook) + (post-command-idle-hook edebug-outside-post-command-idle-hook) + + ;; See edebug-display + (overlay-arrow-position edebug-outside-o-a-p) + (overlay-arrow-string edebug-outside-o-a-s) + (cursor-in-echo-area edebug-outside-c-i-e-a) + ) + (unwind-protect + (save-excursion ; of edebug-buffer + (set-buffer edebug-outside-buffer) + (goto-char edebug-outside-point) + (if (marker-buffer (edebug-mark-marker)) + (set-marker (edebug-mark-marker) edebug-outside-mark)) + (,@ body)) + + ;; Back to edebug-buffer. Restore rest of inside context. + ;; (use-local-map edebug-inside-map) + (if edebug-save-windows + ;; Restore inside windows. + (edebug-set-windows edebug-inside-windows)) + + ;; Save values that may have been changed. + (setq + edebug-outside-last-command-char last-command-char + edebug-outside-last-command-event last-command-event + edebug-outside-last-command last-command + edebug-outside-this-command this-command + edebug-outside-unread-command-char unread-command-char + edebug-outside-unread-command-event unread-command-event + edebug-outside-unread-command-events unread-command-events + edebug-outside-last-input-char last-input-char + edebug-outside-last-input-event last-input-event + edebug-outside-last-event-frame last-event-frame + edebug-outside-last-nonmenu-event last-nonmenu-event + edebug-outside-track-mouse track-mouse + edebug-outside-standard-output standard-output + edebug-outside-standard-input standard-input + + edebug-outside-executing-macro executing-macro + edebug-outside-defining-kbd-macro defining-kbd-macro + edebug-outside-pre-command-hook pre-command-hook + edebug-outside-post-command-hook post-command-hook + edebug-outside-post-command-idle-hook post-command-idle-hook + + edebug-outside-o-a-p overlay-arrow-position + edebug-outside-o-a-s overlay-arrow-string + edebug-outside-c-i-e-a cursor-in-echo-area + ))) ; let + ))) + +(defvar cl-debug-env nil) ;; defined in cl; non-nil when lexical env used. + +(defun edebug-eval (edebug-expr) + ;; Are there cl lexical variables active? + (if cl-debug-env + (eval (cl-macroexpand-all edebug-expr cl-debug-env)) + (eval edebug-expr))) + +(defun edebug-safe-eval (edebug-expr) + ;; Evaluate EXPR safely. + ;; If there is an error, a string is returned describing the error. + (condition-case edebug-err + (edebug-eval edebug-expr) + (error (edebug-format "%s: %s" ;; could + (get (car edebug-err) 'error-message) + (car (cdr edebug-err)))))) + +;;;; Printing +;;; ========= +;; Replace printing functions. + +;; obsolete names +(defalias 'edebug-install-custom-print-funcs 'edebug-install-custom-print) +(defalias 'edebug-reset-print-funcs 'edebug-uninstall-custom-print) +(defalias 'edebug-uninstall-custom-print-funcs 'edebug-uninstall-custom-print) + +(defun edebug-install-custom-print () + "Replace print functions used by Edebug with custom versions." + ;; Modifying the custom print functions, or changing print-length, + ;; print-level, print-circle, custom-print-list or custom-print-vector + ;; have immediate effect. + (interactive) + (require 'cust-print) + (defalias 'edebug-prin1 'custom-prin1) + (defalias 'edebug-print 'custom-print) + (defalias 'edebug-prin1-to-string 'custom-prin1-to-string) + (defalias 'edebug-format 'custom-format) + (defalias 'edebug-message 'custom-message) + "Installed") + +(eval-and-compile + (defun edebug-uninstall-custom-print () + "Replace edebug custom print functions with internal versions." + (interactive) + (defalias 'edebug-prin1 'prin1) + (defalias 'edebug-print 'print) + (defalias 'edebug-prin1-to-string 'prin1-to-string) + (defalias 'edebug-format 'format) + (defalias 'edebug-message 'message) + "Uninstalled") + + ;; Default print functions are the same as Emacs'. + (edebug-uninstall-custom-print)) + + +(defun edebug-report-error (edebug-value) + ;; Print an error message like command level does. + ;; This also prints the error name if it has no error-message. + (message "%s: %s" + (or (get (car edebug-value) 'error-message) + (format "peculiar error (%s)" (car edebug-value))) + (mapconcat (function (lambda (edebug-arg) + ;; continuing after an error may + ;; complain about edebug-arg. why?? + (prin1-to-string edebug-arg))) + (cdr edebug-value) ", "))) + +;; Define here in case they are not already defined. +(defvar print-level nil) +(defvar print-circle nil) +(defvar print-readably) ;; defined by XEmacs +;; Alternatively, we could change the definition of +;; edebug-safe-prin1-to-string to only use these if defined. + +(defun edebug-safe-prin1-to-string (value) + (let ((print-escape-newlines t) + (print-length (or edebug-print-length print-length)) + (print-level (or edebug-print-level print-level)) + (print-circle (or edebug-print-circle print-circle)) + (print-readably nil)) ;; XEmacs uses this. + (edebug-prin1-to-string value))) + +(defun edebug-compute-previous-result (edebug-previous-value) + (setq edebug-previous-result + (if (and (numberp edebug-previous-value) + (< edebug-previous-value 256) + (>= edebug-previous-value 0)) + (format "Result: %s = %s" edebug-previous-value + (single-key-description edebug-previous-value)) + (if edebug-unwrap-results + (setq edebug-previous-value + (edebug-unwrap* edebug-previous-value))) + (concat "Result: " + (edebug-safe-prin1-to-string edebug-previous-value))))) + +(defun edebug-previous-result () + "Print the previous result." + (interactive) + (message "%s" edebug-previous-result)) + +;;;; Read, Eval and Print +;;; ===================== + +(defun edebug-eval-expression (edebug-expr) + "Evaluate an expression in the outside environment. +If interactive, prompt for the expression. +Print result in minibuffer." + (interactive "xEval: ") + (princ + (edebug-outside-excursion + (setq values (cons (edebug-eval edebug-expr) values)) + (edebug-safe-prin1-to-string (car values))))) + +(defun edebug-eval-last-sexp () + "Evaluate sexp before point in the outside environment; +print value in minibuffer." + (interactive) + (edebug-eval-expression (edebug-last-sexp))) + +(defun edebug-eval-print-last-sexp () + "Evaluate sexp before point in the outside environment; +print value into current buffer." + (interactive) + (let* ((edebug-form (edebug-last-sexp)) + (edebug-result-string + (edebug-outside-excursion + (edebug-safe-prin1-to-string (edebug-safe-eval edebug-form)))) + (standard-output (current-buffer))) + (princ "\n") + ;; princ the string to get rid of quotes. + (princ edebug-result-string) + (princ "\n") + )) + + +;;;; Edebug Minor Mode +;;; =============================== + +;; Global GUD bindings for all emacs-lisp-mode buffers. +(define-key emacs-lisp-mode-map "\C-x\C-a\C-s" 'edebug-step-mode) +(define-key emacs-lisp-mode-map "\C-x\C-a\C-n" 'edebug-next-mode) +(define-key emacs-lisp-mode-map "\C-x\C-a\C-c" 'edebug-go-mode) +(define-key emacs-lisp-mode-map "\C-x\C-a\C-l" 'edebug-where) + + +(defvar edebug-mode-map nil) +(if edebug-mode-map + nil + (progn + (setq edebug-mode-map (copy-keymap emacs-lisp-mode-map)) + ;; control + (define-key edebug-mode-map " " 'edebug-step-mode) + (define-key edebug-mode-map "n" 'edebug-next-mode) + (define-key edebug-mode-map "g" 'edebug-go-mode) + (define-key edebug-mode-map "G" 'edebug-Go-nonstop-mode) + (define-key edebug-mode-map "t" 'edebug-trace-mode) + (define-key edebug-mode-map "T" 'edebug-Trace-fast-mode) + (define-key edebug-mode-map "c" 'edebug-continue-mode) + (define-key edebug-mode-map "C" 'edebug-Continue-fast-mode) + + ;;(define-key edebug-mode-map "f" 'edebug-forward) not implemented + (define-key edebug-mode-map "f" 'edebug-forward-sexp) + (define-key edebug-mode-map "h" 'edebug-goto-here) + + (define-key edebug-mode-map "I" 'edebug-instrument-callee) + (define-key edebug-mode-map "i" 'edebug-step-in) + (define-key edebug-mode-map "o" 'edebug-step-out) + + ;; quitting and stopping + (define-key edebug-mode-map "q" 'top-level) + (define-key edebug-mode-map "Q" 'edebug-top-level-nonstop) + (define-key edebug-mode-map "a" 'abort-recursive-edit) + (define-key edebug-mode-map "S" 'edebug-stop) + + ;; breakpoints + (define-key edebug-mode-map "b" 'edebug-set-breakpoint) + (define-key edebug-mode-map "u" 'edebug-unset-breakpoint) + (define-key edebug-mode-map "B" 'edebug-next-breakpoint) + (define-key edebug-mode-map "x" 'edebug-set-conditional-breakpoint) + (define-key edebug-mode-map "X" 'edebug-set-global-break-condition) + + ;; evaluation + (define-key edebug-mode-map "r" 'edebug-previous-result) + (define-key edebug-mode-map "e" 'edebug-eval-expression) + (define-key edebug-mode-map "\C-x\C-e" 'edebug-eval-last-sexp) + (define-key edebug-mode-map "E" 'edebug-visit-eval-list) + + ;; views + (define-key edebug-mode-map "w" 'edebug-where) + (define-key edebug-mode-map "v" 'edebug-view-outside) ;; maybe obsolete?? + (define-key edebug-mode-map "p" 'edebug-bounce-point) + (define-key edebug-mode-map "P" 'edebug-view-outside) ;; same as v + (define-key edebug-mode-map "W" 'edebug-toggle-save-windows) + + ;; misc + (define-key edebug-mode-map "?" 'edebug-help) + (define-key edebug-mode-map "d" 'edebug-backtrace) + + (define-key edebug-mode-map "-" 'negative-argument) + + ;; statistics + (define-key edebug-mode-map "=" 'edebug-temp-display-freq-count) + + ;; GUD bindings + (define-key edebug-mode-map "\C-c\C-s" 'edebug-step-mode) + (define-key edebug-mode-map "\C-c\C-n" 'edebug-next-mode) + (define-key edebug-mode-map "\C-c\C-c" 'edebug-go-mode) + + (define-key edebug-mode-map "\C-x " 'edebug-set-breakpoint) + (define-key edebug-mode-map "\C-c\C-d" 'edebug-unset-breakpoint) + (define-key edebug-mode-map "\C-c\C-t" + (function (lambda () (edebug-set-breakpoint t)))) + (define-key edebug-mode-map "\C-c\C-l" 'edebug-where) + )) + +;; Autoloading these global bindings doesn't make sense because +;; they cannot be used anyway unless Edebug is already loaded and active. + +(defvar global-edebug-prefix "\^XX" + "Prefix key for global edebug commands, available from any buffer.") + +(defvar global-edebug-map nil + "Global map of edebug commands, available from any buffer.") + +(if global-edebug-map + nil + (setq global-edebug-map (make-sparse-keymap)) + + (global-unset-key global-edebug-prefix) + (global-set-key global-edebug-prefix global-edebug-map) + + (define-key global-edebug-map " " 'edebug-step-mode) + (define-key global-edebug-map "g" 'edebug-go-mode) + (define-key global-edebug-map "G" 'edebug-Go-nonstop-mode) + (define-key global-edebug-map "t" 'edebug-trace-mode) + (define-key global-edebug-map "T" 'edebug-Trace-fast-mode) + (define-key global-edebug-map "c" 'edebug-continue-mode) + (define-key global-edebug-map "C" 'edebug-Continue-fast-mode) + + ;; breakpoints + (define-key global-edebug-map "b" 'edebug-set-breakpoint) + (define-key global-edebug-map "u" 'edebug-unset-breakpoint) + (define-key global-edebug-map "x" 'edebug-set-conditional-breakpoint) + (define-key global-edebug-map "X" 'edebug-set-global-break-condition) + + ;; views + (define-key global-edebug-map "w" 'edebug-where) + (define-key global-edebug-map "W" 'edebug-toggle-save-windows) + + ;; quitting + (define-key global-edebug-map "q" 'top-level) + (define-key global-edebug-map "Q" 'edebug-top-level-nonstop) + (define-key global-edebug-map "a" 'abort-recursive-edit) + + ;; statistics + (define-key global-edebug-map "=" 'edebug-display-freq-count) + ) + +(defun edebug-help () + (interactive) + (describe-function 'edebug-mode)) + +(defun edebug-mode () + "Mode for Emacs Lisp buffers while in Edebug. + +In addition to all Emacs Lisp commands (except those that modify the +buffer) there are local and global key bindings to several Edebug +specific commands. E.g. `edebug-step-mode' is bound to \\[edebug-step-mode] +in the Edebug buffer and \\<global-map>\\[edebug-step-mode] in any buffer. + +Also see bindings for the eval list buffer, *edebug*. + +The edebug buffer commands: +\\{edebug-mode-map} + +Global commands prefixed by `global-edebug-prefix': +\\{global-edebug-map} + +Options: +edebug-setup-hook +edebug-all-defs +edebug-all-forms +edebug-save-windows +edebug-save-displayed-buffer-points +edebug-initial-mode +edebug-trace +edebug-test-coverage +edebug-continue-kbd-macro +edebug-print-length +edebug-print-level +edebug-print-circle +edebug-on-error +edebug-on-quit +edebug-on-signal +edebug-unwrap-results +edebug-global-break-condition +" + (use-local-map edebug-mode-map)) + + +;;;; edebug eval list mode +;;; =============================================== +;; A list of expressions and their evaluations is displayed in *edebug*. + +(defun edebug-eval-result-list () + "Return a list of evaluations of edebug-eval-list" + ;; Assumes in outside environment. + ;; Don't do any edebug things now. + (let ((edebug-execution-mode 'Go-nonstop) + (edebug-trace nil)) + (mapcar 'edebug-safe-eval edebug-eval-list))) + +(defun edebug-eval-display-list (edebug-eval-result-list) + ;; Assumes edebug-eval-buffer exists. + (let ((edebug-eval-list-temp edebug-eval-list) + (standard-output edebug-eval-buffer) + (edebug-comment-line + (format ";%s\n" (make-string (- (window-width) 2) ?-)))) + (set-buffer edebug-eval-buffer) + (erase-buffer) + (while edebug-eval-list-temp + (prin1 (car edebug-eval-list-temp)) (terpri) + (prin1 (car edebug-eval-result-list)) (terpri) + (princ edebug-comment-line) + (setq edebug-eval-list-temp (cdr edebug-eval-list-temp)) + (setq edebug-eval-result-list (cdr edebug-eval-result-list))) + (edebug-pop-to-buffer edebug-eval-buffer) + )) + +(defun edebug-create-eval-buffer () + (if (not (and edebug-eval-buffer (buffer-name edebug-eval-buffer))) + (progn + (set-buffer (setq edebug-eval-buffer (get-buffer-create "*edebug*"))) + (edebug-eval-mode)))) + +;; Should generalize this to be callable outside of edebug +;; with calls in user functions, e.g. (edebug-eval-display) + +(defun edebug-eval-display (edebug-eval-result-list) + "Display expressions and evaluations in EVAL-LIST. +It modifies the context by popping up the eval display." + (if edebug-eval-result-list + (progn + (edebug-create-eval-buffer) + (edebug-eval-display-list edebug-eval-result-list) + ))) + +(defun edebug-eval-redisplay () + "Redisplay eval list in outside environment. +May only be called from within edebug-recursive-edit." + (edebug-create-eval-buffer) + (edebug-outside-excursion + (edebug-eval-display-list (edebug-eval-result-list)) + )) + +(defun edebug-visit-eval-list () + (interactive) + (edebug-eval-redisplay) + (edebug-pop-to-buffer edebug-eval-buffer)) + + +(defun edebug-update-eval-list () + "Replace the evaluation list with the sexps now in the eval buffer." + (interactive) + (let ((starting-point (point)) + new-list) + (goto-char (point-min)) + ;; get the first expression + (edebug-skip-whitespace) + (if (not (eobp)) + (progn + (forward-sexp 1) + (setq new-list (cons (edebug-last-sexp) new-list)))) + + (while (re-search-forward "^;" nil t) + (forward-line 1) + (skip-chars-forward " \t\n\r") + (if (and (/= ?\; (following-char)) + (not (eobp))) + (progn + (forward-sexp 1) + (setq new-list (cons (edebug-last-sexp) new-list))))) + + (setq edebug-eval-list (nreverse new-list)) + (edebug-eval-redisplay) + (goto-char starting-point))) + + +(defun edebug-delete-eval-item () + "Delete the item under point and redisplay." + ;; could add arg to do repeatedly + (interactive) + (if (re-search-backward "^;" nil 'nofail) + (forward-line 1)) + (delete-region + (point) (progn (re-search-forward "^;" nil 'nofail) + (beginning-of-line) + (point))) + (edebug-update-eval-list)) + + + +(defvar edebug-eval-mode-map nil + "Keymap for edebug-eval-mode. Superset of lisp-interaction-mode.") + +(if edebug-eval-mode-map + nil + (setq edebug-eval-mode-map (copy-keymap lisp-interaction-mode-map)) + + (define-key edebug-eval-mode-map "\C-c\C-w" 'edebug-where) + (define-key edebug-eval-mode-map "\C-c\C-d" 'edebug-delete-eval-item) + (define-key edebug-eval-mode-map "\C-c\C-u" 'edebug-update-eval-list) + (define-key edebug-eval-mode-map "\C-x\C-e" 'edebug-eval-last-sexp) + (define-key edebug-eval-mode-map "\C-j" 'edebug-eval-print-last-sexp) + ) + + +(defun edebug-eval-mode () + "Mode for evaluation list buffer while in Edebug. + +In addition to all Interactive Emacs Lisp commands there are local and +global key bindings to several Edebug specific commands. E.g. +`edebug-step-mode' is bound to \\[edebug-step-mode] in the Edebug +buffer and \\<global-map>\\[edebug-step-mode] in any buffer. + +Eval list buffer commands: +\\{edebug-eval-mode-map} + +Global commands prefixed by global-edebug-prefix: +\\{global-edebug-map} +" + (lisp-interaction-mode) + (setq major-mode 'edebug-eval-mode) + (setq mode-name "Edebug-Eval") + (use-local-map edebug-eval-mode-map)) + + +;;;; Interface with standard debugger. +;;; ======================================== + +;; (setq debugger 'edebug) ; to use the edebug debugger +;; (setq debugger 'debug) ; use the standard debugger + +;; Note that debug and its utilities must be byte-compiled to work, +;; since they depend on the backtrace looking a certain way. But +;; edebug is not dependent on this, yet. + +(defun edebug (&optional edebug-arg-mode &rest debugger-args) + "Replacement for debug. +If we are running an edebugged function, +show where we last were. Otherwise call debug normally." +;; (message "entered: %s depth: %s edebug-recursion-depth: %s" +;; edebug-entered (recursion-depth) edebug-recursion-depth) (sit-for 1) + (if (and edebug-entered ; anything active? + (eq (recursion-depth) edebug-recursion-depth)) + (let (;; Where were we before the error occurred? + (edebug-offset-index (car edebug-offset-indices)) + ;; Bind variables required by edebug-display + (edebug-value (car debugger-args)) + edebug-breakpoints + edebug-break-data + edebug-break-condition + edebug-global-break + (edebug-break (null edebug-arg-mode)) ;; if called explicitly + ) + (edebug-display) + (if (eq edebug-arg-mode 'error) + nil + edebug-value)) + + ;; Otherwise call debug normally. + ;; Still need to remove extraneous edebug calls from stack. + (apply 'debug edebug-arg-mode debugger-args) + )) + + +(defun edebug-backtrace () + "Display a non-working backtrace. Better than nothing..." + (interactive) + (if (or (not edebug-backtrace-buffer) + (null (buffer-name edebug-backtrace-buffer))) + (setq edebug-backtrace-buffer + (generate-new-buffer "*Backtrace*")) + ;; else, could just display edebug-backtrace-buffer + ) + (with-output-to-temp-buffer (buffer-name edebug-backtrace-buffer) + (setq edebug-backtrace-buffer standard-output) + (let ((print-escape-newlines t) + (print-length 50) + last-ok-point) + (backtrace) + + ;; Clean up the backtrace. + ;; Not quite right for current edebug scheme. + (set-buffer edebug-backtrace-buffer) + (setq truncate-lines t) + (goto-char (point-min)) + (setq last-ok-point (point)) + (if t (progn + + ;; Delete interspersed edebug internals. + (while (re-search-forward "^ \(?edebug" nil t) + (beginning-of-line) + (cond + ((looking-at "^ \(edebug-after") + ;; Previous lines may contain code, so just delete this line + (setq last-ok-point (point)) + (forward-line 1) + (delete-region last-ok-point (point))) + + ((looking-at "^ edebug") + (forward-line 1) + (delete-region last-ok-point (point)) + ))) + ))))) + + +;;;; Trace display +;; =============================== + +(defun edebug-trace-display (buf-name fmt &rest args) + "In buffer BUF-NAME, display FMT and ARGS at the end and make it visible. +The buffer is created if it does not exist. +You must include newlines in FMT to break lines, but one newline is appended." +;; e.g. +;; (edebug-trace-display "*trace-point*" +;; "saving: point = %s window-start = %s" +;; (point) (window-start)) + (let* ((selected-window (selected-window)) + (buffer (get-buffer-create buf-name)) + buf-window) +;; (message "before pop-to-buffer") (sit-for 1) + (edebug-pop-to-buffer buffer) + (setq truncate-lines t) + (setq buf-window (selected-window)) + (goto-char (point-max)) + (insert (apply 'edebug-format fmt args) "\n") + ;; Make it visible. + (vertical-motion (- 1 (window-height))) + (set-window-start buf-window (point)) + (goto-char (point-max)) +;; (set-window-point buf-window (point)) +;; (edebug-sit-for 0) + (bury-buffer buffer) + (select-window selected-window)) + buf-name) + + +(defun edebug-trace (fmt &rest args) + "Convenience call to edebug-trace-display using edebug-trace-buffer" + (apply 'edebug-trace-display edebug-trace-buffer fmt args)) + + +;;;; Frequency count and coverage +;;; ============================== + +(defun edebug-display-freq-count () + "Display the frequency count data for each line of the current +definition. The frequency counts are inserted as comment lines after +each line, and you can undo all insertions with one `undo' command. + +The counts are inserted starting under the `(' before an expression +or the `)' after an expression, or on the last char of a symbol. +The counts are only displayed when they differ from previous counts on +the same line. + +If coverage is being tested, whenever all known results of an expression +are `eq', the char `=' will be appended after the count +for that expression. Note that this is always the case for an +expression only evaluated once. + +To clear the frequency count and coverage data for a definition, +reinstrument it." + (interactive) + (let* ((function (edebug-form-data-symbol)) + (counts (get function 'edebug-freq-count)) + (coverages (get function 'edebug-coverage)) + (data (get function 'edebug)) + (def-mark (car data)) ; mark at def start + (edebug-points (nth 2 data)) + (i (1- (length edebug-points))) + (last-index) + (first-index) + (start-of-line) + (start-of-count-line) + (last-count) + ) + (save-excursion + ;; Traverse in reverse order so offsets are correct. + (while (<= 0 i) + ;; Start at last expression in line. + (goto-char (+ def-mark (aref edebug-points i))) + (beginning-of-line) + (setq start-of-line (- (point) def-mark) + last-index i) + + ;; Find all indexes on same line. + (while (and (<= 0 (setq i (1- i))) + (<= start-of-line (aref edebug-points i)))) + ;; Insert all the indices for this line. + (forward-line 1) + (setq start-of-count-line (point) + first-index i ; really last index for line above this one. + last-count -1) ; cause first count to always appear. + (insert ";#") + ;; i == first-index still + (while (<= (setq i (1+ i)) last-index) + (let ((count (aref counts i)) + (coverage (aref coverages i)) + (col (save-excursion + (goto-char (+ (aref edebug-points i) def-mark)) + (- (current-column) + (if (= ?\( (following-char)) 0 1))))) + (insert (make-string + (max 0 (- col (- (point) start-of-count-line))) ?\ ) + (if (and (< 0 count) + (not (memq coverage + '(unknown ok-coverage)))) + "=" "") + (if (= count last-count) "" (int-to-string count)) + " ") + (setq last-count count))) + (insert "\n") + (setq i first-index))))) + +(defun edebug-temp-display-freq-count () + "Temporarily display the frequency count data for the current definition. +It is removed when you hit any char." + ;; This seems not to work with Emacs 18.59. It undoes too far. + (interactive) + (let ((buffer-read-only nil)) + (undo-boundary) + (edebug-display-freq-count) + (setq unread-command-char (read-char)) + (undo))) + + +;;;; Menus +;;;========= + +(defun edebug-toggle (variable) + (set variable (not (eval variable))) + (message "%s: %s" variable (eval variable))) + +;; We have to require easymenu (even for Emacs 18) just so +;; the easy-menu-define macro call is compiled correctly. +(require 'easymenu) + +(defconst edebug-mode-menus + '("Edebug" + "----" + ["Stop" edebug-stop t] + ["Step" edebug-step-mode t] + ["Next" edebug-next-mode t] + ["Trace" edebug-trace-mode t] + ["Trace Fast" edebug-Trace-fast-mode t] + ["Continue" edebug-continue-mode t] + ["Continue Fast" edebug-Continue-fast-mode t] + ["Go" edebug-go-mode t] + ["Go Nonstop" edebug-Go-nonstop-mode t] + "----" + ["Help" edebug-help t] + ["Abort" abort-recursive-edit t] + ["Quit to Top Level" top-level t] + ["Quit Nonstop" edebug-top-level-nonstop t] + "----" + ("Jumps" + ["Forward Sexp" edebug-forward-sexp t] + ["Step In" edebug-step-in t] + ["Step Out" edebug-step-out t] + ["Goto Here" edebug-goto-here t]) + + ("Breaks" + ["Set Breakpoint" edebug-set-breakpoint t] + ["Unset Breakpoint" edebug-unset-breakpoint t] + ["Set Conditional Breakpoint" edebug-set-conditional-breakpoint t] + ["Set Global Break Condition" edebug-set-global-break-condition t] + ["Show Next Breakpoint" edebug-next-breakpoint t]) + + ("Views" + ["Where am I?" edebug-where t] + ["Bounce to Current Point" edebug-bounce-point t] + ["View Outside Windows" edebug-view-outside t] + ["Previous Result" edebug-previous-result t] + ["Show Backtrace" edebug-backtrace t] + ["Display Freq Count" edebug-display-freq-count t]) + + ("Eval" + ["Expression" edebug-eval-expression t] + ["Last Sexp" edebug-eval-last-sexp t] + ["Visit Eval List" edebug-visit-eval-list t]) + + ("Options" + ["Edebug All Defs" edebug-all-defs t] + ["Edebug All Forms" edebug-all-forms t] + "----" + ["Toggle Tracing" (edebug-toggle 'edebug-trace) t] + ["Toggle Coverage Testing" (edebug-toggle 'edebug-test-coverage) t] + ["Toggle Window Saving" edebug-toggle-save-windows t] + ["Toggle Point Saving" + (edebug-toggle 'edebug-save-displayed-buffer-points) t] + )) + "XEmacs style menus for Edebug.") + + +;;;; Emacs version specific code +;;;============================= +;;; The default for all above is Emacs 18, because it is easier to compile +;;; Emacs 18 code in Emacs 19 than vice versa. This default will +;;; change once most people are using Emacs 19 or derivatives. + +;; Epoch specific code is in a separate file: edebug-epoch.el. + +;; The byte-compiler will complain about changes in number of arguments +;; to functions like mark and read-from-minibuffer. These warnings +;; may be ignored because the right call should always be made. + +(defun edebug-emacs-19-specific () + + (defalias 'edebug-window-live-p 'window-live-p) + + ;; Mark takes an argument in Emacs 19. + (defun edebug-mark () + (mark t));; Does this work for XEmacs too? + + ;; Use minibuffer-history when reading expressions. + (defvar read-expression-history) ;; hush bytecomp + (defvar read-expression-map) + + (defun edebug-set-conditional-breakpoint (arg condition) + "Set a conditional breakpoint at nearest sexp. +The condition is evaluated in the outside context. +With prefix argument, make it a temporary breakpoint." + ;; (interactive "P\nxCondition: ") + (interactive + (list + current-prefix-arg + ;; Read condition as follows; getting previous condition is cumbersome: + (let ((edebug-stop-point (edebug-find-stop-point))) + (if edebug-stop-point + (let* ((edebug-def-name (car edebug-stop-point)) + (index (cdr edebug-stop-point)) + (edebug-data (get edebug-def-name 'edebug)) + (edebug-breakpoints (car (cdr edebug-data))) + (edebug-break-data (assq index edebug-breakpoints)) + (edebug-break-condition (car (cdr edebug-break-data))) + (edebug-expression-history + ;; Prepend the current condition, if any. + (if edebug-break-condition + (cons edebug-break-condition read-expression-history) + read-expression-history))) + (prog1 + (read-from-minibuffer + "Condition: " nil read-expression-map t + 'edebug-expression-history) + (setq read-expression-history edebug-expression-history) + )))))) + (edebug-modify-breakpoint t condition arg)) + + (defun edebug-eval-expression (edebug-expr) + "Evaluate an expression in the outside environment. +If interactive, prompt for the expression. +Print result in minibuffer." + (interactive (list (read-from-minibuffer + "Eval: " nil read-expression-map t + 'read-expression-history))) + (princ + (edebug-outside-excursion + (setq values (cons (edebug-eval edebug-expr) values)) + (edebug-safe-prin1-to-string (car values))))) + + (easy-menu-define edebug-menu edebug-mode-map "Edebug menus" edebug-mode-menus) + (if window-system + (x-popup-menu nil (lookup-key edebug-mode-map [menu-bar Edebug]))) + ) + + +(defun edebug-xemacs-specific () + + ;; We need to bind zmacs-regions to nil around all calls to `mark' and + ;; `mark-marker' but don't bind it to nil before entering a recursive edit, + ;; that is, don't interfere with the binding the user might see while + ;; executing a command. + + (defvar zmacs-regions) + + (defun edebug-mark () + (let ((zmacs-regions nil)) + (mark))) + + (defun edebug-mark-marker () + (let ((zmacs-regions nil));; for XEmacs + (mark-marker))) + + + (defun edebug-mode-menu (event) + (interactive "@event") + (popup-menu edebug-mode-menus)) + + (define-key edebug-mode-map 'button3 'edebug-mode-menu) + ) + +(defun edebug-emacs-version-specific () + (cond + ((string-match "XEmacs" emacs-version);; XEmacs + (edebug-xemacs-specific)) + + ((and (boundp 'epoch::version) epoch::version) + (require 'edebug-epoch)) + + ((not (string-match "^18" emacs-version)) + (edebug-emacs-19-specific)))) + +(edebug-emacs-version-specific) + + +;;;; Byte-compiler +;;; ==================== +;; Extension for bytecomp to resolve undefined function references. +;; Requires new byte compiler. + +;; Reenable byte compiler warnings about unread-command-char and -event. +;; Disabled before edebug-recursive-edit. +(eval-when-compile + (if edebug-unread-command-char-warning + (put 'unread-command-char 'byte-obsolete-variable + edebug-unread-command-char-warning)) + (if edebug-unread-command-event-warning + (put 'unread-command-event 'byte-obsolete-variable + edebug-unread-command-event-warning))) + +(eval-when-compile + ;; The body of eval-when-compile seems to get evaluated with eval-defun. + ;; We only want to evaluate when actually byte compiling. + ;; But it is OK to evaluate as long as byte-compiler has been loaded. + (if (featurep 'byte-compile) (progn + + (defun byte-compile-resolve-functions (funcs) + "Say it is OK for the named functions to be unresolved." + (mapcar + (function + (lambda (func) + (setq byte-compile-unresolved-functions + (delq (assq func byte-compile-unresolved-functions) + byte-compile-unresolved-functions)))) + funcs) + nil) + + '(defun byte-compile-resolve-free-references (vars) + "Say it is OK for the named variables to be referenced." + (mapcar + (function + (lambda (var) + (setq byte-compile-free-references + (delq var byte-compile-free-references)))) + vars) + nil) + + '(defun byte-compile-resolve-free-assignments (vars) + "Say it is OK for the named variables to be assigned." + (mapcar + (function + (lambda (var) + (setq byte-compile-free-assignments + (delq var byte-compile-free-assignments)))) + vars) + nil) + + (byte-compile-resolve-functions + '(reporter-submit-bug-report + edebug-gensym ;; also in cl.el + ;; Interfaces to standard functions. + edebug-original-eval-defun + edebug-original-read + edebug-get-buffer-window + edebug-mark + edebug-mark-marker + edebug-input-pending-p + edebug-sit-for + edebug-prin1-to-string + edebug-format + edebug-original-signal + ;; XEmacs + zmacs-deactivate-region + popup-menu + ;; CL + cl-macroexpand-all + ;; And believe it or not, the byte compiler doesnt know about: + byte-compile-resolve-functions + )) + + '(byte-compile-resolve-free-references + '(read-expression-history + read-expression-map)) + + '(byte-compile-resolve-free-assignments + '(read-expression-history)) + + ))) + + +;;;; Autoloading of Edebug accessories +;;;=================================== + +(if (featurep 'cl) + (add-hook 'edebug-setup-hook + (function (lambda () (require 'cl-specs)))) + ;; The following causes cl-specs to be loaded if you load cl.el. + (add-hook 'cl-load-hook + (function (lambda () (require 'cl-specs))))) + +;;; edebug-cl-read and cl-read are available from liberte@cs.uiuc.edu +(if (featurep 'cl-read) + (add-hook 'edebug-setup-hook + (function (lambda () (require 'edebug-cl-read)))) + ;; The following causes edebug-cl-read to be loaded when you load cl-read.el. + (add-hook 'cl-read-load-hooks + (function (lambda () (require 'edebug-cl-read))))) + + +;;;; Finalize Loading +;;;=================== + +;;; Finally, hook edebug into the rest of Emacs. +;;; There are probably some other things that could go here. + +;; Install edebug read and eval functions. +(edebug-install-read-eval-functions) + +(provide 'edebug) + +;;; edebug.el ends here + +