summaryrefslogtreecommitdiff
path: root/lisp
diff options
context:
space:
mode:
authorEric S. Raymond <esr@snark.thyrsus.com>1992-05-30 23:52:26 +0000
committerEric S. Raymond <esr@snark.thyrsus.com>1992-05-30 23:52:26 +0000
commit71a3e39f3fa808f4d120bcc53ca3d3da9a69c12f (patch)
tree4d489471b306b24c46ce6dd534f9455f3dd63929 /lisp
parent1c5bd65eeb5e36c59e4dc0b74dd89118f07b599c (diff)
downloademacs-71a3e39f3fa808f4d120bcc53ca3d3da9a69c12f.tar.gz
Initial revision
Diffstat (limited to 'lisp')
-rw-r--r--lisp/add-log.el103
-rw-r--r--lisp/dired.el785
-rw-r--r--lisp/emacs-lisp/edebug.el2521
3 files changed, 3409 insertions, 0 deletions
diff --git a/lisp/add-log.el b/lisp/add-log.el
new file mode 100644
index 00000000000..f2c279ae357
--- /dev/null
+++ b/lisp/add-log.el
@@ -0,0 +1,103 @@
+;;; add-log.el --- change log maintenance commands for Emacs
+
+;; Copyright (C) 1985-1991 Free Software Foundation, Inc.
+
+;; 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 1, 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.
+
+
+;;;###autoload
+(defvar change-log-default-name nil
+ "*Name of a change log file for \\[add-change-log-entry].")
+
+(defun change-log-name ()
+ (or change-log-default-name
+ (if (eq system-type 'vax-vms) "$CHANGE_LOG$.TXT" "ChangeLog")))
+
+(defun prompt-for-change-log-name ()
+ "Prompt for a change log name."
+ (let ((default (change-log-name)))
+ (expand-file-name
+ (read-file-name (format "Log file (default %s): " default)
+ nil default))))
+
+;;;###autoload
+(defun add-change-log-entry (&optional whoami file-name other-window)
+ "Find change log file and add an entry for today.
+Optional arg (interactive prefix) non-nil means prompt for user name and site.
+Second arg is file name of change log. If nil, uses `change-log-default-name'.
+Third arg OTHER-WINDOW non-nil means visit in other window."
+ (interactive (list current-prefix-arg
+ (prompt-for-change-log-name)))
+ (let* ((full-name (if whoami
+ (read-input "Full name: " (user-full-name))
+ (user-full-name)))
+ ;; Note that some sites have room and phone number fields in
+ ;; full name which look silly when inserted. Rather than do
+ ;; anything about that here, let user give prefix argument so that
+ ;; s/he can edit the full name field in prompter if s/he wants.
+ (login-name (if whoami
+ (read-input "Login name: " (user-login-name))
+ (user-login-name)))
+ (site-name (if whoami
+ (read-input "Site name: " (system-name))
+ (system-name))))
+ (or file-name
+ (setq file-name (or change-log-default-name
+ default-directory)))
+ (if (file-directory-p file-name)
+ (setq file-name (concat (file-name-as-directory file-name)
+ (change-log-name))))
+ (set (make-local-variable 'change-log-default-name) file-name)
+ (if (and other-window (not (equal file-name buffer-file-name)))
+ (find-file-other-window file-name)
+ (find-file file-name))
+ (undo-boundary)
+ (goto-char (point-min))
+ (if (not (and (looking-at (substring (current-time-string) 0 10))
+ (looking-at (concat ".* " full-name " (" login-name "@"))))
+ (progn (insert (current-time-string)
+ " " full-name
+ " (" login-name
+ "@" site-name ")\n\n")))
+ (goto-char (point-min))
+ (forward-line 1)
+ (while (looking-at "\\sW")
+ (forward-line 1))
+ (delete-region (point)
+ (progn
+ (skip-chars-backward "\n")
+ (point)))
+ (open-line 3)
+ (forward-line 2)
+ (indent-to left-margin)
+ (insert "* ")))
+
+;;;###autoload
+(define-key ctl-x-4-map "a" 'add-change-log-entry-other-window)
+
+;;;###autoload
+(defun add-change-log-entry-other-window (&optional whoami file-name)
+ "Find change log file in other window and add an entry for today.
+First arg (interactive prefix) non-nil means prompt for user name and site.
+Second arg is file name of change log.
+Interactively, with a prefix argument, the file name is prompted for."
+ (interactive (if current-prefix-arg
+ (list current-prefix-arg
+ (prompt-for-change-log-name))))
+ (add-change-log-entry whoami file-name t))
+
+;;; add-log.el ends here
diff --git a/lisp/dired.el b/lisp/dired.el
new file mode 100644
index 00000000000..16a86f72b26
--- /dev/null
+++ b/lisp/dired.el
@@ -0,0 +1,785 @@
+;;; dired.el --- DIRED commands for Emacs
+
+;;; Missing: P command, sorting, setting file modes.
+;;; Dired buffer containing multiple directories gets totally confused
+;;; Implement insertion of subdirectories in situ --- tree dired
+
+;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.
+
+;; 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 1, 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.
+
+
+;;;###autoload
+(defvar dired-listing-switches "-al" "\
+Switches passed to ls for dired. MUST contain the `l' option.
+CANNOT contain the `F' option.")
+
+(defvar dired-chown-program
+ (if (memq system-type '(hpux usg-unix-v))
+ "/bin/chown" "/etc/chown")
+ "Pathname of chown command.")
+
+(defvar dired-directory nil)
+
+(defun dired-readin (dirname buffer)
+ (save-excursion
+ (message "Reading directory %s..." dirname)
+ (set-buffer buffer)
+ (let ((buffer-read-only nil))
+ (widen)
+ (erase-buffer)
+ (setq dirname (expand-file-name dirname))
+ (if (eq system-type 'vax-vms)
+ (vms-read-directory dirname dired-listing-switches buffer)
+ (if (file-directory-p dirname)
+ (call-process "ls" nil buffer nil
+ dired-listing-switches dirname)
+ (if (not (file-readable-p (directory-file-name (file-name-directory dirname))))
+ (insert "Directory " dirname " inaccessible or nonexistent.\n")
+ (let ((default-directory (file-name-directory dirname)))
+ (call-process shell-file-name nil buffer nil
+ "-c" (concat "ls -d " dired-listing-switches " "
+ (file-name-nondirectory dirname)))))))
+ (goto-char (point-min))
+ (indent-rigidly (point-min) (point-max) 2))
+ (set-buffer-modified-p nil)
+ (message "Reading directory %s...done" dirname)))
+
+(defun dired-find-buffer (dirname)
+ (let ((blist (buffer-list))
+ found)
+ (while blist
+ (save-excursion
+ (set-buffer (car blist))
+ (if (and (eq major-mode 'dired-mode)
+ (equal dired-directory dirname))
+ (setq found (car blist)
+ blist nil)
+ (setq blist (cdr blist)))))
+ (or found
+ (create-file-buffer (directory-file-name dirname)))))
+
+;;;###autoload
+(defun dired (dirname)
+ "\"Edit\" directory DIRNAME--delete, rename, print, etc. some files in it.
+Dired displays the list of files in DIRNAME.
+You can move around in it with the usual movement commands.
+You can flag files for deletion with \\<dired-mode-map>\\[dired-flag-file-deleted]
+and then delete them by typing `x'.
+Type `h' after entering dired for more info."
+ (interactive (list (read-file-name "Dired (directory): "
+ nil default-directory nil)))
+ (switch-to-buffer (dired-noselect dirname)))
+;;;###autoload
+(define-key ctl-x-map "d" 'dired)
+
+;;;###autoload
+(defun dired-other-window (dirname)
+ "\"Edit\" directory DIRNAME. Like `dired' but selects in another window."
+ (interactive (list (read-file-name "Dired in other window (directory): "
+ nil default-directory nil)))
+ (switch-to-buffer-other-window (dired-noselect dirname)))
+;;;###autoload
+(define-key ctl-x-4-map "d" 'dired-other-window)
+
+;;;###autoload
+(defun dired-noselect (dirname)
+ "Like `dired' but returns the dired buffer as value, does not select it."
+ (or dirname (setq dirname default-directory))
+ (setq dirname (expand-file-name (directory-file-name dirname)))
+ (if (file-directory-p dirname)
+ (setq dirname (file-name-as-directory dirname)))
+ (let ((buffer (dired-find-buffer dirname)))
+ (save-excursion
+ (set-buffer buffer)
+ (dired-readin dirname buffer)
+ (while (and (not (dired-move-to-filename)) (not (eobp)))
+ (forward-line 1))
+ (dired-mode dirname))
+ buffer))
+
+(defun dired-revert (&optional arg noconfirm)
+ (let ((opoint (point))
+ (ofile (dired-get-filename t t))
+ (buffer-read-only nil)
+ delete-list already-deleted column-dots)
+ (goto-char 1)
+ (if (re-search-forward "^D" nil t)
+ (progn
+ (beginning-of-line)
+ (while (re-search-forward "^D" nil t)
+ (setq delete-list (cons (dired-get-filename t) delete-list)))))
+ (dired-readin dired-directory (current-buffer))
+ (while (and (not (dired-move-to-filename)) (not (eobp)))
+ (forward-line 1))
+ (setq column-dots (concat "^" (make-string (current-column) ?.))
+ delete-list (nreverse delete-list))
+ (while delete-list
+ ;; assumptions: the directory was reread with the files listed in the
+ ;; same order as they were originally. the string of "."s is rather silly
+ ;; but it seems the fastest way to avoid messing with -F flags and
+ ;; matches that occur in places other than the filename column
+ (if (re-search-forward
+ (concat column-dots (regexp-quote (car delete-list))) nil t)
+ (progn (beginning-of-line)
+ (delete-char 1)
+ (insert "D"))
+ (setq already-deleted (cons (car delete-list) already-deleted)))
+ (setq delete-list (cdr delete-list)))
+ (goto-char 0)
+ (or (and ofile (re-search-forward (concat column-dots (regexp-quote ofile))
+ nil t))
+ (goto-char opoint))
+ (dired-move-to-filename)
+ (if already-deleted (message "Already deleted: %s"
+ (prin1-to-string (reverse already-deleted))))))
+
+(defvar dired-mode-map nil "Local keymap for dired-mode buffers.")
+(if dired-mode-map
+ nil
+ (setq dired-mode-map (make-keymap))
+ (suppress-keymap dired-mode-map)
+ (define-key dired-mode-map "r" 'dired-rename-file)
+ (define-key dired-mode-map "\C-d" 'dired-flag-file-deleted)
+ (define-key dired-mode-map "d" 'dired-flag-file-deleted)
+ (define-key dired-mode-map "v" 'dired-view-file)
+ (define-key dired-mode-map "e" 'dired-find-file)
+ (define-key dired-mode-map "f" 'dired-find-file)
+ (define-key dired-mode-map "o" 'dired-find-file-other-window)
+ (define-key dired-mode-map "u" 'dired-unflag)
+ (define-key dired-mode-map "x" 'dired-do-deletions)
+ (define-key dired-mode-map "\177" 'dired-backup-unflag)
+ (define-key dired-mode-map "?" 'dired-summary)
+ (define-key dired-mode-map "c" 'dired-copy-file)
+ (define-key dired-mode-map "#" 'dired-flag-auto-save-files)
+ (define-key dired-mode-map "~" 'dired-flag-backup-files)
+ (define-key dired-mode-map "F" 'dired-flag-regexp-files)
+ (define-key dired-mode-map "." 'dired-clean-directory)
+ (define-key dired-mode-map "h" 'describe-mode)
+ (define-key dired-mode-map " " 'dired-next-line)
+ (define-key dired-mode-map "\C-n" 'dired-next-line)
+ (define-key dired-mode-map "\C-p" 'dired-previous-line)
+ (define-key dired-mode-map "n" 'dired-next-line)
+ (define-key dired-mode-map "p" 'dired-previous-line)
+ (define-key dired-mode-map "g" 'revert-buffer)
+ (define-key dired-mode-map "D" 'dired-create-directory)
+ (define-key dired-mode-map "m" 'dired-move-file)
+ (define-key dired-mode-map "C" 'dired-compress)
+ (define-key dired-mode-map "U" 'dired-uncompress)
+ (define-key dired-mode-map "B" 'dired-byte-recompile)
+ (define-key dired-mode-map "M" 'dired-chmod)
+ (define-key dired-mode-map "G" 'dired-chgrp)
+ (define-key dired-mode-map "O" 'dired-chown)
+ (define-key dired-mode-map "=" 'dired-diff)
+ (define-key dired-mode-map "<" 'dired-up-directory))
+
+
+;; Dired mode is suitable only for specially formatted data.
+(put 'dired-mode 'mode-class 'special)
+
+(defun dired-mode (&optional dirname)
+ "Mode for \"editing\" directory listings.
+In dired, you are \"editing\" a list of the files in a directory.
+You can move using the usual cursor motion commands.
+Letters no longer insert themselves.
+Instead, use the following commands:
+\\{dired-mode-map}"
+ (interactive)
+ (kill-all-local-variables)
+ (make-local-variable 'revert-buffer-function)
+ (setq revert-buffer-function 'dired-revert)
+ (setq major-mode 'dired-mode)
+ (setq mode-name "Dired")
+ (make-local-variable 'dired-directory)
+ (setq dired-directory (or dirname default-directory))
+ (make-local-variable 'list-buffers-directory)
+ (setq list-buffers-directory dired-directory)
+ (set (make-local-variable 'dired-used-F)
+ (string-match "F" dired-listing-switches))
+ (if dirname
+ (setq default-directory
+ (if (file-directory-p dirname)
+ dirname (file-name-directory dirname))))
+ (setq mode-line-buffer-identification '("Dired: %17f"))
+ (setq case-fold-search nil)
+ (setq buffer-read-only t)
+ (use-local-map dired-mode-map)
+ (run-hooks 'dired-mode-hook))
+
+;; FUNCTION receives no arguments
+;; and should return t iff it deletes the current line from the buffer.
+(defun dired-repeat-over-lines (arg function)
+ (beginning-of-line)
+ (while (and (> arg 0) (not (eobp)))
+ (setq arg (1- arg))
+ (let (deleted)
+ (save-excursion
+ (beginning-of-line)
+ (and (bobp) (looking-at " total")
+ (error "No file on this line"))
+ (setq deleted (funcall function)))
+ (or deleted
+ (forward-line 1)))
+ (dired-move-to-filename))
+ (while (and (< arg 0) (not (bobp)))
+ (setq arg (1+ arg))
+ (forward-line -1)
+ (dired-move-to-filename)
+ (save-excursion
+ (beginning-of-line)
+ (funcall function))))
+
+(defun dired-flag-file-deleted (arg)
+ "In dired, flag the current line's file for deletion.
+With prefix arg, repeat over several lines."
+ (interactive "p")
+ (dired-repeat-over-lines arg
+ '(lambda ()
+ (let ((buffer-read-only nil))
+ (delete-char 1)
+ (insert "D")
+ nil))))
+
+(defun dired-flag-regexp-files (regexp)
+ "In dired, flag all files matching the specified REGEXP for deletion."
+ (interactive "sFlagging regexp: ")
+ (save-excursion
+ (let ((buffer-read-only nil))
+ (goto-char (point-min))
+ (while (not (eobp))
+ (and (not (looking-at " d"))
+ (not (eolp))
+ (let ((fn (dired-get-filename t t)))
+ (if fn (string-match regexp fn)))
+ (progn (beginning-of-line)
+ (delete-char 1)
+ (insert "D")))
+ (forward-line 1)))))
+
+(defun dired-summary ()
+ (interactive)
+ ;>> this should check the key-bindings and use substitute-command-keys if non-standard
+ (message
+ "d-elete, u-ndelete, x-ecute, f-ind, o-ther window, r-ename, c-opy, v-iew"))
+
+(defun dired-unflag (arg)
+ "In dired, remove the current line's delete flag then move to next line."
+ (interactive "p")
+ (dired-repeat-over-lines arg
+ '(lambda ()
+ (let ((buffer-read-only nil))
+ (delete-char 1)
+ (insert " ")
+ (forward-char -1)
+ nil))))
+
+(defun dired-backup-unflag (arg)
+ "In dired, move up lines and remove deletion flag there.
+Optional prefix ARG says how many lines to unflag; default is one line."
+ (interactive "p")
+ (dired-unflag (- arg)))
+
+(defun dired-next-line (arg)
+ "Move down lines then position at filename.
+Optional prefix ARG says how many lines to move; default is one line."
+ (interactive "p")
+ (next-line arg)
+ (dired-move-to-filename))
+
+(defun dired-previous-line (arg)
+ "Move up lines then position at filename.
+Optional prefix ARG says how many lines to move; default is one line."
+ (interactive "p")
+ (previous-line arg)
+ (dired-move-to-filename))
+
+(defun dired-up-directory ()
+ "Run dired on the parent of the current directory."
+ (interactive)
+ (find-file ".."))
+
+(defun dired-find-file ()
+ "In dired, visit the file or directory named on this line."
+ (interactive)
+ (find-file (dired-get-filename)))
+
+(defun dired-view-file ()
+ "In dired, examine a file in view mode, returning to dired when done."
+ (interactive)
+ (if (file-directory-p (dired-get-filename))
+ (dired (dired-get-filename))
+ (view-file (dired-get-filename))))
+
+(defun dired-find-file-other-window ()
+ "In dired, visit this file or directory in another window."
+ (interactive)
+ (find-file-other-window (dired-get-filename)))
+
+(defun dired-get-filename (&optional localp no-error-if-not-filep)
+ "In dired, return name of file mentioned on this line.
+Value returned normally includes the directory name.
+Optional arg LOCALP means don't include it.
+Optional arg NO-ERROR-IF-NOT-FILEP means return nil if no filename
+on this line, otherwise an error occurs."
+ (let (eol file type ex (case-fold-search nil))
+ (save-excursion
+ (end-of-line)
+ (setq eol (point))
+ (beginning-of-line)
+ (if (eq system-type 'vax-vms)
+ ;; Non-filename lines don't match
+ ;; because they have lower case letters.
+ (if (re-search-forward "^..\\([][.A-Z-0-9_$;<>]+\\)" eol t)
+ (setq file (buffer-substring (match-beginning 1) (match-end 1))))
+ ;; Unix case
+ (if (not (re-search-forward
+ "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec\\)[ ]+[0-9]+"
+ eol t)) ()
+ (skip-chars-forward " ")
+ (skip-chars-forward "^ " eol)
+ (skip-chars-forward " " eol)
+ (setq file (buffer-substring (point) eol))
+ (re-search-backward "\\(.\\)[-r][-w]\\(.\\)[-r][-w]\\(.\\)[-r][-w]\\(.\\)")
+ (setq flag (buffer-substring (match-beginning 1) (match-end 1))
+ ex (string-match "[xst]" ;; execute bit set anywhere?
+ (concat
+ (buffer-substring (match-beginning 2) (match-end 2))
+ (buffer-substring (match-beginning 3) (match-end 3))
+ (buffer-substring (match-beginning 4) (match-end 4)))))
+ (cond
+ ((string= flag "l")
+ ;; strip the link name. Bombs if file contains " ->"
+ (if (string-match " ->" file)
+ (setq file (substring file 0 (match-beginning 0)))))
+ ((and dired-used-F ;; strip off -F stuff if there
+ (or (string= flag "d") (string= flag "s") ex))
+ (setq file (substring file 0 -1)))))))
+ (or no-error-if-not-filep file
+ (error "No file on this line"))
+ ;; ??? uses default-directory, could lose on cd, multiple.
+ (or localp (setq file (expand-file-name file default-directory)))
+ file))
+
+(defun dired-move-to-filename ()
+ "In dired, move to first char of filename on this line.
+Returns position (point) or nil if no filename on this line."
+ (let ((eol (progn (end-of-line) (point))))
+ (beginning-of-line)
+ (if (re-search-forward
+ "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec\\)[ ]+[0-9]+"
+ eol t)
+ (progn
+ (skip-chars-forward " ")
+ (skip-chars-forward "^ " eol)
+ (skip-chars-forward " " eol)
+ (point)))))
+
+(defun dired-map-dired-file-lines (fn)
+ "Perform function FN with point at the end of each non-directory line.
+The arguments given to FN are the short and long filename"
+ (save-excursion
+ (let (filename longfilename (buffer-read-only nil))
+ (goto-char (point-min))
+ (while (not (eobp))
+ (save-excursion
+ (and (not (looking-at " \\s *[0-9]*\\s *[0-9]* d"))
+ (not (eolp))
+ (setq filename (dired-get-filename t t)
+ longfilename (dired-get-filename nil t))
+ (progn (end-of-line)
+ (funcall fn filename longfilename))))
+ (forward-line 1)))))
+
+(defun dired-flag-auto-save-files (unflag-p)
+ "Flag for deletion files whose names suggest they are auto save files.
+A prefix argument says to unflag those files instead."
+ (interactive "P")
+ (save-excursion
+ (let ((buffer-read-only nil))
+ (goto-char (point-min))
+ (while (not (eobp))
+ (and (not (looking-at " \\s *[0-9]*\\s *[0-9]* d"))
+ (not (eolp))
+ (if (fboundp 'auto-save-file-name-p)
+ (let ((fn (dired-get-filename t t)))
+ (if fn (auto-save-file-name-p fn)))
+ (if (dired-move-to-filename)
+ (looking-at "#")))
+ (progn (beginning-of-line)
+ (delete-char 1)
+ (insert (if unflag-p " " "D"))))
+ (forward-line 1)))))
+
+(defun dired-clean-directory (keep)
+ "Flag numerical backups for deletion.
+Spares `dired-kept-versions' latest versions, and `kept-old-versions' oldest.
+Positive prefix arg KEEP overrides `dired-kept-versions';
+Negative prefix arg KEEP overrides `kept-old-versions' with KEEP made positive.
+
+To clear the flags on these files, you can use \\[dired-flag-backup-files]
+with a prefix argument."
+ (interactive "P")
+ (setq keep (if keep (prefix-numeric-value keep) dired-kept-versions))
+ (let ((early-retention (if (< keep 0) (- keep) kept-old-versions))
+ (late-retention (if (<= keep 0) dired-kept-versions keep))
+ (file-version-assoc-list ()))
+ ;; Look at each file.
+ ;; If the file has numeric backup versions,
+ ;; put on file-version-assoc-list an element of the form
+ ;; (FILENAME . VERSION-NUMBER-LIST)
+ (dired-map-dired-file-lines 'dired-collect-file-versions)
+ ;; Sort each VERSION-NUMBER-LIST,
+ ;; and remove the versions not to be deleted.
+ (let ((fval file-version-assoc-list))
+ (while fval
+ (let* ((sorted-v-list (cons 'q (sort (cdr (car fval)) '<)))
+ (v-count (length sorted-v-list)))
+ (if (> v-count (+ early-retention late-retention))
+ (rplacd (nthcdr early-retention sorted-v-list)
+ (nthcdr (- v-count late-retention)
+ sorted-v-list)))
+ (rplacd (car fval)
+ (cdr sorted-v-list)))
+ (setq fval (cdr fval))))
+ ;; Look at each file. If it is a numeric backup file,
+ ;; find it in a VERSION-NUMBER-LIST and maybe flag it for deletion.
+ (dired-map-dired-file-lines 'dired-trample-file-versions)))
+
+(defun dired-collect-file-versions (ignore fn)
+ "If it looks like file FN has versions, return a list of the versions.
+That is a list of strings which are file names.
+The caller may want to flag some of these files for deletion."
+ (let* ((base-versions
+ (concat (file-name-nondirectory fn) ".~"))
+ (bv-length (length base-versions))
+ (possibilities (file-name-all-completions
+ base-versions
+ (file-name-directory fn)))
+ (versions (mapcar 'backup-extract-version possibilities)))
+ (if versions
+ (setq file-version-assoc-list (cons (cons fn versions)
+ file-version-assoc-list)))))
+
+(defun dired-trample-file-versions (ignore fn)
+ (let* ((start-vn (string-match "\\.~[0-9]+~$" fn))
+ base-version-list)
+ (and start-vn
+ (setq base-version-list ; there was a base version to which
+ (assoc (substring fn 0 start-vn) ; this looks like a
+ file-version-assoc-list)) ; subversion
+ (not (memq (string-to-int (substring fn (+ 2 start-vn)))
+ base-version-list)) ; this one doesn't make the cut
+ (dired-flag-this-line-for-DEATH))))
+
+(defun dired-flag-this-line-for-DEATH ()
+ (beginning-of-line)
+ (delete-char 1)
+ (insert "D"))
+
+(defun dired-flag-backup-files (unflag-p)
+ "Flag all backup files (names ending with `~') for deletion.
+With prefix argument, unflag these files."
+ (interactive "P")
+ (save-excursion
+ (let ((buffer-read-only nil))
+ (goto-char (point-min))
+ (while (not (eobp))
+ (and (not (looking-at " d"))
+ (not (eolp))
+ (if (fboundp 'backup-file-name-p)
+ (let ((fn (dired-get-filename t t)))
+ (if fn (backup-file-name-p fn)))
+ (end-of-line)
+ (forward-char -1)
+ (looking-at "~"))
+ (progn (beginning-of-line)
+ (delete-char 1)
+ (insert (if unflag-p " " "D"))))
+ (forward-line 1)))))
+
+(defun dired-flag-backup-and-auto-save-files (unflag-p)
+ "Flag all backup and temporary files for deletion.
+Backup files have names ending in `~'.
+Auto save file names usually start with `#'.
+With prefix argument, unflag these files."
+ (interactive "P")
+ (dired-flag-backup-files unflag-p)
+ (dired-flag-auto-save-files unflag-p))
+
+(defun dired-create-directory (directory)
+ "Create a directory called DIRECTORY."
+ (interactive "FCreate directory: ")
+ (let ((expanded (expand-file-name directory)))
+ (make-directory expanded)
+ (dired-add-entry (file-name-directory expanded)
+ (file-name-nondirectory expanded))
+ (dired-next-line 1)))
+
+(defun dired-move-file (to-dir &optional count)
+ "Move this file to directory TO-DIR.
+Optional second argument COUNT (the prefix argument)
+specifies moving several consecutive files."
+ (interactive
+ (let ((count (prefix-numeric-value current-prefix-arg)))
+ (list (read-file-name (format "Move %s to directory: "
+ (if (> count 1)
+ (format "%d files" count)
+ (file-name-nondirectory (dired-get-filename))))
+ nil t)
+ count)))
+ (let ((dir (file-name-as-directory (expand-file-name to-dir))))
+ (dired-repeat-over-lines
+ count
+ (function (lambda ()
+ (let ((this (dired-get-filename)))
+ (rename-file this
+ (expand-file-name (file-name-nondirectory this)
+ dir)))
+ (let ((buffer-read-only nil))
+ (beginning-of-line)
+ (delete-region (point) (progn (forward-line 1) (point))))
+ t)))))
+
+(defun dired-rename-file (to-file)
+ "Rename the current file to TO-FILE."
+ (interactive
+ (list (read-file-name (format "Rename %s to: "
+ (file-name-nondirectory (dired-get-filename)))
+ nil (dired-get-filename))))
+ (setq to-file (expand-file-name to-file))
+ (let ((filename (dired-get-filename))
+ (buffer-read-only nil))
+ (rename-file filename to-file)
+ (beginning-of-line)
+ (delete-region (point) (progn (forward-line 1) (point)))
+ (setq to-file (expand-file-name to-file))
+ (dired-add-entry (file-name-directory to-file)
+ (file-name-nondirectory to-file))
+ ;; Optionally rename the visited file of any buffer visiting this file.
+ (and (get-file-buffer filename)
+ (y-or-n-p (message "Change visited file name of buffer %s too? "
+ (buffer-name (get-file-buffer filename))))
+ (save-excursion
+ (set-buffer (get-file-buffer filename))
+ (let ((modflag (buffer-modified-p)))
+ (set-visited-file-name to-file)
+ (set-buffer-modified-p modflag))))))
+
+(defun dired-copy-file (to-file)
+ "Copy the current file to TO-FILE."
+ (interactive "FCopy to: ")
+ (copy-file (dired-get-filename) to-file)
+ (setq to-file (expand-file-name to-file))
+ (dired-add-entry (file-name-directory to-file)
+ (file-name-nondirectory to-file)))
+
+(defun dired-add-entry (directory filename)
+ ;; If tree dired is implemented, this function will have to do
+ ;; something smarter with the directory. Currently, just check
+ ;; default directory, if same, add the new entry at point. With tree
+ ;; dired, should call 'dired-current-directory' or similar. Note
+ ;; that this adds the entry 'out of order' if files sorted by time,
+ ;; etc.
+ (if (string-equal directory default-directory)
+ (let ((buffer-read-only nil))
+ (beginning-of-line)
+ (call-process "ls" nil t nil
+ "-d" dired-listing-switches (concat directory filename))
+ (forward-line -1)
+ (insert " ")
+ (dired-move-to-filename)
+ (let* ((beg (point))
+ (end (progn (end-of-line) (point))))
+ (setq filename (buffer-substring beg end))
+ (delete-region beg end)
+ (insert (file-name-nondirectory filename)))
+ (beginning-of-line))))
+
+(defun dired-diff (point mark)
+ "Compare files at POINT1 and POINT2 by running `diff'.
+Interactively, these are the files at point and mark.
+The file at mark (POINT2) is the first file given to `diff'.
+See the command `diff'."
+ (interactive "d\nm")
+ (let (name1 name2)
+ (setq name2 (dired-get-filename))
+ (save-excursion
+ (goto-char mark)
+ (setq name1 (dired-get-filename)))
+ (diff name1 name2)))
+
+(defun dired-compress ()
+ "Compress the current file."
+ (interactive)
+ (let* ((buffer-read-only nil)
+ (error-buffer (get-buffer-create " *Dired compress output*"))
+ (from-file (dired-get-filename))
+ (to-file (concat from-file ".Z")))
+ (if (string-match "\\.Z$" from-file)
+ (error "%s is already compressed!" from-file))
+ (message "Compressing %s..." from-file)
+ (unwind-protect
+ (progn
+ (save-excursion
+ (set-buffer error-buffer)
+ (erase-buffer))
+ ;; Must have default-directory of dired buffer in call-process
+ (call-process "compress" nil error-buffer nil "-f" from-file)
+ (if (save-excursion
+ (set-buffer error-buffer)
+ (= 0 (buffer-size)))
+ (progn
+ (message "Compressing %s... done" from-file)
+ (kill-buffer error-buffer))
+ (display-buffer error-buffer)
+ (setq error-buffer nil)
+ (error "Compress error on %s." from-file)))
+ (if error-buffer (kill-buffer error-buffer)))
+ (dired-redisplay to-file)))
+
+(defun dired-uncompress ()
+ "Uncompress the current file."
+ (interactive)
+ (let* ((buffer-read-only nil)
+ (error-buffer (get-buffer-create " *Dired compress output*"))
+ (from-file (dired-get-filename))
+ (to-file (substring from-file 0 -2)))
+ (if (string-match "\\.Z$" from-file) nil
+ (error "%s is not compressed!" from-file))
+ (message "Uncompressing %s..." from-file)
+ (unwind-protect
+ (progn
+ (save-excursion
+ (set-buffer error-buffer)
+ (erase-buffer))
+ ;; Must have default-directory of dired buffer in call-process
+ (call-process "uncompress" nil error-buffer nil "-f" from-file)
+ (if (save-excursion
+ (set-buffer error-buffer)
+ (= 0 (buffer-size)))
+ (progn
+ (message "Uncompressing %s... done" from-file)
+ (kill-buffer error-buffer))
+ (display-buffer error-buffer)
+ (setq error-buffer nil)
+ (error "Uncompress error on %s." from-file)))
+ (if error-buffer (kill-buffer error-buffer)))
+ (dired-redisplay to-file)))
+
+(defun dired-byte-recompile ()
+ "Byte recompile the current file."
+ (interactive)
+ (let* ((buffer-read-only nil)
+ (from-file (dired-get-filename))
+ (to-file (substring from-file 0 -3)))
+ (if (string-match "\\.el$" from-file) nil
+ (error "%s is uncompilable!" from-file))
+ (byte-compile-file from-file)))
+
+(defun dired-chmod (mode)
+ "Change mode of the current file to MODE."
+ (interactive "sChange to Mode: ")
+ (let ((buffer-read-only nil)
+ (file (dired-get-filename)))
+ (call-process "/bin/chmod" nil nil nil mode file)
+ (dired-redisplay file)))
+
+(defun dired-chgrp (group)
+ "Change group of the current file to GROUP."
+ (interactive "sChange to Group: ")
+ (let ((buffer-read-only nil)
+ (file (dired-get-filename)))
+ (call-process "/bin/chgrp" nil nil nil group file)
+ (dired-redisplay file)))
+
+(defun dired-chown (owner)
+ "Change owner of the current file to OWNER."
+ (interactive "sChange to Owner: ")
+ (let ((buffer-read-only nil)
+ (file (dired-get-filename)))
+ (call-process dired-chown-program
+ nil nil nil owner file)
+ (dired-redisplay file)))
+
+(defun dired-redisplay (&optional file)
+ "Delete the current line, and insert an entry for file FILE.
+If FILE is nil, then just delete the current line."
+ (beginning-of-line)
+ (delete-region (point) (progn (forward-line 1) (point)))
+ (if file (dired-add-entry (file-name-directory file)
+ (file-name-nondirectory file)))
+ (dired-move-to-filename))
+
+(defun dired-do-deletions ()
+ "In dired, delete the files flagged for deletion."
+ (interactive)
+ (let (delete-list answer)
+ (save-excursion
+ (goto-char 1)
+ (while (re-search-forward "^D" nil t)
+ (setq delete-list
+ (cons (cons (dired-get-filename t) (1- (point)))
+ delete-list))))
+ (if (null delete-list)
+ (message "(No deletions requested)")
+ (save-window-excursion
+ (set-buffer (get-buffer-create " *Deletions*"))
+ (funcall (if (> (length delete-list) (* (window-height) 2))
+ 'switch-to-buffer 'switch-to-buffer-other-window)
+ (current-buffer))
+ (erase-buffer)
+ (setq fill-column 70)
+ (let ((l (reverse delete-list)))
+ ;; Files should be in forward order for this loop.
+ (while l
+ (if (> (current-column) 59)
+ (insert ?\n)
+ (or (bobp)
+ (indent-to (* (/ (+ (current-column) 19) 20) 20) 1)))
+ (insert (car (car l)))
+ (setq l (cdr l))))
+ (goto-char (point-min))
+ (setq answer (yes-or-no-p "Delete these files? ")))
+ (if answer
+ (let ((l delete-list)
+ failures)
+ ;; Files better be in reverse order for this loop!
+ ;; That way as changes are made in the buffer
+ ;; they do not shift the lines still to be changed.
+ (while l
+ (goto-char (cdr (car l)))
+ (let ((buffer-read-only nil))
+ (condition-case ()
+ (let ((fn (concat default-directory (car (car l)))))
+ (if (file-directory-p fn)
+ (progn
+ (remove-directory fn)
+ (if (file-exists-p fn) (delete-file fn)))
+ (delete-file fn))
+ (delete-region (point)
+ (progn (forward-line 1) (point))))
+ (error (delete-char 1)
+ (insert " ")
+ (setq failures (cons (car (car l)) failures)))))
+ (setq l (cdr l)))
+ (if failures
+ (message "Deletions failed: %s"
+ (prin1-to-string failures))))))))
+
+(provide 'dired)
+
+;;; dired.el ends here
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
new file mode 100644
index 00000000000..d586367933b
--- /dev/null
+++ b/lisp/emacs-lisp/edebug.el
@@ -0,0 +1,2521 @@
+;;; edebug.el --- a source-level debugger for emacs lisp.
+
+;; Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc
+
+;; This file is part of GNU Emacs.
+
+;; 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 minor mode allows programmers to step through elisp source
+;;; code while executing, set breakpoints, etc. See the texinfo
+;;; document (being constructed...) for more detailed instructions
+;;; than contained here. Send me your enhancement, ideas, bugs, or
+;;; fixes.
+
+;;; Daniel LaLiberte 217-244-0785
+;;; University of Illinois, Urbana-Champaign
+;;; Department of Computer Science
+;;; 1304 W Springfield
+;;; Urbana, IL 61801
+
+;;; uiucdcs!liberte
+;;; liberte@cs.uiuc.edu
+
+;;; Contents:
+;;; =========
+;;; Change list
+;;; Installation
+;;; Todo list
+;;; Utilities
+;;; Parser
+;;; Debugger
+
+
+;;;================================================================
+;;; Change list
+;;; -----------
+
+;;; $Header: /import/kaplan/kaplan/liberte/Edebug/RCS/edebug.el,v 2.5 91/07/25 13:32:53 liberte Exp Locker: liberte $
+;;; $Log: edebug.el,v $
+;;; Revision 2.5 91/07/25 13:32:53 liberte
+;;; Doc string cleanup.
+;;; If edebug-form-hook is t, evaluate all arguments.
+;;; If edebug-form-hook is 0, evaluate no arguments.
+;;; If edebug-form-hook is nil, evaluate macro args according
+;;; to edebug-eval-macro-args.
+;;; Save the outside value of executing macro.
+;;; Save and restore the outside restriction.
+;;; Dont force update for go and Go-nonstop.
+;;; Save and restore last-command-char, last-command,
+;;; this-command, last-input-char.
+;;; For epoch, do epoch::dispatch-events before sit-for
+;;; and input-pending-p since X events could interfere.
+;;; Warn about unsetting non-existent breakpoint.
+;;; Fix edebug-forward-sexp with prefix arg.
+;;; Add edebug-step-out to exit from current sexp.
+;;;
+;;; Revision 2.4 91/03/18 12:35:44 liberte
+;;; Force update after go or Go-nonstop modes, so overlay arrow is correct.
+;;; Support debug-on-quit. Remove edebug-on-error.
+;;; Fix edebug-anonymous. Bug found by jackr@wpd.sgi.com (Jack Repenning).
+;;; Don't discard-input anymore. Easier to change modes this way.
+;;; Fix max-lisp-eval-depth and max-specpdl-size incrementing.
+;;; Save and restore points in all buffers, if
+;;; edebug-save-buffer-points is non-nil. Expensive!
+;;; Bug caught by wolfgang@wsrcc.com (Wolfgang S. Rupprecht)
+;;; Save standard-output and standard-input in edebug-recursive-edit
+;;; so that edebug-outside-excursion can restore them.
+;;; Call set-buffer in edebug-pop-to-buffer since
+;;; select-window does not do that.
+;;; Fix edebug's eval-defun to remember current buffer inside evaluations
+;;; and to evaluate top-level forms. Found by Jamie Zawinski.
+;;; Add edebug-interactive-entry to support interactive forms with
+;;; non-string arg. Bug found by Jack Repenning.
+;;; Simplify edebug-restore-match-data to just store-match-data.
+;;; Motivated by linus@lysator.liu.se.
+;;; Move the match-data call to before the outside
+;;; buffer is changed, since it assumes that.
+;;;
+;;; Revision 2.3 91/01/17 20:55:14 liberte
+;;; Fix bug found by hollen@megatek.uucp.
+;;; Current buffer was not being restored.
+;;; Call edebug with (edebug begin end 'exp)
+;;; and add additional wrapper around body of functions:
+;;; (edebug-enter function body).
+;;; Make &optional only apply to immediate next arg
+;;; in edebug-form-parser (was edebug-macro-parser).
+;;; Catch debug errors with edebug. Yeah!
+;;; Reset edebug-mode on first function entry. Yeah!
+;;; Motivated by Dion Hollenbeck.
+;;; Add the missing bindings to the global-edebug-map.
+;;; eval-current-buffer now uses eval-region.
+;;; eval-region now does not narrow region.
+;;; Narrowing was the cause of the window-start being set wrong.
+;;; Reset edebug-mode only on
+;;; first entry of any function at each recursive-edit level.
+;;; Add edebug-backtrace, to generate cleaned up
+;;; backtrace. It doesnt "work" like the debug backtrace, however.
+;;; Require reselecting outside window even if
+;;; quit occurs, otherwise save-excursions may restore
+;;; buffer to the wrong window.
+;;;
+;;; Revision 2.2 90/11/26 21:14:22 liberte
+;;; Shadow eval-defun and eval-region. Toggle
+;;; edebugging with edebug-all-defuns.
+;;; Call edebug with (edebug 'function begin end 'exp)
+;;; Suggested by Jamie Zawinski <jwz@lucid.com>.
+;;; Add edebug-form-parser to process macro args.
+;;; Motivated by Darryl Okahata darrylo@hpnmxx.hp.com.
+;;; Fix by Roland McGrath <roland@ai.mit.edu>
+;;; to wrap body of edebug-save-restriction in progn.
+;;; Fix by Darryl Okahata <darrylo%hpnmd@hpcea.hp.com>
+;;; to add (set-window-hscroll (selected-window) 0) to
+;;; edebug-pop-to-buffer.
+;;;
+;;; Revision 2.1 90/11/16 21:55:35 liberte
+;;; Clean up.
+;;; Add edebug-form-hook to edebug macro calls. Thanks to Joe Wells.
+;;; edebug-forward-sexp uses step mode if no forward-sexp.
+;;;
+;;; Revision 2.0 90/11/14 22:30:54 liberte
+;;; Handle lambda forms, function, interactive evals, defmacro.
+;;; Clean up display for Epoch - save and restore screen configurations.
+;;; Note: epoch 3.2 broke set-window-configuration.
+;;; Also, sit-for pauses do not always work in epoch.
+;;; Display evaluations window.
+;;; Display result after expression evaluation.
+;;; Thanks to discussions with Shinichirou Sugou.
+;;; Conditional and temporary breakpoints.
+;;; Change "continue" to "go" mode and add different "continue" mode.
+;;; Option to stop before symbols.
+;;;
+;;; Fix by: Glen Ditchfield gjditchfield@violet.uwaterloo.ca
+;;; to handle ?# type chars.
+;;;
+;;; Revision 1.5 89/05/10 02:39:27 liberte
+;;; Fix condition-case expression lists.
+;;; Reorganize edebug.
+;;;
+;;; Revision 1.4 89/02/14 22:58:34 liberte
+;;; Fix broken breakpointing.
+;;; Temporarily widen elisp buffer during edebug.
+;;;
+;;; Revision 1.3 89/01/30 00:26:09 liberte
+;;; More bug fixes for cond and let.
+;;; Another parsing fix backquote.
+;;; Fix for lambda forms inside defuns.
+;;; Leave point at syntax error, mark at starting position.
+;;;
+;;; Revision 1.2 88/11/28 12:14:15 liberte
+;;; Bug fixes: cond construct didnt execute.
+;;; () in sexp list didnt parse
+;;; () as variable in condition-case didnt parse.
+;;;
+;;; Revision 1.1 88/11/28 12:11:27 liberte
+;;; Initial revision
+;;;
+
+
+;;; Installation
+;;; ------------
+;; Put edebug.el in some directory in your load-path and byte-compile it.
+
+;; Put the following forms in your .emacs file.
+;; (setq edebug-global-prefix "...whatever you want") ; default is C-xX
+;; (define-key emacs-lisp-mode-map "\^Xx" 'edebug-defun)
+;; (autoload 'edebug-defun "edebug")
+;; (autoload 'edebug-debug "edebug")
+;; (setq debugger 'edebug-debug)
+;; ... other options, described in the next section.
+
+;; Evaluate a defun for edebug with edebug-defun.
+;; Evaluate your function normally.
+;; Use the "?" command in edebug to describe other commands.
+;; See edebug.texinfo for more instructions.
+
+
+;;; Options
+;;; -------
+
+(defvar edebug-all-defuns nil
+ "*If non-nil, all defuns and defmacros evaluated will use edebug.
+eval-defun without prefix arg and eval-region will use edebug-defun.
+
+If nil, eval-region evaluates normally, but eval-defun with prefix arg
+uses edebug-defun. eval-region is called by eval-defun, eval-last-sexp,
+and eval-print-last-sexp.
+
+You may wish to make this variable local to each elisp buffer by calling
+(make-local-variable 'edebug-all-defuns) in your emacs-lisp-mode-hook.
+You can use the function edebug-all-defuns to toggle its value.")
+
+
+(defvar edebug-eval-macro-args nil
+ "*If non-nil, edebug will assume that all macro call arguments for
+macros that have no edebug-form-hook may be evaluated, otherwise it
+will not. To specify exceptions for macros that have some arguments
+evaluated and some not, you should specify an edebug-form-hook")
+
+(defvar edebug-stop-before-symbols nil
+ "*Non-nil causes edebug to stop before symbols as well as after.
+In any case, it is possible to stop before a symbol with a breakpoint or
+interrupt.")
+
+(defvar edebug-save-windows t
+ "*If non-nil, save and restore window configuration on edebug calls.
+It takes some time to save and restore, so if your program does not care
+what happens to the window configurations, it is better to set this
+variable to nil.")
+
+(defvar edebug-save-point t
+ "*If non-nil, save and restore the point and mark in source code buffers.")
+
+(defvar edebug-save-buffer-points nil
+ "*If non-nil, save and restore the points of all buffers, displayed or not.
+
+Saving and restoring buffer points 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 all the points is an expensive operation since it
+visits each buffer twice for each edebug call, so it is best to avoid
+it if you can.")
+
+(defvar edebug-initial-mode 'step
+ "*Global initial mode for edebug, if non-nil.
+This is used when edebug is first entered for each recursive-edit level.
+Possible values are nil (meaning keep using edebug-mode), step, go,
+Go-nonstop, trace, Trace-fast, continue, and Continue-fast.")
+
+(defvar edebug-trace nil
+ "*Non-nil if edebug should show 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-entry and
+edebug-print-trace-exit.")
+
+
+
+;;;========================================================================
+;;; Utilities
+;;; ---------
+
+(defun edebug-which-function ()
+ "Return the symbol of the function we are in"
+ (save-excursion
+ (end-of-defun)
+ (beginning-of-defun)
+ (down-list 1)
+ (if (not (memq (read (current-buffer)) '(defun defmacro)))
+ (error "Not in defun or defmacro."))
+ (read (current-buffer))))
+
+(defun edebug-last-sexp ()
+ "Return the last sexp before point in current buffer.
+Assumes elisp 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-get-buffer-points ()
+ "Return a list of buffer point pairs, for all buffers."
+ (save-excursion
+ (mapcar (function (lambda (buf)
+ (set-buffer buf)
+ (cons buf (point))))
+ (buffer-list))))
+
+(defun edebug-set-buffer-points ()
+ "Restore the buffer-points given by edebug-get-buffer-points."
+ (mapcar (function (lambda (buf-point)
+ (if (buffer-name (car buf-point)) ; still exists
+ (progn
+ (set-buffer (car buf-point))
+ (goto-char (cdr buf-point))))))
+ edebug-buffer-points))
+
+(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))))))
+
+(defun edebug-macrop (object)
+ "Return the macro named by OBJECT, or nil if it is not a macro."
+ (while (and (symbolp object) (fboundp object))
+ (setq object (symbol-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."
+ (while (and (symbolp object) (fboundp object))
+ (setq object (symbol-function object)))
+ (if (or (subrp 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))))))
+
+(put 'edebug-save-restriction 'edebug-form-hook
+ '(&rest form))
+
+(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))))))
+
+
+;;;=============================================================
+;;; Redefine eval-defun, eval-region, and eval-current-buffer.
+;;; -----------------------------------------------------------
+
+(defun edebug-all-defuns ()
+ "Toggle edebugging of all defuns and defmacros,
+not including those evaluated in the minibuffer, or during load."
+ (interactive)
+ (setq edebug-all-defuns (not edebug-all-defuns))
+ (message "Edebugging is %s." (if edebug-all-defuns "on" "off")))
+
+
+(if (not (fboundp 'edebug-emacs-eval-defun))
+ (fset 'edebug-emacs-eval-defun (symbol-function 'eval-defun)))
+;;(fset 'eval-defun (symbol-function 'edebug-emacs-eval-defun))
+
+(defun eval-defun (edebug-debug)
+ "Edebug replacement for eval-defun. Print value in the minibuffer.
+Evaluate the top-level form that point is in or before. Note:
+eval-defun normally evaluates any top-level form, not just defuns.
+
+Here are the differences from the standard eval-defun. If the prefix
+argument is the same as edebug-all-defuns (nil or non-nil), evaluate
+normally; otherwise edebug-defun is called to wrap edebug calls around
+evaluatable expressions in the defun or defmacro body. Also, the
+value printed by edebug-defun is not just the function name."
+ (interactive "P")
+ (let ((edebug-all-defuns
+ (not (eq (not edebug-debug) (not edebug-all-defuns)))))
+ (edebug-emacs-eval-defun nil)
+ ))
+
+
+(if (not (fboundp 'edebug-emacs-eval-region))
+ (fset 'edebug-emacs-eval-region (symbol-function 'eval-region)))
+;; (fset 'eval-region (symbol-function 'edebug-emacs-eval-region))
+
+(defun eval-region (edebug-e-r-start edebug-e-r-end
+ &optional edebug-e-r-output)
+ "Edebug replacement for eval-defun.
+Like eval-region, but call edebug-defun for defuns or defmacros.
+Also, this eval-region does not narrow to the region and
+if an error occurs, point is left at the error."
+ ;; One other piddling difference concerns whitespace after the expression.
+ (interactive "r")
+ (let ((standard-output (or edebug-e-r-output 'symbolp))
+ (edebug-e-r-pnt (point))
+ (edebug-e-r-buf (current-buffer))
+ (edebug-e-r-inside-buf (current-buffer))
+ ;; Mark the end because it may move.
+ (edebug-e-r-end-marker (set-marker (make-marker) edebug-e-r-end))
+ edebug-e-r-val
+ )
+ (goto-char edebug-e-r-start)
+ (edebug-skip-whitespace)
+ (while (< (point) edebug-e-r-end-marker)
+ (if (and edebug-all-defuns
+ (eq 'lparen (edebug-next-token-class))
+ (save-excursion
+ (forward-char 1) ; skip \(
+ (memq (edebug-read-sexp) '(defun defmacro))))
+ (progn
+ (edebug-defun)
+ ;; Potential problem: edebug-defun always prints name.
+ (forward-sexp 1) ; skip the defun
+ )
+ (if (and (eq 'lparen (edebug-next-token-class))
+ (save-excursion
+ (forward-char 1) ; skip \(
+ (memq (edebug-read-sexp) '(defun defmacro))))
+ ;; If it's a defun or defmacro, but not edebug-all-defuns
+ ;; reset the symbols edebug property to be just a marker at
+ ;; the definitions source code.
+ (put (edebug-which-function) 'edebug (point-marker)))
+
+ ;; Evaluate normally - after restoring the current-buffer.
+ (setq edebug-e-r-val (edebug-read-sexp))
+ (save-excursion
+ (set-buffer edebug-e-r-inside-buf)
+ (setq edebug-e-r-val (eval edebug-e-r-val))
+ ;; Remember current buffer for next time.
+ (setq edebug-e-r-inside-buf (current-buffer)))
+
+ (if edebug-e-r-output
+ (progn
+ (setq values (cons edebug-e-r-val values))
+ (if (eq standard-output t)
+ (prin1 edebug-e-r-val)
+ (print edebug-e-r-val))))
+ )
+ (goto-char
+ (min (max edebug-e-r-end-marker (point))
+ (progn (edebug-skip-whitespace) (point))))
+ ) ; while
+ (if (null edebug-e-r-output)
+ ;; do the save-excursion recovery
+ (progn
+ ;; but mark is not restored
+ (set-buffer edebug-e-r-buf)
+ (goto-char edebug-e-r-pnt)))
+ nil
+ ))
+
+
+;; The standard eval-current-buffer doesn't use eval-region.
+(if (not (fboundp 'edebug-emacs-eval-current-buffer))
+ (fset 'edebug-emacs-eval-current-buffer
+ (symbol-function 'eval-current-buffer)))
+;; (fset 'eval-current-buffer (symbol-function 'edebug-emacs-eval-current-buffer))
+
+(defun eval-current-buffer (&optional edebug-e-c-b-output)
+ "Call eval-region on the whole buffer."
+ (interactive)
+ (eval-region (point-min) (point-max) edebug-e-c-b-output))
+
+
+
+;;;======================================================================
+;;; The Parser
+;;; ----------
+
+;;; The top level function for parsing defuns is edebug-defun; 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
+;;; function edebug together with indexes into a table of positions of
+;;; all expressions. Thus an expression "exp" in function foo
+;;; becomes:
+
+;;; (edebug 1 2 'exp)
+
+;;; First point moved to to the beginning of exp (offset 1 of the
+;;; current function). Then the expression is evaluated and point is
+;;; moved to offset 2, at the end of exp.
+
+;;; The top 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 and edebug-enter
+;;; for more details.
+
+
+(defun edebug-defun ()
+ "Evaluate defun or defmacro, like eval-defun, but with edebug calls.
+Print its name in the minibuffer and leave point after any error it finds,
+with mark at the original point."
+ (interactive)
+ (let (def-kind ; whether defmacro or defun
+ def-name
+ def-args
+ def-docstring
+ defun-interactive
+ (edebug-offset-index 0)
+ edebug-offset-list
+ edebug-func-mark
+ (starting-point (point))
+ tmp-point
+ (parse-sexp-ignore-comments t))
+
+ (condition-case err
+ (progn
+ (end-of-defun)
+ (beginning-of-defun)
+ (down-list 1)
+
+ (setq edebug-func-mark (point-marker))
+ (if (not (eq 'defun (setq def-kind (edebug-read-sexp))))
+ (if (not (eq 'defmacro def-kind))
+ (edebug-syntax-error "%s is not a defun or defmacro."
+ def-kind)))
+ (setq def-name (edebug-read-sexp))
+ (if (not (symbolp def-name))
+ (edebug-syntax-error "Bad defun name: %s" def-name))
+ (setq def-args (edebug-read-sexp))
+ (if (not (listp def-args))
+ (edebug-syntax-error "Bad defun arg list: %s" def-args))
+
+ ;; look for doc string
+ (setq tmp-point (point))
+ (if (eq 'string (edebug-next-token-class))
+ (progn
+ (setq def-docstring (edebug-read-sexp))
+ (setq tmp-point (point))))
+
+ ;; look for interactive form
+ (if (eq 'lparen (edebug-next-token-class))
+ (progn
+ (forward-char 1) ; skip \(
+ (if (eq 'interactive (edebug-read-sexp))
+ (progn
+ (setq defun-interactive
+ (cons 'interactive (edebug-interactive)))
+ (forward-char 1) ; skip \)
+ (setq tmp-point (point))
+ ))))
+
+ (goto-char tmp-point)
+
+ ;; build the new definition
+ (fset def-name (` (lambda
+ (, def-args)
+ (, def-docstring)
+ (, defun-interactive)
+ ;; the remainder is a list of sexps
+ (edebug-enter
+ (quote (, def-name))
+ (quote (, def-args))
+ (quote (progn
+ (,@ (edebug-sexp-list t)))))
+ )))
+ ;; if it is a defmacro, prepend 'macro
+ (if (eq 'defmacro def-kind)
+ (fset def-name (cons 'macro (symbol-function def-name))))
+
+ ;; recover point, like save-excursion but only if no error occurs
+ (goto-char starting-point)
+
+ ;; store the offset list in functions property list
+ (put def-name 'edebug
+ (list edebug-func-mark
+ nil ; clear breakpoints
+ (vconcat (nreverse edebug-offset-list))))
+ (message "edebug: %s" def-name)
+ ) ; progn
+
+ (invalid-read-syntax
+ ;; Set mark at starting-point so user can return.
+ ;; Leave point at error.
+ (save-excursion
+ (goto-char starting-point)
+ (set-mark-command nil))
+ (message "Syntax error: %s" (cdr err))
+;; (signal 'invalid-read-syntax (cdr err)) ; pass it on, to who?
+ )
+ ) ; condition-case
+ def-name
+ ))
+
+
+(defun edebug-sexp-list (debuggable)
+ "Return an edebug form built from the sexp list following point in the
+current buffer. If DEBUGGABLE then wrap edebug calls around each sexp.
+The sexp list does not start with a left paren; we are already in the list.
+Leave point at (before) the trailing right paren."
+ (let (sexp-list)
+ (while (not (eq 'rparen (edebug-next-token-class)))
+ (setq sexp-list (cons (if debuggable
+ (edebug-form)
+ (edebug-read-sexp))
+ sexp-list)))
+ (nreverse sexp-list)))
+
+
+(defun edebug-increment-offset ()
+ ;; accesses edebug-offset-index and edebug-offset-list
+ (setq edebug-offset-index (1+ edebug-offset-index))
+ (setq edebug-offset-list (cons (- (point) edebug-func-mark)
+ edebug-offset-list)))
+
+
+(defun edebug-make-edebug-form (index form)
+ "Return the edebug form for the current function at offset INDEX given FORM.
+Looks like: (edebug def-name INDEX edebug-offset-index 'FORM).
+Also increment the offset index."
+ (prog1
+ (list 'edebug
+ index
+ edebug-offset-index
+ (list 'quote form))
+ (edebug-increment-offset)
+ ))
+
+
+(defun edebug-form ()
+ "Return the debug form for the following form. Add the point offset
+to the edebug-offset-list for the function and move point to
+immediately after the form."
+ (let* ((index edebug-offset-index)
+ form class)
+ ;; The point must be added to the offset list now
+ ;; because edebug-list will add more offsets indirectly.
+ (edebug-skip-whitespace)
+ (edebug-increment-offset)
+ (setq class (edebug-next-token-class))
+ (cond
+ ((eq 'lparen class)
+ (edebug-make-edebug-form index (edebug-list)))
+
+ ((eq 'symbol class)
+ (if (and (not (memq (setq form (edebug-read-sexp)) '(nil t)))
+ ;; note: symbol includes numbers, see parsing utilities
+ (not (numberp form)))
+ (edebug-make-edebug-form index form)
+ form))
+ (t (edebug-read-sexp)))))
+
+
+(defun edebug-list ()
+ "Return an edebug form built from the list form that follows point.
+Insert debug calls as appropriate to the form. Start with point at
+the left paren. Leave point after the right paren."
+ (let ((beginning (point))
+ class
+ head)
+
+ (forward-char 1) ; skip \(
+ (setq class (edebug-next-token-class))
+ (cond
+ ((eq 'symbol class)
+ (setq head (edebug-read-sexp)))
+ ((eq 'lparen class)
+ (setq head (edebug-anonymous)))
+ ((eq 'rparen class)
+ (setq head nil))
+ (t (edebug-syntax-error
+ "Head of list must be a symbol or lambda expression.")))
+
+ (prog1
+ (if head
+ (cons head
+ (cond
+
+;; None of the edebug-form-hooks defined below are used, for speed.
+;; They are included for documentation, though the hook would not
+;; necessarily behave the same as the function it is replacing.
+
+;;; Using the edebug-form-hooks should work, but would take more time.
+;;; ((symbolp head)
+;;; (let ((form (get head 'edebug-form-hook)))
+;;; (if form
+;;; (edebug-form-parser form)
+;;; (if (edebug-macrop head)
+;;; (if edebug-eval-macro-args
+;;; (edebug-sexp-list t)
+;;; (edebug-sexp-list nil))
+;;; ;; assume it is a function
+;;; (edebug-sexp-list t)))))
+
+ ;; handle all special-forms with unevaluated arguments
+ ((memq head '(let let*)) (edebug-let))
+ ((memq head '(setq setq-default)) (edebug-setq))
+ ((eq head 'cond) (edebug-cond))
+ ((eq head 'condition-case) (edebug-condition-case))
+
+ ((memq head '(quote ; permits more than one arg
+ defun defvar defconst defmacro))
+ (edebug-sexp-list nil))
+ ((eq head 'function)
+ (list
+ (if (eq 'lparen (edebug-next-token-class))
+ (edebug-anonymous)
+ (edebug-read-sexp) ; should be just a symbol
+ )))
+
+ ;; is it a lisp macro?
+ ((edebug-macrop head)
+ (or (and (symbolp head)
+ (let ((form (get head 'edebug-form-hook)))
+ (if form
+ (if (eq form t)
+ (edebug-sexp-list t)
+ (if (eq form 0)
+ (edebug-sexp-list nil)
+ (edebug-form-parser form))))))
+ (edebug-sexp-list edebug-eval-macro-args)))
+
+ ((eq head 'interactive)
+ (edebug-syntax-error "interactive not expected here."))
+
+ ;; otherwise it is a function call
+ (t (edebug-sexp-list t))
+ )))
+
+ (if (eq 'rparen (edebug-next-token-class))
+ (forward-char 1) ; skip \)
+ (edebug-syntax-error "Too many arguments."))
+ )))
+
+
+(defun edebug-form-parser (args)
+ "Parse the macro arguments that follow based on ARGS.
+ARGS describes the types of the arguments of a list form. Each of the ARGS
+is processed left to right, in the same order as the arguments of the
+list form. See the edebug documentation for more details. The ARGS
+may be one of the following:
+
+ symbolp - an unevaluated symbol
+ integerp - an unevaluated number
+ stringp - an unevaluated string
+ vectorp - an unevaluated vector
+ atom - an unevaluated number, string, symbol, or vector
+
+ sexp - an unevaluated sexp (atom or list); may not be empty
+ form - an evaluated sexp; may not be empty
+
+ foo - any other symbol should be the name of a function; this
+ function is called on the argument as a predicate and an error
+ is signaled if the predicate fails.
+
+ &optional - one following arg in the list may or may not appear.
+ &rest - all following args are repeated zero or more times as a group.
+ This is an extension of the normal meaning of &rest.
+ &or - each of the following args are alternatives, processed left to
+ right until one succeeds. There is no way to group
+ more than one list element as one alternative.
+
+ (...) - a sublist, of the same format as the top level, processed recursively.
+ Special case: if the car of the list is quote, the argument must match
+ the quoted sexp (see example below of 'for macro).
+"
+
+ (let ((arglist args)
+ arg form form-list class
+ &optional &rest &or)
+ (while (and arglist
+ (not (eq 'rparen (setq class (edebug-next-token-class)))))
+ (catch 'no-match
+ (setq arg (car arglist))
+ (setq arglist (cdr arglist))
+ (if (and &rest (null arglist))
+ (setq arglist &rest))
+
+ (cond
+ ((memq arg '(&optional &rest &or))
+ ;; remember arglist at this point
+ (set arg arglist)
+ (throw 'no-match nil))
+
+ ((eq arg 'form)
+ (setq form (edebug-form)))
+
+ ((eq arg 'sexp)
+ (setq form (edebug-read-sexp)))
+
+ ((listp arg)
+ (if (eq 'quote (car arg))
+ ;; special case, match the quoted symbol
+ (let ((pnt (point)))
+ (setq arg (car (cdr arg)))
+ (if (not (eq arg (setq form (edebug-read-sexp))))
+ (edebug-form-parser-error)
+ ))
+ (if (eq class 'lparen)
+ (progn
+ (forward-char 1) ; skip \(
+ (setq form (edebug-form-parser arg))
+ (forward-char 1) ; skip \)
+ ))))
+ ((symbolp arg)
+ (let ((pnt (point))
+ (pred (if (fboundp arg) (symbol-function arg))))
+ (and pred
+ (not (funcall pred (setq form (edebug-read-sexp))))
+ (edebug-form-parser-error)
+ )))
+ (t (throw 'no-match nil))
+ ) ; cond
+ (setq &optional nil) ; only lasts for one match
+ (setq form-list (cons form form-list)) ; skipped by no-match throw
+ )) ; while
+
+ (if (and arglist (not (or &optional &rest
+ (memq (car arglist) '(&optional &rest)))))
+ (edebug-syntax-error "Not enough arguments."))
+ (if (not (eq 'rparen (edebug-next-token-class)))
+ (if &or
+ (edebug-syntax-error "Unrecognized argument.")
+ (edebug-syntax-error "Too many arguments.")))
+ (nreverse form-list)))
+
+
+(defun edebug-form-parser-error ()
+ (goto-char pnt)
+ (if &or
+ (throw 'no-match nil)
+ (if &optional
+ (progn
+ (setq &optional nil) ; only lasts for one failed match not in &or
+ (throw 'no-match nil))
+ (edebug-syntax-error "%s is not %s" form arg))))
+
+;; for loop defined in elisp manual
+(put 'for 'edebug-form-hook
+ '(symbolp 'from form 'to form 'do &rest form))
+
+;; case and do defined in cl.el
+(put 'case 'edebug-form-hook
+ '(form &rest (sexp form)))
+
+(put 'do 'edebug-form-hook
+ '((&rest
+ &or symbolp
+ (symbolp &optional form
+ &optional form))
+ (form &rest form)
+ &rest body))
+
+(put 'defvar 'edebug-form-hook
+ (put 'defconst 'edebug-form-hook
+ '(symbolp &optional form &optional stringp)))
+
+(put 'defun 'edebug-form-hook
+ (put 'defmacro 'edebug-form-hook
+ '(symbolp (&rest symbolp)
+ &optional stringp
+ &optional ('interactive &or stringp form)
+ &rest form)))
+
+(put 'anonymous 'edebug-form-hook
+ '(&optional 'macro 'lambda (&rest symbolp) &rest form))
+
+(defun edebug-anonymous ()
+ "Return the edebug form for an anonymous lambda or macro.
+Point starts before the left paren and ends after it."
+ (forward-char 1) ; skip \(
+ (prog1
+ (let ((head (edebug-read-sexp)))
+ (cond
+ ((eq head 'lambda)
+ (edebug-lambda))
+ ((eq head 'macro)
+ (if (not (eq 'lambda (edebug-read-sexp)))
+ (edebug-syntax-error "lambda expected."))
+ (cons 'macro (edebug-lambda)))
+ (t (edebug-syntax-error "Anonymous lambda or macro expected."))))
+ (forward-char 1) ; skip \)
+ ))
+
+
+(defun edebug-lambda ()
+ "Return the edebug form for the lambda form that follows.
+Point starts after the lambda symbol and is moved to before the right paren."
+ (append
+ (list 'lambda (edebug-read-sexp)) ; the args
+ (edebug-sexp-list t))) ; the body
+
+
+
+(put 'let 'edebug-form-hook
+ (put 'let* 'edebug-form-hook
+ '((&rest
+ &or (symbolp &optional form)
+ symbolp)
+ &rest form)))
+
+(defun edebug-let ()
+ "Return the edebug form of the let or let* form.
+Leave point before the right paren."
+ (let (var-value-list
+ token
+ class)
+ (cons
+ ;; first process the var/value list
+ (if (not (eq 'lparen (edebug-next-token-class)))
+ (if (setq token (edebug-read-sexp))
+ (edebug-syntax-error "Bad var list in let.") ; should be nil
+ token ; == nil
+ )
+
+ (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-sexp)
+ (forward-char 1) ; lparen
+ (prog1
+ (edebug-var-value)
+ (if (not (eq 'rparen (edebug-next-token-class)))
+ (edebug-syntax-error "Right paren expected in let.")
+ (forward-char 1) ; rparen
+ )))
+ var-value-list)))
+ (forward-char 1) ; rparen
+ (nreverse var-value-list))
+
+ ;; now process the expression list
+ (edebug-sexp-list t))))
+
+
+(defun edebug-var-value ()
+ "Return the edebug form of the var and optional value that follow point.
+Leave point after the value, if there is one."
+ (list
+ (edebug-read-sexp) ; the variable
+ (and (not (eq 'rparen (edebug-next-token-class)))
+ (edebug-form))))
+
+
+(put 'setq 'edebug-form-hook
+ (put 'setq-default 'edebug-form-hook
+ '(&rest symbolp form)))
+
+(defun edebug-setq ()
+ "Return the edebug form of the setq or setq-default 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))
+
+
+(put 'interactive 'edebug-form-hook
+ '(&optional &or stringp form))
+
+(defun edebug-interactive ()
+ "Return the edebug form of the interactive form."
+ (list
+ (if (not (eq 'rparen (edebug-next-token-class)))
+ (if (eq 'string (edebug-next-token-class))
+ (edebug-read-sexp)
+ (prog1
+ (` (edebug-interactive-entry
+ (quote (, def-name))
+ (quote ((,@ (edebug-form))))))
+ (if (not (eq 'rparen (edebug-next-token-class)))
+ (edebug-syntax-error
+ "Only first expression used in interactive form.")))))))
+
+
+(put 'cond 'edebug-form-hook
+ '(&rest (form &rest form)))
+
+(defun edebug-cond ()
+ "Return the edebug form of the cond form."
+ (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))
+ (let ((thing (edebug-read-sexp)))
+ (if thing
+ (edebug-syntax-error "Condition expected in cond")
+ nil))
+ (forward-char 1) ; \(
+ (prog1
+ (cons
+ (edebug-form)
+ (if (eq 'rparen (edebug-next-token-class))
+ nil
+ (edebug-sexp-list t)))
+ (if (not (eq 'rparen (edebug-next-token-class)))
+ (edebug-syntax-error "Right paren expected in cond"))
+ (forward-char 1) ; \)
+ ))
+ value-value-list)))
+ (nreverse value-value-list)))
+
+
+;; Bug: this doesnt support condition name lists
+(put 'condition-case 'edebug-form-hook
+ '(symbolp
+ form
+ &rest (symbolp &optional form)))
+
+(defun edebug-condition-case ()
+ "Return the edebug form of the condition-case form."
+ (cons
+ (let (token)
+ ;; read the variable or nil
+ (setq token (edebug-read-sexp))
+ (if (not (symbolp token))
+ (edebug-syntax-error
+ "Variable or nil required for condition-case; found: %s" token))
+ token)
+
+ (cons
+ (edebug-form) ; the form
+
+ ;; process handlers
+ (let (symb-sexp-list
+ class)
+ (while (not (eq 'rparen (setq class (edebug-next-token-class))))
+ (setq symb-sexp-list
+ (cons
+ (if (not (eq 'lparen class))
+ (edebug-syntax-error "Bad handler in condition-case.")
+ (forward-char 1) ; \(
+ (prog1
+ (cons
+ (edebug-read-sexp) ; the error-condition
+ (and (not (eq 'rparen (edebug-next-token-class)))
+ (edebug-sexp-list t)))
+ (forward-char 1) ; \)
+ ))
+ symb-sexp-list)))
+ (nreverse symb-sexp-list)))))
+
+
+
+;;------------------------------------------------
+;; Parser utilities
+
+(defun edebug-syntax-error (msg &rest args)
+ "Signal an invalid-read-syntax with MSG and ARGS.
+ This is caught by edebug-defun."
+ (signal 'invalid-read-syntax (apply 'format msg args)))
+
+
+(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) ?\;)
+ (skip-chars-forward "^\n\r") ; skip the comment
+ (skip-chars-forward " \t\r\n\f")))
+
+(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 is gummed up by parser inconsistencies (bugs?)
+ (let (token)
+ (edebug-skip-whitespace)
+ (if (or (= (following-char) ?\[) (= (following-char) ??))
+ ;; scan-sexps doesn't read vectors or character literals correctly,
+ ;; but read does.
+ (setq token (read (current-buffer)))
+ (goto-char
+ (min ; use the lesser of the read and scan-sexps motion
+ ;; read goes one too far if (quoted) string or symbol
+ ;; is immediately followed by non-whitespace
+ (save-excursion
+ (setq token (read (current-buffer)))
+ (point))
+ ;; scan-sexps reads too far if a quoting character is read
+ (scan-sexps (point) 1))))
+ token))
+
+(defconst edebug-syntax-table
+ (let ((table (make-vector 256 'symbol)))
+ ;; Treat numbers as symbols, because of confusion with -, -1, and 1-.
+ (aset table ?\( 'lparen)
+ (aset table ?\) 'rparen)
+ (aset table ?\' 'quote)
+ (aset table ?\" 'string)
+ (aset table ?\? 'char)
+ (aset table ?\[ 'vector)
+ (aset table ?\. 'dot)
+ ;; We dont care about any other chars since they wont be seen.
+ table)
+ "Lookup table for the token class of each character.")
+
+(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-syntax-table (following-char)))
+
+
+;;;=================================================================
+;;; 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-backtrace nil
+ "Stack of active functions evaluated via edebug.
+Should be nil at the top level.")
+
+(defvar edebug-offset-indices nil ; not used yet.
+ "Stack of offset indices of visited edebug sexps.
+Should be nil at the top level.")
+
+(defvar edebug-entered nil
+ "Non-nil if edebug has already been entered at this recursive edit level.")
+
+
+(defun edebug-enter (edebug-func 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-defun is entered. Return the
+result of BODY."
+
+ ;; Is this the first time we are entering edebug since
+ ;; lower-level recursive-edit command?
+ (if (and (not edebug-entered)
+ edebug-initial-mode)
+ ;; Reset edebug-mode to the initial mode.
+ (setq edebug-mode edebug-initial-mode))
+ (let* ((edebug-entered t)
+ (edebug-data (get edebug-func 'edebug))
+ ;; pull out parts of the edebug-data
+ (edebug-func-mark (car edebug-data)) ; mark at function start
+
+ (edebug-buffer (marker-buffer edebug-func-mark))
+ (edebug-backtrace (cons edebug-func edebug-backtrace))
+ (max-lisp-eval-depth (+ 6 max-lisp-eval-depth)) ; too much??
+ (max-specpdl-size (+ 10 max-specpdl-size)) ; the args and these vars
+ )
+ (if edebug-trace
+ (let ((edebug-stack-depth (1- (length edebug-backtrace)))
+ edebug-result)
+ (edebug-print-trace-entry
+ "*edebug-trace*" edebug-func edebug-args edebug-stack-depth)
+ (setq edebug-result (eval edebug-body))
+ (edebug-print-trace-exit
+ "*edebug-trace*" edebug-func edebug-result edebug-stack-depth)
+ edebug-result)
+ (eval edebug-body)
+ )))
+
+(defun edebug-interactive-entry (edebug-func edebug-args)
+ "Evaluating FUNCs non-string argument of interactive form ARGS."
+ (if (and (not edebug-entered)
+ edebug-initial-mode)
+ ;; Reset edebug-mode to the initial mode.
+ (setq edebug-mode edebug-initial-mode))
+ (let* ((edebug-entered t)
+ (edebug-data (get edebug-func 'edebug))
+ ;; pull out parts of the edebug-data
+ (edebug-func-mark (car edebug-data)) ; mark at function start
+
+ (edebug-buffer (marker-buffer edebug-func-mark))
+;; (edebug-backtrace (cons edebug-func edebug-backtrace))
+ )
+ (eval edebug-args)))
+
+
+(defun edebug-print-trace-entry
+ (edebug-stream edebug-function edebug-args edebug-stack-depth)
+ (edebug-trace-display
+ edebug-stream
+ "%sEnter: %s\n" (make-string edebug-stack-depth ?\ ) edebug-function)
+ )
+
+(defun edebug-print-trace-exit
+ (edebug-stream edebug-function edebug-result edebug-stack-depth)
+ (edebug-trace-display
+ edebug-stream
+ "%sExit: %s\n" (make-string edebug-stack-depth ?\ ) edebug-function)
+ )
+
+
+(defun edebug (edebug-before-index edebug-after-index edebug-exp)
+ "Debug current function given BEFORE and AFTER positions around EXP.
+BEFORE and AFTER are indexes into the position offset vector in the
+functions 'edebug property. edebug is called from functions compiled
+with edebug-defun."
+ (let ((max-lisp-eval-depth (+ 5 max-lisp-eval-depth)) ; enough??
+ (max-specpdl-size (+ 7 max-specpdl-size)) ; the args and these vars
+ (edebug-offset-indices
+ (cons edebug-before-index edebug-offset-indices))
+ ;; Save the outside value of executing macro.
+ (edebug-outside-executing-macro executing-macro)
+ ;; Don't keep reading from an executing kbd macro within edebug!
+ (executing-macro nil)
+ )
+ (if (and (eq edebug-mode 'Go-nonstop)
+ (not (edebug-input-pending-p)))
+ ;; Just return evalled expression.
+ (eval edebug-exp)
+ (edebug-debugger edebug-before-index 'enter edebug-exp)
+ (edebug-debugger edebug-after-index 'exit (eval edebug-exp))
+ )))
+
+
+(defun edebug-debugger (edebug-offset-index edebug-arg-mode edebug-exp)
+ "Determine if edebug display should be updated."
+ (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
+ (if edebug-break-data
+ (let ((edebug-break-condition
+ (car (cdr edebug-break-data))))
+ (or (not edebug-break-condition)
+ (eval edebug-break-condition)))))
+ )
+ (if (and edebug-break
+ (car (cdr (cdr edebug-break-data)))) ; is it temporary?
+ ;; Delete the breakpoint.
+ (setcdr edebug-data
+ (cons (delq edebug-break-data edebug-breakpoints)
+ (cdr (cdr edebug-data)))))
+
+ ;; Dont do anything if mode is go, continue, or Continue-fast
+ ;; and no break, and no input.
+ (if (or (and (not (memq edebug-mode '(go continue Continue-fast)))
+ (or edebug-stop-before-symbols
+ (not (and (eq edebug-arg-mode 'enter)
+ (symbolp edebug-exp)))))
+ (edebug-input-pending-p)
+ edebug-break)
+ (edebug-display))
+
+ edebug-exp
+ ))
+
+
+(defvar edebug-window-start 0
+ "Remember where each buffers' window starts between edebug calls.
+This is to avoid spurious recentering.")
+
+(setq-default edebug-window-start 0)
+(make-variable-buffer-local 'edebug-window-start)
+
+(defun edebug-display ()
+ "Setup windows for edebug, determine mode, maybe enter recursive-edit."
+ ;; uses local variables of edebug-enter, edebug, and edebug-debugger.
+ (let ((edebug-active t) ; for minor mode alist
+ edebug-stop ; should we enter recursive-edit
+ (edebug-point (+ edebug-func-mark
+ (aref (car (cdr (cdr edebug-data)))
+ edebug-offset-index)))
+ (edebug-buffer-points
+ (if edebug-save-buffer-points (edebug-get-buffer-points)))
+ edebug-window ; window displaying edebug-buffer
+ edebug-inside-window ; window displayed after recursive edit
+ (edebug-outside-window (selected-window))
+ (edebug-outside-buffer (current-buffer))
+ (edebug-outside-point (point))
+ (edebug-outside-mark (mark))
+ edebug-outside-windows ; window or screen configuration
+ edebug-outside-edebug-point ; old point in edebug buffer
+ edebug-outside-edebug-mark
+
+ edebug-eval-buffer ; declared here so we can kill it below
+ (edebug-eval-result-list (and edebug-eval-list
+ (edebug-eval-result-list)))
+ (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)
+
+ edebug-outside-point-min
+ edebug-outside-point-max
+
+ overlay-arrow-position
+ 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-func)))
+
+ ;; Save windows now before we modify them.
+ (if edebug-save-windows
+ (setq edebug-outside-windows
+ (edebug-current-window-configuration)))
+
+ ;; If edebug-buffer is not currently displayed,
+ ;; first find a window for it.
+ (edebug-pop-to-buffer edebug-buffer)
+ (setq edebug-window (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 quit.
+ (edebug-eval-display edebug-eval-result-list)
+ (select-window edebug-window)
+
+ (if edebug-save-point
+ (progn
+ (setq edebug-outside-edebug-point (point))
+ (setq edebug-outside-edebug-mark (mark))))
+
+ (edebug-save-restriction
+ (setq edebug-outside-point-min (point-min))
+ (setq edebug-outside-point-max (point-max))
+ (widen)
+ (goto-char edebug-point)
+
+ (setq edebug-window-start
+ (edebug-adjust-window edebug-window-start))
+
+ (if (edebug-input-pending-p) ; not including keyboard macros
+ (progn
+ (setq edebug-mode 'step)
+ (setq edebug-stop t)
+ (edebug-stop)
+ ;; (discard-input) ; is this unfriendly??
+ ))
+ (edebug-overlay-arrow)
+
+ (cond
+ ((eq 'exit edebug-arg-mode)
+ ;; Display result of previous evaluation.
+ (setq edebug-previous-result edebug-exp)
+ (edebug-previous-result))
+
+ ((eq 'error edebug-arg-mode)
+ ;; Display error message
+ (beep)
+ (if (eq 'quit (car edebug-exp))
+ (message "Quit")
+ (message "%s: %s"
+ (get (car edebug-exp) 'error-message)
+ (car (cdr edebug-exp)))))
+
+ (edebug-break
+ (message "Break"))
+ (t (message "")))
+
+ (if edebug-break
+ (if (not (memq edebug-mode '(continue Continue-fast)))
+ (setq edebug-stop t)
+ (if (eq edebug-mode 'continue)
+ (edebug-sit-for 1)
+ (edebug-sit-for 0)))
+ ;; not edebug-break
+ (if (eq edebug-mode 'trace)
+ (edebug-sit-for 1) ; Force update and pause.
+ (if (eq edebug-mode 'Trace-fast)
+ (edebug-sit-for 0) ; Force update and continue.
+ )))
+
+ (unwind-protect
+ (if (or edebug-stop
+ (eq edebug-mode 'step)
+ (eq edebug-arg-mode 'error))
+ (progn
+ (setq edebug-mode 'step)
+ (edebug-overlay-arrow) ; this doesnt always show up.
+ (edebug-recursive-edit));; <<<<<< Recursive edit
+ )
+
+ (if edebug-save-buffer-points
+ (edebug-set-buffer-points))
+ ;; Since we may be in a save-excursion, in case of quit
+ ;; restore the outside window only.
+ (select-window edebug-outside-window)
+ ) ; unwind-protect
+
+ ;; None of the following is done if quit or signal occurs.
+ (if edebug-save-point
+ ;; Restore point and mark in edebug-buffer.
+ ;; This does the save-excursion recovery only if no quit.
+ ;; If edebug-buffer == edebug-outside-buffer,
+ ;; then this is redundant with outside save-excursion.
+ (progn
+ (set-buffer edebug-buffer)
+ (goto-char edebug-outside-edebug-point)
+ (if (mark-marker)
+ (set-marker (mark-marker) edebug-outside-edebug-mark))
+ ))
+ ) ; edebug-save-restriction
+
+ ;; Restore windows, buffer, point, and mark.
+ (if edebug-save-windows
+ ;; Restore windows before continuing.
+ (edebug-set-window-configuration edebug-outside-windows))
+ (set-buffer edebug-outside-buffer)
+ (goto-char edebug-outside-point)
+ (if (mark-marker)
+ (set-marker (mark-marker) edebug-outside-mark))
+ ;; The following is not sufficient, and sometimes annoying.
+ ;; (if (memq edebug-mode '(go Go-nonstop))
+ ;; (edebug-sit-for 0))
+ ))
+
+
+(defvar edebug-depth 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.")
+
+
+(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.
+ (let ((edebug-buffer-read-only buffer-read-only)
+ ;; match-data must be done in the outside buffer
+ (edebug-outside-match-data
+ (save-excursion
+ (set-buffer edebug-outside-buffer)
+ (match-data)))
+
+ (edebug-depth (1+ edebug-depth))
+ (edebug-recursion-depth (recursion-depth))
+ edebug-entered ; bind locally to nil
+ 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-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)
+;; (edebug-outside-unread-command-char unread-command-char)
+
+ ;; Declare the following local variables to protect global values.
+ ;; We could set these to the values for previous edebug call.
+ ;; But instead make it local, but use global value.
+ (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)
+
+ (debug-on-error debug-on-error)
+
+ ;; others??
+ )
+
+ (if (and (eq edebug-mode 'go)
+ (not (memq edebug-arg-mode '(exit error))))
+ (message "Break"))
+ (edebug-mode)
+ (if (boundp 'edebug-outside-debug-on-error)
+ (setq debug-on-error edebug-outside-debug-on-error))
+
+ (setq buffer-read-only t)
+ (unwind-protect
+ (recursive-edit) ; <<<<<<<<<< Recursive edit
+
+ ;; Do the following, even if quit occurs.
+ (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-mode '(go Go-nonstop))
+ (edebug-overlay-arrow))
+ (setq buffer-read-only edebug-buffer-read-only)
+ (use-local-map edebug-outside-map)
+ ;; Remember current window-start for next visit.
+ (select-window edebug-window)
+ (if (eq edebug-buffer (window-buffer edebug-window))
+ (setq edebug-window-start (window-start)))
+ (select-window edebug-inside-window)
+ ))
+ )))
+
+
+;;--------------------------
+;; Display related functions
+
+(defun edebug-adjust-window (old-start)
+ "Adjust window to fit as much as possible following point.
+The display should prefer to start at OLD-START if point is not visible.
+Return the new window-start."
+ (if (not (pos-visible-in-window-p))
+ (progn
+ (set-window-start (selected-window) old-start)
+ (if (not (pos-visible-in-window-p))
+ (let ((start (window-start))
+ (pnt (point)))
+ (set-window-start
+ (selected-window)
+ (save-excursion
+ (forward-line
+ (if (< pnt start) -1 ; one line before
+ (- (/ (window-height) 2)) ; center the line
+ ))
+ (beginning-of-line)
+ (point)))))))
+ (window-start))
+
+
+(defconst edebug-arrow-alist
+ '((Continue-fast . ">")
+ (Trace-fast . ">")
+ (continue . ">")
+ (trace . "->")
+ (step . "=>")
+ (go . "<>")
+ (Go-nonstop . "..") ; not used
+ )
+ "Association list of arrows for each edebug mode.
+If you come up with arrows that make more sense, let me know.")
+
+(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))))
+
+
+(put 'edebug-outside-excursion 'edebug-form-hook
+ '(&rest form))
+
+(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-window-configuration))
+ ;; Restore outside windows.
+ (edebug-set-window-configuration edebug-outside-windows)))
+
+ (set-buffer edebug-buffer)
+ ;; Restore outside context.
+ (let ((edebug-inside-map (current-local-map))
+ (last-command-char edebug-outside-last-command-char)
+ (last-command edebug-outside-last-command)
+ (this-command edebug-outside-this-command)
+;; (unread-command-char edebug-outside-unread-command-char)
+ (last-input-char edebug-outside-last-input-char)
+ (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)
+ (standard-output edebug-outside-standard-output)
+ (standard-input edebug-outside-standard-input)
+ (executing-macro edebug-outside-executing-macro)
+ )
+ (unwind-protect
+ (save-restriction
+ (narrow-to-region edebug-outside-point-min
+ edebug-outside-point-max)
+ (save-excursion ; of edebug-buffer
+ (if edebug-save-point
+ (progn
+ (goto-char edebug-outside-edebug-point)
+ (if (mark-marker)
+ (set-marker (mark-marker)
+ edebug-outside-edebug-mark))
+ ))
+ (use-local-map edebug-outside-map)
+ (store-match-data edebug-outside-match-data)
+ (select-window edebug-outside-window)
+ (set-buffer edebug-outside-buffer)
+ (goto-char edebug-outside-point)
+ (,@ body)
+ ) ; save-excursion
+ ) ; save-restriction
+ ;; Back to edebug-buffer. Restore rest of inside context.
+ (use-local-map edebug-inside-map)
+ (if edebug-save-windows
+ ;; Restore inside windows.
+ (edebug-set-window-configuration edebug-inside-windows))
+ )) ; let
+ )))
+
+
+(defun edebug-toggle-save-windows ()
+ "Toggle the edebug-save-windows variable.
+Each time you toggle it, the inside and outside window configurations
+become the same as the current configuration."
+ (interactive)
+ (if (setq edebug-save-windows (not edebug-save-windows))
+ (setq edebug-inside-windows
+ (setq edebug-outside-windows
+ (edebug-current-window-configuration))))
+ (message "Window saving is %s."
+ (if edebug-save-windows "on" "off")))
+
+
+(defun edebug-where ()
+ "Show the debug windows and where we stopped in the program."
+ (interactive)
+ (if (not edebug-active)
+ (error "edebug is not active."))
+ (edebug-pop-to-buffer edebug-buffer)
+ (goto-char edebug-point) ; from edebug
+ )
+
+(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-window-configuration))
+ (edebug-set-window-configuration 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 ()
+ "Bounce the point in the outside current buffer."
+ (interactive)
+ (if (not edebug-active)
+ (error "edebug is not active."))
+ (save-excursion
+ ;; If the buffer's currently displayed, avoid the set-window-configuration.
+ (save-window-excursion
+ (edebug-pop-to-buffer edebug-outside-buffer)
+ ;; (edebug-sit-for 1) ; this shouldnt be necessary
+ (goto-char edebug-outside-point)
+ ;; (message "current buffer: %s" (current-buffer))
+ (edebug-sit-for 1)
+ (edebug-pop-to-buffer edebug-buffer))))
+
+
+
+;;--------------------------
+;; epoch related things
+
+(defvar edebug-epoch-running (and (boundp 'epoch::version) epoch::version)
+ "non-nil if epoch is running.
+Windows are handled a little differently under epoch.")
+
+
+(defun edebug-current-window-configuration ()
+ "Return the current window or screen configuration."
+ (if edebug-epoch-running
+ (edebug-current-screen-configuration)
+ (current-window-configuration)))
+
+
+(defun edebug-set-window-configuration (conf)
+ "Set the window or screen configuration to CONF."
+ (if edebug-epoch-running
+ (edebug-set-screen-configuration conf)
+ (set-window-configuration conf)))
+
+
+(defun edebug-get-buffer-window (buffer)
+ (if edebug-epoch-running
+ (epoch::get-buffer-window buffer)
+ (get-buffer-window buffer)))
+
+
+(defun edebug-pop-to-buffer (buffer)
+ "Like pop-to-buffer, but select a screen that buffer was shown in."
+ (let ((edebug-window (edebug-get-buffer-window buffer)))
+ (if edebug-window
+ (select-window edebug-window)
+ ;; It is not currently displayed, so find some place to display it.
+ (if edebug-epoch-running
+ ;; Select a screen that the buffer has been displayed in before
+ ;; or the current screen otherwise.
+ (select-screen
+ ;; allowed-screens in epoch 3.2, was called screens before that
+ (or (car (symbol-buffer-value 'allowed-screens buffer))
+ (epoch::current-screen))))
+ (if (one-window-p)
+ (split-window))
+ (select-window (next-window))
+ (set-window-buffer (selected-window) buffer)
+ (set-window-hscroll (selected-window) 0)
+ ))
+ ;; Selecting the window does not set the buffer.
+ (set-buffer buffer)
+ )
+
+
+(defun edebug-current-screen-configuration ()
+ "Return an object recording the current configuration of Epoch screen-list.
+The object is a list of pairs of the form (SCREEN . CONFIGURATION)
+where SCREEN has window-configuration CONFIGURATION. The current
+screen is the head of the list."
+ (let ((screen-list (epoch::screen-list 'unmapped))
+ (current-screen (epoch::get-screen))
+ (current-buffer (current-buffer))
+ )
+ ;; put current screen first
+ (setq screen-list (cons current-screen (delq current-screen screen-list)))
+ (prog1
+ (mapcar (function
+ (lambda (screen)
+ (cons screen
+ (progn
+ (epoch::select-screen screen)
+ (current-window-configuration)))))
+ screen-list)
+ (epoch::select-screen current-screen)
+ (set-buffer current-buffer)
+ )))
+
+(defun edebug-set-screen-configuration (sc)
+ "Set the window-configuration for all the screens in SC.
+Set the current screen to be the head of SC."
+ (mapcar (function
+ (lambda (screen-conf)
+ (if (epoch::screen-p (car screen-conf)) ; still exist?
+ (progn
+ (epoch::select-screen (car screen-conf))
+ (set-window-configuration (cdr screen-conf))))))
+ sc)
+ (if (epoch::screen-p (car (car sc)))
+ (epoch::select-screen (car (car sc))))
+ )
+
+
+(defun edebug-sit-for (arg)
+ (if edebug-epoch-running
+ (epoch::dispatch-events))
+ (sit-for arg)
+)
+
+(defun edebug-input-pending-p ()
+ (if edebug-epoch-running
+ (epoch::dispatch-events))
+ (input-pending-p)
+)
+
+
+
+;;--------------------------
+;; breakpoint related functions
+
+(defun edebug-find-stop-point ()
+ "Return (function . index) of the nearest edebug stop point."
+ (let* ((def-name (edebug-which-function))
+ (edebug-data
+ (or (get def-name 'edebug)
+ (error
+ "%s must first be evaluated with edebug-defun." def-name)))
+ ;; pull out parts of edebug-data.
+ (edebug-func-mark (car edebug-data))
+ (edebug-breakpoints (car (cdr edebug-data)))
+
+ (offset-vector (car (cdr (cdr edebug-data))))
+ (offset (- (save-excursion
+ (if (looking-at "[ \t]")
+ ;; skip backwards until non-whitespace, or bol
+ (skip-chars-backward " \t"))
+ (point))
+ edebug-func-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 def-name i)
+ (message "Point is not on an expression in %s."
+ 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* ((def-name (car edebug-stop-point))
+ (index (cdr edebug-stop-point))
+ (edebug-data (get def-name 'edebug))
+
+ ;; pull out parts of edebug-data
+ (edebug-func-mark (car edebug-data))
+ (edebug-breakpoints (car (cdr edebug-data)))
+ (offset-vector (car (cdr (cdr 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-func-mark
+ (aref offset-vector (car breakpoint))))
+
+ (message (concat (if (car (cdr (cdr breakpoint)))
+ "Temporary " "")
+ (if (car (cdr breakpoint))
+ (format "Condition: %s"
+ (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* ((def-name (car edebug-stop-point))
+ (index (cdr edebug-stop-point))
+ (edebug-data (get def-name 'edebug))
+
+ ;; pull out parts of edebug-data
+ (edebug-func-mark (car edebug-data))
+ (edebug-breakpoints (car (cdr edebug-data)))
+ (offset-vector (car (cdr (cdr 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) '<))
+ (message "Breakpoint set in %s." def-name))
+ (if present
+ (message "Breakpoint unset in %s." def-name)
+ (message "No breakpoint here.")))
+
+ (setcdr edebug-data
+ (cons edebug-breakpoints (cdr (cdr edebug-data))))
+ (goto-char (+ edebug-func-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))
+
+(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: ")
+ (edebug-modify-breakpoint t condition arg))
+
+
+;;--------------------------
+;; Mode switching functions
+
+(defun edebug-set-mode (mode shortmsg msg)
+ "Set the edebug mode to MODE.
+Display SHORTMSG, or MSG if not within edebug."
+ (interactive)
+ (setq edebug-mode mode)
+ (if (< 0 edebug-depth)
+ (if (eq (current-buffer) edebug-buffer)
+ (progn
+ (message shortmsg)
+ (exit-recursive-edit)))
+ (message msg)))
+
+
+(defun edebug-step-through ()
+ "Proceed to next debug step."
+ (interactive)
+ (edebug-set-mode 'step "" "edebug will stop before next eval."))
+
+(defun edebug-go (arg)
+ "Go, evaluating until break.
+With ARG set temporary break at stop 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 ()
+ "Go, evaluating without debugging."
+ (interactive)
+ (edebug-set-mode 'Go-nonstop "Go-Nonstop..."
+ "edebug will not stop at breaks."))
+
+(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 err
+ (let ((parse-sexp-ignore-comments t))
+ ;; Call forward-sexp repeatedly until done or failure.
+ (forward-sexp arg)
+ (edebug-go 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 err
+ (let ((parse-sexp-ignore-comments t))
+ (up-list 1)
+ (save-excursion
+ ;; Is there still a containing expression?
+ (up-list 1))
+ (edebug-go 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-through) ; No more at this level, so step.
+ (edebug-go t)
+ )))))
+
+
+(defun edebug-goto-here ()
+ "Proceed to this stop point."
+ (interactive)
+ (edebug-go t)
+ )
+
+(defun edebug-trace ()
+ "Begin trace mode."
+ (interactive)
+ (edebug-set-mode 'trace "Tracing..." "edebug will trace with pause."))
+
+(defun edebug-Trace-fast ()
+ "Trace with no wait at each step."
+ (interactive)
+ (edebug-set-mode 'Trace-fast
+ "Trace fast..." "edebug will trace without pause."))
+
+(defun edebug-continue ()
+ "Begin continue mode."
+ (interactive)
+ (edebug-set-mode 'continue "Continue..."
+ "edebug will pause at breakpoints."))
+
+(defun edebug-Continue-fast ()
+ "Trace with no wait at each step."
+ (interactive)
+ (edebug-set-mode 'Continue-fast "Continue fast..."
+ "edebug will stop and go at breakpoints."))
+
+
+(defun edebug-step-in ()
+ "Step into the function about to be called.
+Do this before the arguments are evaluated since otherwise it will be
+too late. One side effect of using edebug-step-in is that the next
+time the function is called, edebug will be called there as well."
+ (interactive)
+ (if (not (eq 'enter edebug-arg-mode))
+ (error "You must be in front of a function or macro call."))
+ (let* ((func (car edebug-exp))
+ (func-marker (get func 'edebug)))
+ (cond
+ ((markerp func-marker)
+ (save-excursion
+ (set-buffer (marker-buffer func-marker))
+ (goto-char func-marker)
+ (edebug-defun)))
+ ((listp func-marker)
+ ;; its already been evaluated for edebug
+ nil)
+ (t (error "You must first evaluate %s in a buffer." func))))
+ (exit-recursive-edit))
+
+
+;;(defun edebug-exit-out ()
+;; "Go until the current function exits."
+;; (interactive)
+;; (edebug-set-mode 'exiting "Exit..."))
+
+
+(defun edebug-stop ()
+ "Useful for exiting from trace loop."
+ (interactive)
+ (message "Stop"))
+
+
+;;; 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
+
+(defvar edebug-previous-result nil
+ "Last result returned from an expression.")
+
+(defun edebug-previous-result ()
+ "Return the previous result."
+ (interactive)
+ (let ((print-escape-newlines t)
+ (print-length 20))
+ (message "Result: %s" (prin1-to-string edebug-previous-result))))
+
+
+(defun edebug-eval (expr)
+ "Evaluate EXPR in the outside environment."
+ (if (not edebug-active)
+ (error "edebug is not active."))
+ (edebug-outside-excursion
+ (eval expr)))
+
+(defun edebug-eval-expression (expr)
+ "Prompt and evaluate an expression in the outside environment.
+Print result in minibuffer."
+ (interactive "xEval: ")
+ (prin1 (edebug-eval expr)))
+
+(defun edebug-eval-last-sexp ()
+ "Evaluate sexp before point in the outside environment;
+print value in minibuffer."
+ (interactive)
+ (prin1 (edebug-eval (edebug-last-sexp))))
+
+(defun edebug-eval-print-last-sexp ()
+ "Evaluate sexp before point in the outside environment;
+print value into current buffer."
+ (interactive)
+ (let ((standard-output (current-buffer)))
+ (print
+ (condition-case err
+ (edebug-eval (edebug-last-sexp))
+ (error (format "%s: %s"
+ (get (car err) 'error-message)
+ (car (cdr err))))))))
+
+;;;---------------------------------
+;;; edebug minor mode initialization
+
+(defvar edebug-mode 'step
+ "Current edebug mode set by user.")
+
+(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-through)
+ (define-key edebug-mode-map "g" 'edebug-go)
+ (define-key edebug-mode-map "G" 'edebug-Go-nonstop)
+ (define-key edebug-mode-map "t" 'edebug-trace)
+ (define-key edebug-mode-map "T" 'edebug-Trace-fast)
+ (define-key edebug-mode-map "c" 'edebug-continue)
+ (define-key edebug-mode-map "C" 'edebug-Continue-fast)
+
+ (define-key edebug-mode-map "f" 'edebug-forward-sexp)
+ (define-key edebug-mode-map "h" 'edebug-goto-here)
+
+ (define-key edebug-mode-map "r" 'edebug-previous-result)
+
+ (define-key edebug-mode-map "i" 'edebug-step-in)
+ (define-key edebug-mode-map "o" 'edebug-step-out)
+
+;; (define-key edebug-mode-map "m" 'edebug-set-initial-mode)
+
+ (define-key edebug-mode-map "q" 'top-level)
+ (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)
+
+ ;; evaluation
+ (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)
+ (define-key edebug-mode-map "p" 'edebug-bounce-point)
+ (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)
+ ))
+
+
+(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 "X" 'edebug-step-through)
+ (define-key global-edebug-map " " 'edebug-step-through)
+ (define-key global-edebug-map "g" 'edebug-go)
+ (define-key global-edebug-map "G" 'edebug-Go-nonstop)
+ (define-key global-edebug-map "t" 'edebug-trace)
+ (define-key global-edebug-map "T" 'edebug-Trace-fast)
+ (define-key global-edebug-map "c" 'edebug-continue)
+ (define-key global-edebug-map "C" 'edebug-Continue-fast)
+
+;; (define-key global-edebug-map "m" 'edebug-set-initial-mode)
+ (define-key global-edebug-map "b" 'edebug-set-breakpoint)
+ (define-key global-edebug-map "x" 'edebug-set-conditional-breakpoint)
+ (define-key global-edebug-map "u" 'edebug-unset-breakpoint)
+ (define-key global-edebug-map "w" 'edebug-where)
+ (define-key global-edebug-map "q" 'top-level)
+ )
+
+
+(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.
+
+edebug buffer commands:
+\\{edebug-mode-map}
+
+Global commands prefixed by global-edbug-prefix:
+\\{global-edebug-map}
+
+Options:
+edebug-all-defuns
+edebug-eval-macro-args
+edebug-stop-before-symbols
+edebug-save-windows
+edebug-save-point
+edebug-save-buffer-points
+edebug-initial-mode
+edebug-trace
+"
+ (use-local-map edebug-mode-map))
+
+
+
+;;===============================================
+;; edebug eval list mode
+;; A list of expressions and their evaluations is displayed
+;; in edebug-eval-buffer
+
+(defvar edebug-eval-list nil
+ "List of expressions to evaluate.")
+
+;;(defvar edebug-eval-buffer "*edebug*"
+;; "*Declared globally so edebug-eval-display can be called independent
+;;of edebug (not implemented yet).")
+
+
+(defun edebug-eval-result-list ()
+ "Return a list of evaluations of edebug-eval-list"
+ ;; Assumes in outside environment.
+ (mapcar (function
+ (lambda (expr)
+ (condition-case err
+ (eval expr)
+ (error (format "%s: %s"
+ (get (car err) 'error-message)
+ (car (cdr err))))
+ )))
+ 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-display-line
+ (format ";%s\n" (make-string (- (window-width) 2) ?-))))
+ (edebug-pop-to-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-display-line)
+ (setq edebug-eval-list-temp (cdr edebug-eval-list-temp))
+ (setq edebug-eval-result-list (cdr edebug-eval-result-list)))
+ ))
+
+(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-pop-to-buffer edebug-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-pop-to-buffer edebug-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 data display buffer while in edebug. Under construction.
+... ignore the following...
+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.
+
+Eval list buffer commands:
+\\{edebug-eval-mode-map}
+
+Global commands prefixed by global-edbug-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-debug)
+;; (setq debugger 'debug) ; use the default
+
+;; Note that debug and its utilities must be byte-compiled to work, since
+;; they depend on the backtrace looking a certain way.
+
+(defun edebug-debug (&rest debugger-args)
+ "Replacement for debug.
+If an error or quit occurred and we are running an edebugged function,
+show where we last were. Otherwise call debug normally."
+ (if (and edebug-backtrace ; anything active?
+ (eq (recursion-depth) edebug-recursion-depth)
+ )
+
+ ;; Where were we before the error occurred?
+ (let ((edebug-offset-index (car edebug-offset-indices))
+ (edebug-arg-mode (car debugger-args))
+ (edebug-exp (car (cdr debugger-args)))
+ edebug-break-data
+ edebug-break
+ (edebug-outside-debug-on-eror debug-on-error)
+ (debug-on-error nil))
+ (edebug-display)
+ )
+
+ ;; Otherwise call debug normally.
+ ;; Still need to remove extraneous edebug calls from stack.
+ (apply 'debug debugger-args)
+ ))
+
+
+(defun edebug-backtrace ()
+ "Display a non-working backtrace. Better than nothing..."
+ (interactive)
+ (let ((old-buf (current-buffer)))
+ (if (not edebug-backtrace-buffer)
+ (setq edebug-backtrace-buffer
+ (let ((default-major-mode 'fundamental-mode))
+ (generate-new-buffer "*Backtrace*"))))
+ (edebug-pop-to-buffer edebug-backtrace-buffer)
+ (erase-buffer)
+ (let ((standard-output (current-buffer))
+ (print-escape-newlines t)
+ (print-length 50)
+ last-ok-point
+ )
+ (setq truncate-lines t)
+ (backtrace)
+
+ ;; Clean up the backtrace.
+ (goto-char (point-min))
+ (delete-region
+ (point)
+ (progn
+ ;; Everything up to the first edebug is internal.
+ (re-search-forward "^ edebug(")
+ (forward-line 1)
+ (point)))
+ (forward-line 1)
+ (setq last-ok-point (point))
+
+ ;; Delete interspersed edebug internals.
+ (while (re-search-forward "^ edebug" nil t)
+ (if (looking-at "-enter")
+ ;; delete extraneous progn at top level of function body
+ (save-excursion
+ (goto-char last-ok-point)
+ (forward-line -1)
+ (setq last-ok-point (point))))
+ (forward-line 1)
+ (delete-region last-ok-point (point))
+ (forward-line 1) ; skip past the good line
+ (setq last-ok-point (point))
+ )
+ )
+ (edebug-pop-to-buffer old-buf)
+ ))
+
+
+;;========================================================================
+;; Trace display - append text to a buffer, and update display.
+;;; e.g.
+;;; (edebug-trace-display
+;;; "*trace-point*"
+;;; "saving: point = %s window-start = %s\n"
+;;; (point) (window-start))
+
+(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."
+ (let* ((selected-window (selected-window))
+ (buffer (get-buffer-create buf-name))
+ (buf-window))
+ (edebug-pop-to-buffer buffer)
+ (save-excursion
+ (setq buf-window (selected-window))
+ (set-buffer buffer)
+ (goto-char (point-max))
+ (insert (apply 'format fmt args))
+ (set-window-point buf-window (point))
+ (forward-line (- 1 (window-height buf-window)))
+ (set-window-start buf-window (point))
+;; (edebug-sit-for 1)
+ (bury-buffer buffer)
+ )
+ (select-window selected-window)))
+
+;;; edebug.el ends here