liberte@M.CS.UIUC.EDU (Daniel LaLiberte) (11/19/88)
Here is edebug.el. This is a debugger for GNU Emacs Lisp that allows you to step through code, set breakpoints by pointing at the text, etc. I am posting it because it appears to be reliable and I expect people will want to use it. I posted it to bug-gnu-emacs several months ago and didnt get any bug reports. Dan LaLiberte uiucdcs!liberte liberte@cs.uiuc.edu liberte%a.cs.uiuc.edu@uiucvmd.bitnet ================================================== ;; edebug.el electric-debug mode or elisp-debug mode. ;; Copyright (C) 1988 Free Software Foundation, Inc. and Daniel LaLiberte ;; This file is part of GNU Emacs. - or could be if they want it. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY. No author or distributor ;; accepts responsibility to anyone for the consequences of using it ;; or for whether it serves any particular purpose or works at all, ;; unless he says so in writing. Refer to the GNU Emacs General Public ;; License for full details. ;; Everyone is granted permission to copy, modify and redistribute ;; GNU Emacs, but only under the conditions described in the ;; GNU Emacs General Public License. A copy of this license is ;; supposed to have been given to you along with GNU Emacs so you ;; can know your rights and responsibilities. It should be in a ;; file named COPYING. Among other things, the copyright notice ;; and this notice must be preserved on all copies. ;;;================================================================ ;;; This mode allows the user to step through elisp source code ;;; while executing, set breakpoints, etc. It works very well so far, ;;; but below is a list of things still to do. Help yourself ;;; if you feel inclinded to make enhancements and ;;; let me know if you come up with any ideas, bugs, or fixes. ;;; Daniel LaLiberte 217-333-7937 ;;; University of Illinois, Urbana-Champaign ;;; Department of Computer Science ;;; 1304 W Springfield ;;; Urbana, IL 61801 ;;; uiucdcs!liberte ;;; liberte@cs.uiuc.edu ;;; liberte%a.cs.uiuc.edu@uiucvmd.bitnet ;;;======================================================================== ;;; To use edebug, simply evaluate a defun with edebug-defun. Before ;;; calling the function, the buffer that the function is defined in ;;; must be available. Then call the function normally. In the debugger ;;; try the "h" command for a list of commands. ;;; Initially, you will be in step mode which stops before every ;;; expression. Continue to the next step with " " or "d". ;;; You can change to trace mode, which pauses one second before each ;;; expression, by using the "t" command. "T" is a fast trace with ;;; zero pause time. You can set a breakpoint with "b" and clear it ;;; with "B" inside the debugger or "C-xXb" and "C-xXB" outside. ;;; "c" continues until a break. "C" continues ignoring breaks. ;;; During trace or continue modes, hit any character to interrupt. ;;; You can view the window configuration outside of edebug with "v" ;;; and pop back to the edebug windows with "w" in a edebug window or ;;; "C-xXw" in any window. Quit to top level with "q" or abort one ;;; level with "a". ;; Put the following two forms in your .emacs file. If you desire, change ;; the global-set-key to local-set-key inside your emacs-lisp-mode-hook. ;;(global-set-key "\^Xx" 'edebug-defun) ;;(autoload 'edebug-defun "edebug") ;; Use setq to change the value of global-edebug-prefix before loading ;; edebug.el. (defvar global-edebug-prefix "\^XX" "Prefix key for global edebug commands.") (defvar allow-recursive-debug t "*If non-nil, recursive calls to edebug are not questioned.") ;;;===================================================================== ;; todo list * means difficult; may require mods to emacs ;; ------------------------------------------------------- ;; Bug: occasionally, your elisp buffer will remain read-only. ;; *Annoyance: the debugging mode (trace, step, etc) is not reset on restart. ;; *Call edebug on exit from a function call. ;; *Previous result command. ;; Command to not descend but step to next call at same or higher level. ;; Maybe augment mark-list with next-at-same-level pointers. ;; *Maybe use some trick with backtrace-debug. ;; Make it work with selective display - dont stop in hidden lines ;; Command to display backtrace. ;; Handle macros? ;; Handle interactive form if the argument is not a string. ;; Per-function debugging mode? ;; Customizable default modes. ;; Should overlay-arrow-position and -string be buffer local? ;; Explicit calls to edebug in user code. ;; Use just one mark per function; compute relative position. ;; Or allow arbitrary editing of buffer - switch to outside keymap. ;; *Need to be able to recognize if a command has been executed at ;; a lower level since edebug was last called. ;; Remember window configuration inside debugger between edebug calls ;; and remember original configuration on the first call to edebug ;; after an interactive command at a lower level. ;; Write a replacement for, or fix, pop-to-buffer to avoid window-hopping. ;; Use copy of current-local-map instead of emacs-lisp-mode-map ;; (but only copy the first time after lower level command - to save time). ;; *Better integration with standard debug. ;; *Catch errors that would go to debug if in an edebug function. ;; Use inhibit-quit while edebugging? ;; Crawl mode would sit-for 0 or 1 in the outside window configuration ;; between each edebug step. ;; Maybe it should be a separate option that applies to trace as well. ;; Customizable sit-for time for trace and crawl modes. ;; Pause and continue at breakpoints if in continue mode? ;; (be sure to catch keyboard interrupt in time). ;; Conditional breakpoints. Enter expression to be evaluated. ;; Counting conditions - nth time through. ;; Environmental conditions - like nesting depth. ;; minibuffer mode - show the current line in the minibuffer instead. ;; Trace to buffer mode - print enclosing function and function at point. ;; Performance monitoring - summarize trace data. ;; Check that buffers havent disappeared under us. ;; List side effects of using edebug, e.g. last-command, undo, etc. ;; Handle expressions outside of defuns. Store mark-list in buffer-local var. ;; Debug just one subexpression of a defun - the rest is evalled normally. ;; Speed up edebug-defun. ;; *Investigate whether eval could do what edebug-defun does, only alot faster. ;; Shadow eval-defun or eval-region temporarily. ;;;====================================================================== ;;; The first half of this file is the edebug compiler, edebug-defun. ;;; The second half is the debugger, edebug and edebug-mode. ;;; Some general utilities are at the end. ;(setq debug-on-error t) (defun edebug-defun () "Evaluate the defun that point is in or before, but set it up for edebug. Print its name in the minibuffer." (interactive) (let ((tmp-point (point)) (defun-name) (defun-args) (defun-doc-string) (defun-interactive) (parse-sexp-ignore-comments t) sexp token (edebug-sexp-count 0) (edebug-mark-list nil) ) (save-excursion (end-of-defun) (beginning-of-defun) (down-list 1) (if (not (eq 'defun (edebug-read-one))) (error "Not in a defun")) (setq defun-name (edebug-read-one)) (if (not (symbolp defun-name)) (error "Bad defun name.")) (setq defun-args (edebug-read-one)) (if (not (listp defun-args)) (error "Bad arg list.")) (setq tmp-point (point)) (if (eq 'atom (edebug-next-token-class)) (setq token (edebug-read-one))) (if (stringp token) (progn (setq defun-doc-string token) (setq tmp-point (point)) )) (if (eq 'lparen (edebug-next-token-class)) (progn (forward-char 1) (if (eq 'atom (edebug-next-token-class)) (progn (setq token (edebug-read-one)) (goto-char tmp-point) (if (eq 'interactive token) (progn (setq defun-interactive (edebug-read-one)) (setq tmp-point (point)) ) ))))) (goto-char tmp-point) ;; the remainder is a list of sexps (fset defun-name (` (lambda (, defun-args) (, defun-doc-string) (, defun-interactive) (,@ (edebug-sexp-list t)) ))) ) (put defun-name 'edebug (cons nil (vconcat (nreverse edebug-mark-list))) ) (message "edebug: %s" defun-name) )) (defun edebug-sexp () "Return the debug form for the following sexp. Move point to immediately after the sexp." (let (sexp class mark count ) (setq mark (point-marker) count (1- (setq edebug-sexp-count (1+ edebug-sexp-count))) edebug-mark-list (cons mark edebug-mark-list)) (setq sexp (cond ((eq 'lparen (edebug-next-token-class)) (edebug-form)) (t (edebug-read-one)) )) (list 'progn (list 'edebug (list 'quote defun-name) count) sexp) )) (defun edebug-sexp-list (debuggable) "Return a list built from the sexp list following point in the current buffer. If DEBUGGABLE then wrap edebug calls around each sexp. Leave point before the trailing right paren." (let ((sexp-list) sexp class ) (while (not (eq 'rparen (setq class (edebug-next-token-class)))) (setq sexp (if debuggable (edebug-sexp) (edebug-read-one))) ;; (message "sexp after debug: %s" sexp) (sit-for 2) (setq sexp-list (cons sexp sexp-list)) ) (nreverse sexp-list) )) (defun edebug-form () "Return a list built from the following list form with debug calls inserted as appropriate to the form. Leave point after the right paren." (let ((beginning (point)) token) (forward-char 1) ; skip \( (if (eq 'atom (edebug-next-token-class)) (setq token (edebug-read-one)) (error "Bad form.")) ;; (message "head token: %s" token) (sit-for 2) (prog1 (cons token (cond ;; handle all special cases with unevaluated arguments - any more?? ((memq token '(let let*)) (edebug-let)) ((memq token '(setq setq-default)) (edebug-setq)) ((eq token 'cond) (edebug-cond)) ((eq token 'condition-case) (edebug-condition-case)) ((memq token '(quote function defun defvar defconst defmacro)) (edebug-sexp-list nil)) ;; is it a lisp macro? ((macrop token) (edebug-sexp-list nil)) (t (edebug-sexp-list t)) )) (forward-char 1) ; skip \) ) )) (defun edebug-let () (let (var-value-list token class) (cons ;; first process the var/value list (if (not (eq 'lparen (edebug-next-token-class))) (edebug-read-one) (forward-char 1) ; lparen (while (not (eq 'rparen (setq class (edebug-next-token-class)))) (setq var-value-list (cons (if (not (eq 'lparen class)) (edebug-read-one) (forward-char 1) ; lparen (prog1 (edebug-var-value) (forward-char 1)) ; rparen ) var-value-list))) (forward-char 1) ; rparen (nreverse var-value-list) ) ;; now process the expression list (progn (edebug-next-token) (edebug-sexp-list t)) ))) (defun edebug-var-value () "Return the debug form of the var and value that follow point." (list (edebug-read-one) ; the variable (and (not (eq 'rparen (edebug-next-token-class))) (edebug-sexp)) )) (defun edebug-setq () "Return the debug form of the setq var-value list." (let (var-value-list) (while (not (eq 'rparen (edebug-next-token-class))) (setq var-value-list (append var-value-list (edebug-var-value)))) var-value-list )) (defun edebug-cond () "Return the debug form of the cond condition-expressionlist pairs." (let (value-value-list class) (while (not (eq 'rparen (setq class (edebug-next-token-class)))) (setq value-value-list (cons (if (not (eq 'lparen class)) (error "Bad condition in cond") (forward-char 1) ; \( (prog1 (list (edebug-sexp) (if (eq 'rparen (edebug-next-token-class)) nil (edebug-sexp-list t))) (if (not (eq 'rparen (edebug-next-token-class))) (error "Bad condition in cond")) (forward-char 1) ; \) )) value-value-list)) ) (nreverse value-value-list) )) (defun edebug-condition-case () "Return the debug form of a condition-case." (let (symb-sexp-list class) (cons (if (not (eq 'atom (edebug-next-token-class))) (error "Bad variable in condition-case") (edebug-read-one) (edebug-next-token) ) (cons (edebug-sexp) ; the form ;; process handlers (progn (while (not (eq 'rparen (setq class (edebug-next-token-class)))) (setq symb-sexp-list (cons (if (not (eq 'lparen class)) (error "Bad handler in condition-case") (forward-char 1) ; \( (prog1 (edebug-var-value) (forward-char 1)) ; \) ) symb-sexp-list))) (nreverse symb-sexp-list) ))))) ;;------------------------------------------------ ;; Parser utilities (defun edebug-next-token () "Leave point before the next token skipping comments." (skip-chars-forward " \t\r\n\f") (while (= (following-char) ?\;) (end-of-line) (skip-chars-forward " \t\r\n\f") )) (defun edebug-read-one () "Read one sexp from the current buffer starting at point. Leave point immediately after it. A sexp can be a list or atom (symbol, character, string, vector). Sexps are translated to internal form." ;; This is gummed up by parser inconsistencies (bugs?) (let ((parse-sexp-ignore-comments t)) (edebug-next-token) (if (= (following-char) ?\[) ; scan-sexps doesnt read vectors correctly (read (current-buffer)) ; but read does (prog1 (save-excursion ;; read goes one too far if (quoted) string or symbol ;; is immediately followed by non-whitespace (read (current-buffer))) (goto-char (scan-sexps (point) 1))) ))) (defun edebug-next-token-class () "Move to the next token and return its class. We only care about parens, dot, quote, and (anything else) atom." (edebug-next-token) (cond ((= (following-char) ?\() 'lparen) ((= (following-char) ?\)) 'rparen) ((= (following-char) ?\.) 'dot) ((= (following-char) ?\') 'quote) (t 'atom))) ;;; The next two defs are not used any more, but make interesting reading. ;;; (defvar edebug-defun-name nil ;;; "Global variable for use by edebug-wrapper since names are bound ;;; at byte-compile time.") ;;; defmacros must appear before macro calls in order to byte-compile. ;;; (defmacro edebug-wrapper (count sexp) ;;; "Wrap the COUNTth SEXP in a call to edebug. ;;; Uses global edebug-defun-name." ;;; (` (list 'progn ;;; (list 'edebug (list 'quote '(, edebug-defun-name)) (, count)) ;;; (, sexp)) ;;; )) ;;;================================================================= ;;; The debugger itself ;;; add minor-mode-alist entry (or (assq 'edebug-active minor-mode-alist) (setq minor-mode-alist (cons (list 'edebug-active " *EDebugging*") minor-mode-alist))) (defconst edebug-arrow-alist '((continue . "*>") (trace . "->") (fast . ">") (step . "=>")) "Association list of arrows for each edebug mode.") (defvar edebug-depth 0 "Current debug editing depth.") ;; These variables need to be maintained between edebug calls. ;; But recursive edebug calls could confuse them. (defvar edebug-window-start 0 "Remember where each buffers' window starts. This is to avoid spurious recentering and also to auto adjust window start.") ;; what about a buffer with more than one window? (setq-default edebug-window-start 0) (make-variable-buffer-local 'edebug-window-start) (defvar edebug-inside-window-configuration nil "Configuration of debugger windows.") (defvar edebug-outside-window-configuration nil "Configuration of windows before debugger.") ;;------------------------------------------------- (defun edebug (edebug-func edebug-mark-index) "Debug FUNC. The position of the current sexp is given by MARK-INDEX which is used to index a mark in the mark vector in the functions 'edebug property. edebug is called from functions compiled with edebug-defun." ;; remember, nothing is safe until save-excursion etc. (if (or (= 0 edebug-depth) allow-recursive-debug (y-or-n-p "Recursive debug?? ")) (if (and (eq edebug-mode 'sleep) (not (input-pending-p))) nil ;; some variables are declared here to allow recursive debugging (let ((edebug-match-data (match-data)) edebug-outside-map ; keymap before edebug (edebug-outside-buffer (current-buffer)) edebug-read-only (edebug-outside-point (point)) (edebug-active t) edebug-buffer (edebug-step-after-exit nil) (cursor-in-echo-area nil) ; ?? (edebug-data (get edebug-func 'edebug)) edebug-breakpoints edebug-break ; true if we should break now edebug-mark edebug-recursive (edebug-outside-arrow-position overlay-arrow-position) (edebug-outside-arrow-string overlay-arrow-string) ) (save-excursion (unwind-protect (progn (setq edebug-outside-window-configuration (current-window-configuration)) (setq edebug-depth (1+ edebug-depth)) (setq edebug-breakpoints (car edebug-data)) (setq edebug-mark (aref (cdr edebug-data) edebug-mark-index)) (setq edebug-buffer (marker-buffer edebug-mark)) (setq edebug-break (memq edebug-mark-index edebug-breakpoints)) (if (or (not (eq 'continue edebug-mode)) edebug-break (input-pending-p)) (progn (if (input-pending-p) (progn ;;(setq edebug-mode 'step) (setq edebug-recursive t) (message "Interrupted") ;;(discard-input) ; can we use this input safely? ;;(sit-for 1) )) ;;(make-local-variable 'overlay-arrow-position) ;;(make-local-variable 'overlay-arrow-string) ;;; Dont use the following since we dont know if ;;; this is the first time in debug since last command at lower level. ;;; (if edebug-inside-window-configuration ;;; (set-window-configuration ;;; edebug-inside-window-configuration)) ;; avoid window-hopping for > 2 windows ;; doesnt catch all cases yet (if (and (not (get-buffer-window edebug-buffer)) (not (eq (selected-window) (next-window (next-window (selected-window)))))) (progn ; (message "flipping windows") (sit-for 2) (select-window (get-lru-window)) )) ;; a substitute for pop-to-buffer might be simpler (pop-to-buffer edebug-buffer) (set-buffer edebug-buffer) (goto-char edebug-mark) ;; adjust window to fit as much as possible (set-window-start (selected-window) edebug-window-start) (if (not (pos-visible-in-window-p)) (let ((start (window-start))) (set-window-start (selected-window) (setq edebug-window-start (save-excursion (forward-line (if (< edebug-mark start) -1 (- (/ (window-height) 2)))) (beginning-of-line) (point)))))) (edebug-overlay-arrow) ;; (sit-for 0) (if (and (not edebug-break) (memq edebug-mode '(trace fast))) (if (eq edebug-mode 'trace) (sit-for 1) ; parameterize time (sit-for 0)) (if (and edebug-break (not (eq 'step edebug-mode))) (message "Break")) (setq edebug-recursive t) ))) (if edebug-recursive (progn (edebug-mode) (setq edebug-read-only buffer-read-only) (let ((buffer-read-only t)) ; could make this optional (setq edebug-inside-window-configuration (current-window-configuration)) (recursive-edit) (set-buffer edebug-buffer) ; may have changed buffers ))) ) ;; unwind-protect cleanup (if edebug-recursive (progn (save-excursion (set-buffer edebug-buffer) (use-local-map edebug-outside-map)) (if (not (eq buffer-read-only edebug-read-only)) (error "This should not happen! Read only changed from %s to %s" edebug-read-only buffer-read-only)) )) ;; (message "Unwinding. depth=%d" (recursion-depth)) (sit-for 2) (restore-match-data edebug-match-data) (setq edebug-depth (1- edebug-depth)) (set-window-configuration edebug-outside-window-configuration) (setq overlay-arrow-position edebug-outside-arrow-position) (setq overlay-arrow-string edebug-outside-arrow-string) ) ; unwind-protect ))))) (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-mode." (let* ((pos)) (save-excursion (beginning-of-line) (setq pos (point))) (setq overlay-arrow-string (cdr (assq edebug-mode edebug-arrow-alist))) (setq overlay-arrow-position (make-marker)) (set-marker overlay-arrow-position pos (current-buffer)) )) (defun edebug-modify-breakpoint (flag) "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." ;; must find the right debugger data first (let (defun-name edebug-data edebug-breakpoints) (save-excursion (end-of-defun) (beginning-of-defun) (down-list 1) (forward-sexp 1) (setq defun-name (read (current-buffer)))) (setq edebug-data (get defun-name 'edebug)) (if (not edebug-data) (error "%s must first be evaluated with edebug-defun." defun-name)) (setq edebug-breakpoints (car edebug-data)) (let ((mark-vector (cdr edebug-data)) (pnt (point)) len i) ;; assume that the marks are in order (setq len (length mark-vector)) (setq i 0) (while (and (< i len) (> pnt (aref mark-vector i))) (setq i (1+ i))) (if (<= pnt (aref mark-vector i)) (progn ; found it (setq edebug-breakpoints (delq i edebug-breakpoints)) (if flag (progn (setq edebug-breakpoints (cons i edebug-breakpoints)) (message "Breakpoint set.")) (message "Breakpoint cleared.")) (setq edebug-data (cons edebug-breakpoints mark-vector)) (put defun-name 'edebug edebug-data) (goto-char (aref mark-vector i)) )) ))) (defun edebug-set-breakpoint () "Set the breakpoint of sexp following point." (interactive) (edebug-modify-breakpoint t)) (defun edebug-clear-breakpoint () "Clear the breakpoint of sexp following point." (interactive) (edebug-modify-breakpoint nil)) (defun edebug-step-through () "Proceed to next debug step." (interactive) (setq edebug-mode 'step) (if (< 0 (recursion-depth)) (if (eq (current-buffer) edebug-buffer) (exit-recursive-edit)) (message "edebug will stop before next eval."))) (defun edebug-sleep () "Continue, evaluating without debugging." (interactive) (message "Sleep...") (setq edebug-mode 'sleep) (if (< 0 (recursion-depth)) (if (eq (current-buffer) edebug-buffer) (exit-recursive-edit)) (message "edebug will sleep through breaks."))) (defun edebug-continue () "Continue, evaluating until break." (interactive) (message "Continue...") (setq edebug-mode 'continue) (if (< 0 (recursion-depth)) (if (eq (current-buffer) edebug-buffer) (exit-recursive-edit)) (message "edebug will continue until break." ))) (defun edebug-trace-fast () "Trace with no wait at each step." (interactive) (message "Trace fast...") (setq edebug-mode 'fast) (if (< 0 (recursion-depth)) (if (eq (current-buffer) edebug-buffer) (exit-recursive-edit)) (message "edebug will trace without pause."))) (defun edebug-trace () "Begin trace mode." (interactive) (message "Tracing...") (sit-for 1) ; or delay time (setq edebug-mode 'trace) (if (< 0 (recursion-depth)) (if (eq (current-buffer) edebug-buffer) (exit-recursive-edit)) (message "edebug will trace with pause."))) (defun edebug-where () "Show the debug windows and where we stopped in program." (interactive) (set-window-configuration edebug-inside-window-configuration) (set-buffer edebug-buffer) (select-window (get-buffer-window edebug-buffer)) ; is this needed? (goto-char edebug-mark) (message "Window configuration inside of edebug.") ) (defun edebug-view-outside () "Show the windows before edebug was called." (interactive) (set-window-configuration edebug-outside-window-configuration) (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 () "Show the point in the outside current buffer by bouncing. If the buffer has a current window, then dont do a set-window-configuration" (interactive) (save-excursion (if (not (get-buffer-window edebug-outside-buffer)) (progn (set-window-configuration edebug-outside-window-configuration) (sit-for 1) (set-window-configuration edebug-inside-window-configuration) ) (save-window-excursion (select-window (get-buffer-window edebug-outside-buffer)) (goto-char edebug-outside-point) (sit-for 0) ; I want to sit one second after update is finished! (sit-for 1)) ))) (defun edebug-interrupt () "Useful for exiting from trace loop." (interactive) (message "Interrupted") (ding t)) (defun edebug-eval-expression (exp) "Prompt and evaluate an expression" (interactive "xEval: ") (save-excursion (if (null (buffer-name edebug-outside-buffer)) ;; outside buffer deleted (setq edebug-outside-buffer (current-buffer))) (set-buffer edebug-outside-buffer) (eval-expression exp))) ;;;------------------ ;;; edebug-mode stuff (defvar edebug-mode-map nil) (if edebug-mode-map nil (let ((loop ? )) ; what's this for? ;; should use copy of current local map (setq edebug-mode-map (copy-keymap emacs-lisp-mode-map)) ;; (suppress-keymap edebug-mode-map) (define-key edebug-mode-map "-" 'negative-argument) (define-key edebug-mode-map "c" 'edebug-continue) (define-key edebug-mode-map "C" 'edebug-sleep) (define-key edebug-mode-map "t" 'edebug-trace) (define-key edebug-mode-map "T" 'edebug-trace-fast) (define-key edebug-mode-map " " 'edebug-step-through) (define-key edebug-mode-map "d" 'edebug-step-through) (define-key edebug-mode-map "w" 'edebug-where) (define-key edebug-mode-map "v" 'edebug-view-outside) (define-key edebug-mode-map "p" 'edebug-bounce-point) (define-key edebug-mode-map "h" 'edebug-help) (define-key edebug-mode-map "?" 'edebug-help) (define-key edebug-mode-map "q" 'top-level) (define-key edebug-mode-map "a" 'abort-recursive-edit) (define-key edebug-mode-map "e" 'edebug-eval-expression) (define-key edebug-mode-map "b" 'edebug-set-breakpoint) (define-key edebug-mode-map "B" 'edebug-clear-breakpoint) (define-key edebug-mode-map "i" 'edebug-interrupt) (define-key edebug-mode-map "\^g" 'edebug-interrupt) )) (defvar global-edebug-map (make-sparse-keymap)) (define-key global-edebug-map "d" 'edebug-step-through) (define-key global-edebug-map "c" 'edebug-continue) (define-key global-edebug-map "C" 'edebug-sleep) (define-key global-edebug-map "t" 'edebug-trace) (define-key global-edebug-map "T" 'edebug-trace-fast) (define-key global-edebug-map "b" 'edebug-set-breakpoint) (define-key global-edebug-map "B" 'edebug-clear-breakpoint) (define-key global-edebug-map "w" 'edebug-where) (global-set-key global-edebug-prefix global-edebug-map) (defvar edebug-mode 'step "Current edebug mode set by user.") (defun edebug-help () (interactive) (describe-function 'edebug-mode)) (defun edebug-mode () "Mode for elisp buffers while in edebug. Under construction. There are both buffer local and global key bindings to several functions. E.g. edebug-step-through is bound to \\[edebug-step-through] in the debug buffer and \\<global-map>\\[edebug-step-through] in any buffer. Global commands prefixed by global-edbug-prefix: \\{global-edebug-map} Debugger buffer commands \\{edebug-mode-map} " (setq edebug-outside-map (current-local-map)) (use-local-map edebug-mode-map) ) ;;;-------------------- ;;; Utilities (defun window-list () "Return a list of window, in order of next-window." (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 restore-match-data (data) "Restore the match data DATA safely." (catch 'foo (let ((d data)) (while d (and (car d) (null (marker-buffer (car d))) ;; match-data buffer is deleted. (throw 'foo nil)) (setq d (cdr d))) (store-match-data data) ))) (defun macrop (object) "Return true if OBJECTs function is a lisp macro, directly or indirectly." (while (and (symbolp object) (fboundp object)) (setq object (symbol-function object))) (and (listp object) (eq 'macro (car object))) )
grunwald@m.cs.uiuc.edu (11/21/88)
re: edebug I'd like to give a testimonial for ``edebug''. It makes debugging so easy that I now use Emacs for things where I used to use Awk, sed, etc. It drastically reduces debugging time, cures warts and is fun to use. Dirk ``A Statisfied Customer'' Grunwald