+\f
+;; ======================================================================
+;; pdb (Python debugger) functions
+
+;;; History of argument lists passed to pdb.
+(defvar gud-pdb-history nil)
+
+(defun gud-pdb-massage-args (file args)
+ args)
+
+;; Last group is for return value, e.g. "> test.py(2)foo()->None"
+;; Either file or function name may be omitted: "> <string>(0)?()"
+(defvar gud-pdb-marker-regexp
+ "^> \\([-a-zA-Z0-9_/.]*\\|<string>\\)(\\([0-9]+\\))\\([a-zA-Z0-9_]*\\|\\?\\)()\\(->[^\n]*\\)?\n")
+(defvar gud-pdb-marker-regexp-file-group 1)
+(defvar gud-pdb-marker-regexp-line-group 2)
+(defvar gud-pdb-marker-regexp-fnname-group 3)
+
+(defvar gud-pdb-marker-regexp-start "^> ")
+
+;; There's no guarantee that Emacs will hand the filter the entire
+;; marker at once; it could be broken up across several strings. We
+;; might even receive a big chunk with several markers in it. If we
+;; receive a chunk of text which looks like it might contain the
+;; beginning of a marker, we save it here between calls to the
+;; filter.
+(defun gud-pdb-marker-filter (string)
+ (setq gud-marker-acc (concat gud-marker-acc string))
+ (let ((output ""))
+
+ ;; Process all the complete markers in this chunk.
+ (while (string-match gud-pdb-marker-regexp gud-marker-acc)
+ (setq
+
+ ;; Extract the frame position from the marker.
+ gud-last-frame
+ (let ((file (match-string gud-pdb-marker-regexp-file-group
+ gud-marker-acc))
+ (line (string-to-int
+ (match-string gud-pdb-marker-regexp-line-group
+ gud-marker-acc))))
+ (if (string-equal file "<string>")
+ gud-last-frame
+ (cons file line)))
+
+ ;; Output everything instead of the below
+ output (concat output (substring gud-marker-acc 0 (match-end 0)))
+;; ;; Append any text before the marker to the output we're going
+;; ;; to return - we don't include the marker in this text.
+;; output (concat output
+;; (substring gud-marker-acc 0 (match-beginning 0)))
+
+ ;; Set the accumulator to the remaining text.
+ gud-marker-acc (substring gud-marker-acc (match-end 0))))
+
+ ;; Does the remaining text look like it might end with the
+ ;; beginning of another marker? If it does, then keep it in
+ ;; gud-marker-acc until we receive the rest of it. Since we
+ ;; know the full marker regexp above failed, it's pretty simple to
+ ;; test for marker starts.
+ (if (string-match gud-pdb-marker-regexp-start gud-marker-acc)
+ (progn
+ ;; Everything before the potential marker start can be output.
+ (setq output (concat output (substring gud-marker-acc
+ 0 (match-beginning 0))))
+
+ ;; Everything after, we save, to combine with later input.
+ (setq gud-marker-acc
+ (substring gud-marker-acc (match-beginning 0))))
+
+ (setq output (concat output gud-marker-acc)
+ gud-marker-acc ""))
+
+ output))
+
+(defun gud-pdb-find-file (f)
+ (save-excursion
+ (let ((buf (find-file-noselect f)))
+ (set-buffer buf)
+ (gud-make-debug-menu)
+ ;; (local-set-key [menu-bar debug finish] '("Finish Function" . gud-finish))
+ ;; (local-set-key [menu-bar debug up] '("Up Stack" . gud-up))
+ ;; (local-set-key [menu-bar debug down] '("Down Stack" . gud-down))
+ buf)))
+
+(defvar pdb-minibuffer-local-map nil
+ "Keymap for minibuffer prompting of pdb startup command.")
+(if pdb-minibuffer-local-map
+ ()
+ (setq pdb-minibuffer-local-map (copy-keymap minibuffer-local-map))
+ (define-key
+ pdb-minibuffer-local-map "\C-i" 'comint-dynamic-complete-filename))
+
+(defcustom gud-pdb-command-name "pdb"
+ "File name for executing the Python debugger.
+This should be an executable on your path, or an absolute file name."
+ :type 'string
+ :group 'gud)
+
+;;;###autoload
+(defun pdb (command-line)
+ "Run pdb on program FILE in buffer `*gud-FILE*'.
+The directory containing FILE becomes the initial working directory
+and source-file directory for your debugger."
+ (interactive
+ (list (read-from-minibuffer "Run pdb (like this): "
+ (if (consp gud-pdb-history)
+ (car gud-pdb-history)
+ (concat gud-pdb-command-name " "))
+ pdb-minibuffer-local-map nil
+ 'gud-pdb-history)))
+
+ (gud-common-init command-line 'gud-pdb-massage-args
+ 'gud-pdb-marker-filter 'gud-pdb-find-file)
+
+ (gud-def gud-break "break %l" "\C-b" "Set breakpoint at current line.")
+ (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line")
+ (gud-def gud-step "step" "\C-s" "Step one source line with display.")
+ (gud-def gud-next "next" "\C-n" "Step one line (skip functions).")
+ (gud-def gud-cont "continue" "\C-r" "Continue with display.")
+ (gud-def gud-finish "return" "\C-f" "Finish executing current function.")
+ (gud-def gud-up "up" "<" "Up one stack frame.")
+ (gud-def gud-down "down" ">" "Down one stack frame.")
+ (gud-def gud-print "p %e" "\C-p" "Evaluate Python expression at point.")
+ ;; Is this right?
+ (gud-def gud-statement "! %e" "\C-e" "Execute Python statement at point.")
+
+ (local-set-key [menu-bar debug finish] '("Finish Function" . gud-finish))
+ (local-set-key [menu-bar debug up] '("Up Stack" . gud-up))
+ (local-set-key [menu-bar debug down] '("Down Stack" . gud-down))
+ ;; (setq comint-prompt-regexp "^(.*pdb[+]?) *")
+ (setq comint-prompt-regexp "^(Pdb) *")
+ (setq paragraph-start comint-prompt-regexp)
+ (run-hooks 'pdb-mode-hook))
+\f
+;; ======================================================================
+;;
+;; JDB support.
+;;
+;; AUTHOR: Derek Davies <ddavies@world.std.com>
+;;
+;; CREATED: Sun Feb 22 10:46:38 1998 Derek Davies.
+;;
+;; INVOCATION NOTES:
+;;
+;; You invoke jdb-mode with:
+;;
+;; M-x jdb <enter>
+;;
+;; It responds with:
+;;
+;; Run jdb (like this): jdb
+;;
+;; type any jdb switches followed by the name of the class you'd like to debug.
+;; Supply a fully qualfied classname (these do not have the ".class" extension)
+;; for the name of the class to debug (e.g. "COM.the-kind.ddavies.CoolClass").
+;; See the known problems section below for restrictions when specifying jdb
+;; command line switches (search forward for '-classpath').
+;;
+;; You should see something like the following:
+;;
+;; Current directory is ~/src/java/hello/
+;; Initializing jdb...
+;; 0xed2f6628:class(hello)
+;; >
+;;
+;; To set an initial breakpoint try:
+;;
+;; > stop in hello.main
+;; Breakpoint set in hello.main
+;; >
+;;
+;; To execute the program type:
+;;
+;; > run
+;; run hello
+;;
+;; Breakpoint hit: running ...
+;; hello.main (hello:12)
+;;
+;; Type M-n to step over the current line and M-s to step into it. That,
+;; along with the JDB 'help' command should get you started. The 'quit'
+;; JDB command will get out out of the debugger. There is some truly
+;; pathetic JDB documentation available at:
+;;
+;; http://java.sun.com/products/jdk/1.1/debugging/
+;;
+;; KNOWN PROBLEMS AND FIXME's:
+;;
+;; Not sure what happens with inner classes ... haven't tried them.
+;;
+;; Does not grok UNICODE id's. Only ASCII id's are supported.
+;;
+;; You must not put whitespace between "-classpath" and the path to
+;; search for java classes even though it is required when invoking jdb
+;; from the command line. See gud-jdb-massage-args for details.
+;;
+;; If any of the source files in the directories listed in
+;; gud-jdb-directories won't parse you'll have problems. Make sure
+;; every file ending in ".java" in these directories parses without error.
+;;
+;; All the .java files in the directories in gud-jdb-directories are
+;; syntactically analyzed each time gud jdb is invoked. It would be
+;; nice to keep as much information as possible between runs. It would
+;; be really nice to analyze the files only as neccessary (when the
+;; source needs to be displayed.) I'm not sure to what extent the former
+;; can be accomplished and I'm not sure the latter can be done at all
+;; since I don't know of any general way to tell which .class files are
+;; defined by which .java file without analyzing all the .java files.
+;; If anyone knows why JavaSoft didn't put the source file names in
+;; debuggable .class files please clue me in so I find something else
+;; to be spiteful and bitter about.
+;;
+;; ======================================================================
+;; gud jdb variables and functions
+
+;; History of argument lists passed to jdb.
+(defvar gud-jdb-history nil)
+
+;; List of Java source file directories.
+(defvar gud-jdb-directories (list ".")
+ "*A list of directories that gud jdb should search for source code.
+The file names should be absolute, or relative to the current
+directory.
+
+The set of .java files residing in the directories listed are
+syntactically analyzed to determine the classes they define and the
+packages in which these classes belong. In this way gud jdb maps the
+package-qualified class names output by the jdb debugger to the source
+file from which the class originated. This allows gud mode to keep
+the source code display in sync with the debugging session.")
+
+;; List of the java source files for this debugging session.
+(defvar gud-jdb-source-files nil)
+
+;; Association list of fully qualified class names (package + class name) and
+;; their source files.
+(defvar gud-jdb-class-source-alist nil)
+
+;; This is used to hold a source file during analysis.
+(defvar gud-jdb-analysis-buffer nil)
+
+;; Return a list of java source files. PATH gives the directories in
+;; which to search for files with extension EXTN. Normally EXTN is
+;; given as the regular expression "\\.java$" .
+(defun gud-jdb-build-source-files-list (path extn)
+ (apply 'nconc (mapcar (lambda (d)
+ (when (file-directory-p d)
+ (directory-files d t extn nil)) path))))
+
+;; Move point past whitespace.
+(defun gud-jdb-skip-whitespace ()
+ (skip-chars-forward " \n\r\t\014"))
+
+;; Move point past a "// <eol>" type of comment.
+(defun gud-jdb-skip-single-line-comment ()
+ (end-of-line))
+
+;; Move point past a "/* */" or "/** */" type of comment.
+(defun gud-jdb-skip-traditional-or-documentation-comment ()
+ (forward-char 2)
+ (catch 'break
+ (while (not (eobp))
+ (if (eq (following-char) ?*)
+ (progn
+ (forward-char)
+ (if (not (eobp))
+ (if (eq (following-char) ?/)
+ (progn
+ (forward-char)
+ (throw 'break nil)))))
+ (forward-char)))))
+
+;; Move point past any number of consecutive whitespace chars and/or comments.
+(defun gud-jdb-skip-whitespace-and-comments ()
+ (gud-jdb-skip-whitespace)
+ (catch 'done
+ (while t
+ (cond
+ ((looking-at "//")
+ (gud-jdb-skip-single-line-comment)
+ (gud-jdb-skip-whitespace))
+ ((looking-at "/\\*")
+ (gud-jdb-skip-traditional-or-documentation-comment)
+ (gud-jdb-skip-whitespace))
+ (t (throw 'done nil))))))
+
+;; Move point past things that are id-like. The intent is to skip regular
+;; id's, such as class or interface names as well as package and interface
+;; names.
+(defun gud-jdb-skip-id-ish-thing ()
+ (skip-chars-forward "^ /\n\r\t\014,;{"))
+
+;; Move point past a string literal.
+(defun gud-jdb-skip-string-literal ()
+ (forward-char)
+ (while (not (cond
+ ((eq (following-char) ?\\)
+ (forward-char))
+ ((eq (following-char) ?\042))))
+ (forward-char))
+ (forward-char))
+
+;; Move point past a character literal.
+(defun gud-jdb-skip-character-literal ()
+ (forward-char)
+ (while
+ (progn
+ (if (eq (following-char) ?\\)
+ (forward-char 2))
+ (not (eq (following-char) ?\')))
+ (forward-char))
+ (forward-char))
+
+;; Move point past the following block. There may be (legal) cruft before
+;; the block's opening brace. There must be a block or it's the end of life
+;; in petticoat junction.
+(defun gud-jdb-skip-block ()
+
+ ;; Find the begining of the block.
+ (while
+ (not (eq (following-char) ?{))
+
+ ;; Skip any constructs that can harbor literal block delimiter
+ ;; characters and/or the delimiters for the constructs themselves.
+ (cond
+ ((looking-at "//")
+ (gud-jdb-skip-single-line-comment))
+ ((looking-at "/\\*")
+ (gud-jdb-skip-traditional-or-documentation-comment))
+ ((eq (following-char) ?\042)
+ (gud-jdb-skip-string-literal))
+ ((eq (following-char) ?\')
+ (gud-jdb-skip-character-literal))
+ (t (forward-char))))
+
+ ;; Now at the begining of the block.
+ (forward-char)
+
+ ;; Skip over the body of the block as well as the final brace.
+ (let ((open-level 1))
+ (while (not (eq open-level 0))
+ (cond
+ ((looking-at "//")
+ (gud-jdb-skip-single-line-comment))
+ ((looking-at "/\\*")
+ (gud-jdb-skip-traditional-or-documentation-comment))
+ ((eq (following-char) ?\042)
+ (gud-jdb-skip-string-literal))
+ ((eq (following-char) ?\')
+ (gud-jdb-skip-character-literal))
+ ((eq (following-char) ?{)
+ (setq open-level (+ open-level 1))
+ (forward-char))
+ ((eq (following-char) ?})
+ (setq open-level (- open-level 1))
+ (forward-char))
+ (t (forward-char))))))
+
+;; Find the package and class definitions in Java source file FILE. Assumes
+;; that FILE contains a legal Java program. BUF is a scratch buffer used
+;; to hold the source during analysis.
+(defun gud-jdb-analyze-source (buf file)
+ (let ((l nil))
+ (set-buffer buf)
+ (insert-file-contents file nil nil nil t)
+ (goto-char 0)
+ (catch 'abort
+ (let ((p ""))
+ (while (progn
+ (gud-jdb-skip-whitespace)
+ (not (eobp)))
+ (cond
+
+ ;; Any number of semi's following a block is legal. Move point
+ ;; past them. Note that comments and whitespace may be
+ ;; interspersed as well.
+ ((eq (following-char) ?\073)
+ (forward-char))
+
+ ;; Move point past a single line comment.
+ ((looking-at "//")
+ (gud-jdb-skip-single-line-comment))
+
+ ;; Move point past a traditional or documentation comment.
+ ((looking-at "/\\*")
+ (gud-jdb-skip-traditional-or-documentation-comment))
+
+ ;; Move point past a package statement, but save the PackageName.
+ ((looking-at "package")
+ (forward-char 7)
+ (gud-jdb-skip-whitespace-and-comments)
+ (let ((s (point)))
+ (gud-jdb-skip-id-ish-thing)
+ (setq p (concat (buffer-substring s (point)) "."))
+ (gud-jdb-skip-whitespace-and-comments)
+ (if (eq (following-char) ?\073)
+ (forward-char))))
+
+ ;; Move point past an import statement.
+ ((looking-at "import")
+ (forward-char 6)
+ (gud-jdb-skip-whitespace-and-comments)
+ (gud-jdb-skip-id-ish-thing)
+ (gud-jdb-skip-whitespace-and-comments)
+ (if (eq (following-char) ?\073)
+ (forward-char)))
+
+ ;; Move point past the various kinds of ClassModifiers.
+ ((looking-at "public")
+ (forward-char 6))
+ ((looking-at "abstract")
+ (forward-char 8))
+ ((looking-at "final")
+ (forward-char 5))
+
+ ;; Move point past a ClassDeclaraction, but save the class
+ ;; Identifier.
+ ((looking-at "class")
+ (forward-char 5)
+ (gud-jdb-skip-whitespace-and-comments)
+ (let ((s (point)))
+ (gud-jdb-skip-id-ish-thing)
+ (setq
+ l (nconc l (list (concat p (buffer-substring s (point)))))))
+ (gud-jdb-skip-block))
+
+ ;; Move point past an interface statement.
+ ((looking-at "interface")
+ (forward-char 9)
+ (gud-jdb-skip-block))
+
+ ;; Anything else means the input is invalid.
+ (t
+ (message (format "Error parsing file %s." file))
+ (throw 'abort nil))))))
+ l))
+
+(defun gud-jdb-build-class-source-alist-for-file (file)
+ (mapcar
+ (lambda (c)
+ (cons c file))
+ (gud-jdb-analyze-source gud-jdb-analysis-buffer file)))
+
+;; Return an alist of fully qualified classes and the source files
+;; holding their definitions. SOURCES holds a list of all the source
+;; files to examine.
+(defun gud-jdb-build-class-source-alist (sources)
+ (setq gud-jdb-analysis-buffer (get-buffer-create " *gud-jdb-scratch*"))
+ (prog1
+ (apply
+ 'nconc
+ (mapcar
+ 'gud-jdb-build-class-source-alist-for-file
+ sources))
+ (kill-buffer gud-jdb-analysis-buffer)
+ (setq gud-jdb-analysis-buffer nil)))
+
+;; Change what was given in the minibuffer to something that can be used to
+;; invoke the debugger.
+(defun gud-jdb-massage-args (file args)
+ ;; The jdb executable must have whitespace between "-classpath" and
+ ;; its value while gud-common-init expects all switch values to
+ ;; follow the switch keyword without intervening whitespace. We
+ ;; require that when the user enters the "-classpath" switch in the
+ ;; EMACS minibuffer that they do so without the intervening
+ ;; whitespace. This function adds it back (it's called after
+ ;; gud-common-init). There are more switches like this (for
+ ;; instance "-host" and "-password") but I don't care about them
+ ;; yet.
+ (if args
+ (let (massaged-args user-error)
+
+ (while
+ (and args
+ (not (string-match "-classpath\\(.+\\)" (car args)))
+ (not (setq user-error
+ (string-match "-classpath$" (car args)))))
+ (setq massaged-args (append massaged-args (list (car args))))
+ (setq args (cdr args)))
+
+ ;; By this point the current directory is all screwed up. Maybe we
+ ;; could fix things and re-invoke gud-common-init, but for now I think
+ ;; issueing the error is good enough.
+ (if user-error
+ (progn
+ (kill-buffer (current-buffer))
+ (error "Error: Omit whitespace between '-classpath' and its value")))
+
+ (if args
+ (setq massaged-args
+ (append
+ massaged-args
+ (list "-classpath")
+ (list
+ (substring
+ (car args)
+ (match-beginning 1) (match-end 1)))
+ (cdr args)))
+ massaged-args))))
+
+;; Search for an association with P, a fully qualified class name, in
+;; gud-jdb-class-source-alist. The asssociation gives the fully
+;; qualified file name of the source file which produced the class.
+(defun gud-jdb-find-source-file (p)
+ (cdr (assoc p gud-jdb-class-source-alist)))
+
+;; See comentary for other debugger's marker filters - there you will find
+;; important notes about STRING.
+(defun gud-jdb-marker-filter (string)
+
+ ;; Build up the accumulator.
+ (setq gud-marker-acc
+ (if gud-marker-acc
+ (concat gud-marker-acc string)
+ string))
+
+ ;; We process STRING from left to right. Each time through the following
+ ;; loop we process at most one marker. The start variable keeps track of
+ ;; where we are in the input string through the iterations of this loop.
+ (let (start file-found)
+
+ ;; Process each complete marker in the input. There may be an incomplete
+ ;; marker at the end of the input string. Incomplete markers are left
+ ;; in the accumulator for processing the next time the function is called.
+ (while
+
+ ;; Do we see a marker?
+ (string-match
+ ;; jdb puts out a string of the following form when it
+ ;; hits a breakpoint:
+ ;;
+ ;; <fully-qualified-class><method> (<class>:<line-number>)
+ ;;
+ ;; <fully-qualified-class>'s are composed of Java ID's
+ ;; separated by periods. <method> and <class> are
+ ;; also Java ID's. <method> begins with a period and
+ ;; may contain less-than and greater-than (constructors,
+ ;; for instance, are called <init> in the symbol table.)
+ ;; Java ID's begin with a letter followed by letters
+ ;; and/or digits. The set of letters includes underscore
+ ;; and dollar sign.
+ ;;
+ ;; The first group matches <fully-qualified-class>,
+ ;; the second group matches <class> and the third group
+ ;; matches <line-number>. We don't care about using
+ ;; <method> so we don't "group" it.
+ ;;
+ ;; FIXME: Java ID's are UNICODE strings, this matches ASCII
+ ;; ID's only.
+ "\\([a-zA-Z0-9.$_]+\\)\\.[a-zA-Z0-9$_<>]+ (\\([a-zA-Z0-9$_]+\\):\\([0-9]+\\))"
+ gud-marker-acc start)
+
+ ;; Figure out the line on which to position the debugging arrow.
+ ;; Return the info as a cons of the form:
+ ;;
+ ;; (<file-name> . <line-number>) .
+ (if (setq
+ file-found
+ (gud-jdb-find-source-file
+ (substring gud-marker-acc
+ (match-beginning 1)
+ (match-end 1))))
+ (setq gud-last-frame
+ (cons
+ file-found
+ (string-to-int
+ (substring gud-marker-acc
+ (match-beginning 3)
+ (match-end 3)))))
+ (message "Could not find source file."))
+
+ ;; Set start after the last character of STRING that we've looked at
+ ;; and loop to look for another marker.
+ (setq start (match-end 0))))
+
+ ;; We don't filter any debugger output so just return what we were given.
+ string)
+
+(defun gud-jdb-find-file (f)
+ (and (file-readable-p f)
+ (find-file-noselect f)))
+
+(defvar gud-jdb-command-name "jdb" "Command that executes the Java debugger.")
+
+;;;###autoload
+(defun jdb (command-line)
+ "Run jdb with command line COMMAND-LINE in a buffer. The buffer is named
+\"*gud*\" if no initial class is given or \"*gud-<initial-class-basename>*\"
+if there is. If the \"-classpath\" switch is given, omit all whitespace
+between it and it's value."
+ (interactive
+ (list (read-from-minibuffer "Run jdb (like this): "
+ (if (consp gud-jdb-history)
+ (car gud-jdb-history)
+ (concat gud-jdb-command-name " "))
+ nil nil
+ 'gud-jdb-history)))
+
+ (gud-common-init command-line 'gud-jdb-massage-args
+ 'gud-jdb-marker-filter 'gud-jdb-find-file)
+
+ (gud-def gud-break "stop at %F:%l" "\C-b" "Set breakpoint at current line.")
+ (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line")
+ (gud-def gud-step "step" "\C-s" "Step one source line with display.")
+ (gud-def gud-next "next" "\C-n" "Step one line (skip functions).")
+ (gud-def gud-cont "cont" "\C-r" "Continue with display.")
+
+ (setq comint-prompt-regexp "^> \\|^.+\\[[0-9]+\\] ")
+ (setq paragraph-start comint-prompt-regexp)
+ (run-hooks 'jdb-mode-hook)
+
+ ;; Create and bind the class/source association list as well as the source
+ ;; file list.
+ (setq
+ gud-jdb-class-source-alist
+ (gud-jdb-build-class-source-alist
+ (setq
+ gud-jdb-source-files
+ (gud-jdb-build-source-files-list gud-jdb-directories "\\.java$")))))
+\f