]> code.delx.au - gnu-emacs-elpa/blob - aside/pdbtrack.el
a61546f077543e5a765ebe68364a289ab1936ae3
[gnu-emacs-elpa] / aside / pdbtrack.el
1 ;;; pdbtrack - Track source file lines as you run python/pdb in an emacs shell.
2
3 ;;; Standalone Python PDB dynamic file tracking.
4
5 ;;; CRAP. This was extracted from python.el, which lacks some crucial
6 ;;; features of my original pdbtrack code. python-mode.el (available via
7 ;;; marmalade, melpa) has a much more faithful version, including seeking
8 ;;; an existing buffer for a function if the indicated file can't be found
9 ;;; - crucial for doing remote debugging, eg via rpdb.
10 ;;;
11 ;;; I'm going to retire this code, for the moment, until I can recover
12 ;;; pdbtrack (plus whatever improvements may have been developed) from
13 ;;; python-mode.el.
14
15 (define-minor-mode pdbtrack-minor-mode
16 "Show lines in source file when Python PDB debugger steps through them."
17 nil ":PDBtrack" :require 'pdbtrack :version "2.1"
18
19 (add-hook 'comint-output-filter-functions
20 'pdbtrack-comint-output-filter-function)
21 (make-local-variable 'pdbtrack-buffers-to-kill)
22 (make-local-variable 'pdbtrack-tracked-buffer)
23 )
24
25 (defcustom pdbtrack-stacktrace-info-regexp
26 "> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()"
27 "Regular Expression matching stacktrace information.
28 Used to extract the current line and module being inspected."
29 :type 'string
30 :group 'python
31 :safe 'stringp)
32
33 (defvar pdbtrack-tracked-buffer nil
34 "Variable containing the value of the current tracked buffer.
35 Never set this variable directly, use
36 `pdbtrack-set-tracked-buffer' instead.")
37
38 (defcustom pdbtrack-remove-new-buffers-after-tracking t
39 "Remove buffers visited for the sake of tracking, on pdb session conclusion."
40 :type 'boolean
41 :group 'python)
42 (defvar pdbtrack-buffers-to-kill nil
43 "List of buffers to be deleted after tracking finishes.")
44
45 (defun pdbtrack-set-tracked-buffer (file-name)
46 "Set the buffer for FILE-NAME as the tracked buffer.
47 Internally it uses the `pdbtrack-tracked-buffer' variable.
48 Returns the tracked buffer."
49 (let ((file-buffer (get-file-buffer
50 (concat (file-remote-p default-directory)
51 file-name))))
52 (if file-buffer
53 (setq pdbtrack-tracked-buffer file-buffer)
54 (setq file-buffer (find-file-noselect file-name))
55 (when (not (member file-buffer pdbtrack-buffers-to-kill))
56 (add-to-list 'pdbtrack-buffers-to-kill file-buffer)))
57 file-buffer))
58
59 (defun pdbtrack-comint-output-filter-function (output)
60 "Move overlay arrow to current pdb line in tracked buffer.
61 Argument OUTPUT is a string with the output from the comint process."
62 (when (and pdbtrack-minor-mode (not (string= output "")))
63 (let* ((full-output (ansi-color-filter-apply
64 (buffer-substring comint-last-input-end (point-max))))
65 (line-number)
66 (file-name
67 (with-temp-buffer
68 (insert full-output)
69 ;; When the debugger encounters a pdb.set_trace()
70 ;; command, it prints a single stack frame. Sometimes
71 ;; it prints a bit of extra information about the
72 ;; arguments of the present function. When ipdb
73 ;; encounters an exception, it prints the _entire_ stack
74 ;; trace. To handle all of these cases, we want to find
75 ;; the _last_ stack frame printed in the most recent
76 ;; batch of output, then jump to the corresponding
77 ;; file/line number.
78 (goto-char (point-max))
79 (when (re-search-backward pdbtrack-stacktrace-info-regexp nil t)
80 (setq line-number (string-to-number
81 (match-string-no-properties 2)))
82 (match-string-no-properties 1)))))
83 (if (and file-name line-number)
84 (let* ((tracked-buffer
85 (pdbtrack-set-tracked-buffer file-name))
86 (shell-buffer (current-buffer))
87 (tracked-buffer-window (get-buffer-window tracked-buffer))
88 (tracked-buffer-line-pos))
89 (with-current-buffer tracked-buffer
90 (set (make-local-variable 'overlay-arrow-string) "=>")
91 (set (make-local-variable 'overlay-arrow-position) (make-marker))
92 (setq tracked-buffer-line-pos (progn
93 (goto-char (point-min))
94 (forward-line (1- line-number))
95 (point-marker)))
96 (when tracked-buffer-window
97 (set-window-point
98 tracked-buffer-window tracked-buffer-line-pos))
99 (set-marker overlay-arrow-position tracked-buffer-line-pos))
100 (pop-to-buffer tracked-buffer)
101 (switch-to-buffer-other-window shell-buffer))
102 (when pdbtrack-tracked-buffer
103 (with-current-buffer pdbtrack-tracked-buffer
104 (set-marker overlay-arrow-position nil))
105 (when (not pdbtrack-remove-new-buffers-after-tracking)
106 (mapc #'(lambda (buffer)
107 (ignore-errors (kill-buffer buffer)))
108 pdbtrack-buffers-to-kill))
109 (setq pdbtrack-tracked-buffer nil
110 pdbtrack-buffers-to-kill nil)))))
111 output)
112
113 (defun pdbtrack-cherry-pick-buffer (funcname lineno)
114 "Find most recent buffer having name or having function named FUNCNAME.
115 We walk the buffer-list history for python-mode buffers that are
116 named for funcname or define a function funcname."
117 (let ((buffers (buffer-list))
118 buf
119 got)
120 (while (and buffers (not got))
121 (setq buf (car buffers)
122 buffers (cdr buffers))
123 (if (and (save-excursion (set-buffer buf)
124 (string= major-mode "python-mode"))
125 (or (string-match funcname (buffer-name buf))
126 (string-match (concat "^\\s-*\\(def\\|class\\)\\s-+"
127 funcname "\\s-*(")
128 (save-excursion
129 (set-buffer buf)
130 (buffer-substring (point-min)
131 (point-max))))))
132 (setq got buf)))
133 got))
134
135 (provide 'pdbtrack)