[Python-Dev] Enhancement to pdb in gud.el
Nick Roberts
nick at nick.uklinux.net
Sat Sep 20 11:20:50 EDT 2003
Kevin J. Butler writes:
> ...
>
> After my patch:
> (Pdb) cl 3
> Deleted breakpoint 3 at z:\work\wm\wm.py:136
> (Pdb) cl wm.py:17
> Deleted breakpoint 5 at z:\work\wm\wm.py:17
I've applied your patch manually to my files bdb.py and pdb.py (I have 2.2)
and it appears to work. If you load the file below in Emacs (M-x eval-buffer
or M-x load-file), then type M-x pdb, the debugger (with your patch) should
work with breakpoint icons in the display margin. Emacs must be a version from
the CVS repository to work straight away. This will also give you buttons for
debugging if you turn the toolbar on (M-x tool-bar-mode).
I've tested it on GNU/Linux but it should work on Windows too if I've defined
the regexp correctly.
I've just noticed that you can enable/disable breakpoints in python. In my
mode for gdb, the breakpoints are greyed out when the breakpoints become
disabled. I could do this for python too if these commands were changed to
emit a message.
If your patches are accepted (by SF?) then I will integrate my changes into
gud.el in the Emacs CVS repository.
Nick
-----------------------------------------------------
(require 'gud)
(defun gud-sentinel (proc msg)
(cond ((null (buffer-name (process-buffer proc)))
;; buffer killed
;; Stop displaying an arrow in a source file.
(setq overlay-arrow-position nil)
(set-process-buffer proc nil)
(if (memq gud-minor-mode-type '(gdba pdb))
(gdb-reset)
(gud-reset)))
((memq (process-status proc) '(signal exit))
;; Stop displaying an arrow in a source file.
(setq overlay-arrow-position nil)
(with-current-buffer gud-comint-buffer
(if (memq gud-minor-mode '(gdba pdb))
(gdb-reset)
(gud-reset)))
(let* ((obuf (current-buffer)))
;; save-excursion isn't the right thing if
;; process-buffer is current-buffer
(unwind-protect
(progn
;; Write something in *compilation* and hack its mode line,
(set-buffer (process-buffer proc))
;; Fix the mode line.
(setq mode-line-process
(concat ":"
(symbol-name (process-status proc))))
(force-mode-line-update)
(if (eobp)
(insert ?\n mode-name " " msg)
(save-excursion
(goto-char (point-max))
(insert ?\n mode-name " " msg)))
;; If buffer and mode line will show that the process
;; is dead, we can delete it now. Otherwise it
;; will stay around until M-x list-processes.
(delete-process proc))
;; Restore old buffer, but don't restore old point
;; if obuf is the gud buffer.
(set-buffer obuf))))))
(defun gdb-reset ()
"Exit a debugging session cleanly by killing the gdb buffers and resetting
the source buffers."
(dolist (buffer (buffer-list))
(if (not (eq buffer gud-comint-buffer))
(with-current-buffer buffer
(if (memq gud-minor-mode '(gdba pdb))
(if (string-match "^\*.+*$" (buffer-name))
(kill-buffer nil)
(if (display-images-p)
(remove-images (point-min) (point-max))
(gdb-remove-strings (point-min) (point-max)))
(setq left-margin-width 0)
(setq gud-minor-mode nil)
(kill-local-variable 'tool-bar-map)
(setq gud-running nil)
(if (get-buffer-window (current-buffer))
(set-window-margins (get-buffer-window
(current-buffer))
left-margin-width
right-margin-width))))))))
(defun gdb-put-string (putstring pos)
"Put string PUTSTRING in front of POS in the current buffer.
PUTSTRING is displayed by putting an overlay into the current buffer with a
`before-string' STRING that has a `display' property whose value is
PUTSTRING."
(let ((gdb-string "x")
(buffer (current-buffer)))
(let ((overlay (make-overlay pos pos buffer))
(prop (list (list 'margin 'left-margin) putstring)))
(put-text-property 0 (length gdb-string) 'display prop gdb-string)
(overlay-put overlay 'put-break t)
(overlay-put overlay 'before-string gdb-string))))
(defun gdb-remove-strings (start end &optional buffer)
"Remove strings between START and END in BUFFER.
Remove only strings that were put in BUFFER with calls to `put-string'.
BUFFER nil or omitted means use the current buffer."
(unless buffer
(setq buffer (current-buffer)))
(let ((overlays (overlays-in start end)))
(while overlays
(let ((overlay (car overlays)))
(when (overlay-get overlay 'put-break)
(delete-overlay overlay)))
(setq overlays (cdr overlays)))))
(defconst breakpoint-xpm-data "/* XPM */
static char *magick[] = {
/* columns rows colors chars-per-pixel */
\"12 12 2 1\",
\" c red\",
\"+ c None\",
/* pixels */
\"++++++++++++\",
\"+++ +++\",
\"++ ++\",
\"+ +\",
\"+ +\",
\"+ +\",
\"+ +\",
\"+ +\",
\"+ +\",
\"++ ++\",
\"+++ +++\",
\"++++++++++++\"
};"
"XPM data used for breakpoint icon.")
(defconst breakpoint-enabled-pbm-data
"P1
12 12\",
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0
0 0 1 1 1 1 1 1 1 1 0 0
0 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 1 0
0 0 1 1 1 1 1 1 1 1 0 0
0 0 0 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0"
"PBM data used for enabled breakpoint icon.")
(defvar breakpoint-enabled-icon
(find-image `((:type xpm :data ,breakpoint-xpm-data)
(:type pbm :data ,breakpoint-enabled-pbm-data)))
"Icon for enabled breakpoint in display margin")
;; ======================================================================
;; pdb (Python debugger) functions
;; History of argument lists passed to pdb.
(defvar gud-pdb-history nil)
;; 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 "^> ")
(defvar gud-pdb-marker-regexp-breakpoint
"reakpoint [0-9]+ at \\(\\([a-zA-Z]:\\)?[^:\n]*\\):\\([0-9]*\\)\n")
;; 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))))
(if (string-match (concat "B" gud-pdb-marker-regexp-breakpoint)
gud-marker-acc)
(let ((file (match-string 1 gud-marker-acc))
(line (match-string 3 gud-marker-acc)))
(gud-pdb-insert-breakpoint file line)))
(if (string-match (concat "Deleted b" gud-pdb-marker-regexp-breakpoint)
gud-marker-acc)
(let ((file (match-string 1 gud-marker-acc))
(line (match-string 3 gud-marker-acc)))
(gud-pdb-remove-breakpoint file line)))
;; 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-insert-breakpoint (file line)
(with-current-buffer (find-file-noselect file)
(save-current-buffer
(set (make-local-variable 'gud-minor-mode) 'pdb)
(set (make-local-variable 'tool-bar-map) gud-tool-bar-map)
(setq left-margin-width 2)
(if (get-buffer-window (current-buffer))
(set-window-margins (get-buffer-window (current-buffer))
left-margin-width right-margin-width)))
(save-excursion
(goto-line (string-to-number line))
(let ((start (progn (beginning-of-line)
(- (point) 1)))
(end (progn (end-of-line) (+ (point) 1))))
(if (display-images-p)
(progn
(remove-images start end)
(put-image breakpoint-enabled-icon
(+ start 1)
"breakpoint icon enabled"
'left-margin))
(gdb-remove-strings start end)
(gdb-put-string "B" (+ start 1)))))))
(defun gud-pdb-remove-breakpoint (file line)
(with-current-buffer (find-file-noselect file)
(save-excursion
(goto-line (string-to-number line))
(let ((start (progn (beginning-of-line)
(- (point) 1)))
(end (progn (end-of-line) (+ (point) 1))))
(if (display-images-p)
(remove-images start end)
(gdb-remove-strings start end))))))
(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)
(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 (gud-query-cmdline 'pdb)))
(gud-common-init command-line nil 'gud-pdb-marker-filter)
(set (make-local-variable 'gud-minor-mode) 'pdb)
(gud-def gud-break "break %l" "\C-b" "Set breakpoint at current line.")
(gud-def gud-remove "clear %f:%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.")
;; (setq comint-prompt-regexp "^(.*pdb[+]?) *")
(setq comint-prompt-regexp "^(Pdb) *")
(setq paragraph-start comint-prompt-regexp)
(run-hooks 'pdb-mode-hook))
More information about the Python-Dev
mailing list