changeset 92060:fd85a7810d53

Revise pdbtrack functionality to incorporate advances in python-mode.el. (I'm doing this python.el checkin with some byte-compiler warnings. These warnings existed before Nick Roberts or I applied any of the pdbtrack changes, and look very clearly like preexisting, incomplete adoption of code from python-mode.el. I'm going to next look at settling those warnings, though I don't have time for a major reconciliation of the two python-mode implementations.) (python-pdbtrack-toggle-stack-tracking): Clarify docstring. (python-pdbtrack-minor-mode-string): A sign indicating that pdb tracking is happening. (python-pdbtrack-stack-entry-regexp): Better recognize stack traces. (python-pdbtrack-input-prompt): Better recognize PDB prompts. (add python-pdbtrack-track-stack-file to comint-output-filter-functions): Tracking is plugged in to all comint buffers once python.el is loaded. (python-pdbtrack-overlay-arrow): Toggle activation of `python-pdbtrack-minor-mode-string' in addition to the overlay arrow. (python-pdbtrack-track-stack-file): Use new `python-pdbtrack-get-source-buffer' for more flexible access to debugging source files. (python-pdbtrack-get-source-buffer): Identify debugging target buffer according to pdb stack trace, optionally using new `python-pdbtrack-grub-for-buffer' if file is not locally available. (python-pdbtrack-grub-for-buffer): Find most recent python-mode named buffer, or having function with indicated name. (python-shell): Remove comint-output-filter-functions hook addition, it's being done elsewhere. Wrap long line.
author Ken Manheimer <ken.manheimer@gmail.com>
date Thu, 21 Feb 2008 22:28:13 +0000
parents a22a49c13e2b
children 2b55e8843eff
files lisp/ChangeLog lisp/progmodes/python.el
diffstat 2 files changed, 173 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Thu Feb 21 20:29:34 2008 +0000
+++ b/lisp/ChangeLog	Thu Feb 21 22:28:13 2008 +0000
@@ -1,3 +1,30 @@
+2008-02-21  Ken Manheimer  <ken.manheimer@gmail.com>
+
+	* python.el (python-pdbtrack-toggle-stack-tracking): Clarify
+	docstring.
+	(python-pdbtrack-minor-mode-string): A sign indicating that pdb
+	tracking is happening.
+	(python-pdbtrack-stack-entry-regexp): Better recognize stack
+	traces.
+	(python-pdbtrack-input-prompt): Better recognize PDB prompts.
+	(add python-pdbtrack-track-stack-file to
+	comint-output-filter-functions): Tracking is plugged in to all
+	comint buffers once python.el is loaded.
+	(python-pdbtrack-overlay-arrow): Toggle activation of
+	`python-pdbtrack-minor-mode-string' in addition to the overlay
+	arrow.
+	(python-pdbtrack-track-stack-file): Use new
+	`python-pdbtrack-get-source-buffer' for more flexible access to
+	debugging source files.
+	(python-pdbtrack-get-source-buffer): Identify debugging target
+	buffer according to pdb stack trace, optionally using new
+	`python-pdbtrack-grub-for-buffer' if file is not locally
+	available.
+	(python-pdbtrack-grub-for-buffer): Find most recent python-mode
+	named buffer, or having function with indicated name.
+	(python-shell): Remove comint-output-filter-functions hook
+	addition, it's being done elsewhere.  Wrap long line.
+
 2008-02-21  Michael Olson  <mwolson@gnu.org>
 
 	* net/json.el: Install new file by Edward O'Connor.  json.el is a
--- a/lisp/progmodes/python.el	Thu Feb 21 20:29:34 2008 +0000
+++ b/lisp/progmodes/python.el	Thu Feb 21 22:28:13 2008 +0000
@@ -545,15 +545,29 @@
 
 (defcustom python-pdbtrack-do-tracking-p t
   "*Controls whether the pdbtrack feature is enabled or not.
+
 When non-nil, pdbtrack is enabled in all comint-based buffers,
-e.g. shell buffers and the *Python* buffer.  When using pdb to debug a
-Python program, pdbtrack notices the pdb prompt and displays the
-source file and line that the program is stopped at, much the same way
-as gud-mode does for debugging C programs with gdb."
+e.g. shell interaction buffers and the *Python* buffer.
+
+When using pdb to debug a Python program, pdbtrack notices the
+pdb prompt and presents the line in the source file where the
+program is stopped in a pop-up buffer.  It's similar to what
+gud-mode does for debugging C programs with gdb, but without
+having to restart the program."
   :type 'boolean
   :group 'python)
 (make-variable-buffer-local 'python-pdbtrack-do-tracking-p)
 
+(defcustom python-pdbtrack-minor-mode-string " PDB"
+  "*Minor-mode sign to be displayed when pdbtrack is active."
+  :type 'string
+  :group 'python)
+
+;; Add a designator to the minor mode strings
+(or (assq 'python-pdbtrack-is-tracking-p minor-mode-alist)
+    (push '(python-pdbtrack-is-tracking-p python-pdbtrack-minor-mode-string)
+	  minor-mode-alist))
+
 ;; Bind python-file-queue before installing the kill-emacs-hook.
 (defvar python-file-queue nil
   "Queue of Python temp files awaiting execution.
@@ -562,10 +576,10 @@
 (defvar python-pdbtrack-is-tracking-p nil)
 
 (defconst python-pdbtrack-stack-entry-regexp
-  "> \\([^(]+\\)(\\([0-9]+\\))[?a-zA-Z0-9_]+()"
+  "^> \\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_]+\\)()"
   "Regular expression pdbtrack uses to find a stack trace entry.")
 
-(defconst python-pdbtrack-input-prompt "\n[(<]?pdb[>)]? "
+(defconst python-pdbtrack-input-prompt "\n[(<]*[Pp]db[>)]+ "
   "Regular expression pdbtrack uses to recognize a pdb prompt.")
 
 (defconst python-pdbtrack-track-range 10000
@@ -2375,6 +2389,10 @@
 
 ;;;; Modes.
 
+;; pdb tracking is alert once this file is loaded, but takes no action if
+;; `python-pdbtrack-do-tracking-p' is nil.
+(add-hook 'comint-output-filter-functions 'python-pdbtrack-track-stack-file)
+
 (defvar outline-heading-end-regexp)
 (defvar eldoc-documentation-function)
 (defvar python-mode-running)            ;Dynamically scoped var.
@@ -2700,17 +2718,16 @@
 	  (python-execute-file pyproc (car python-file-queue))))))
 
 (defun python-pdbtrack-overlay-arrow (activation)
-  "Activate or de arrow at beginning-of-line in current buffer."
-  ;; This was derived/simplified from edebug-overlay-arrow
-  (cond (activation
-	 (setq overlay-arrow-position (make-marker))
-	 (setq overlay-arrow-string "=>")
-	 (set-marker overlay-arrow-position
-		     (python-point 'bol) (current-buffer))
-	 (setq python-pdbtrack-is-tracking-p t))
-	(python-pdbtrack-is-tracking-p
-	 (setq overlay-arrow-position nil)
-	 (setq python-pdbtrack-is-tracking-p nil))))
+  "Activate or deactivate arrow at beginning-of-line in current buffer."
+  (if activation
+      (progn
+        (setq overlay-arrow-position (make-marker)
+              overlay-arrow-string "=>"
+              python-pdbtrack-is-tracking-p t)
+        (set-marker overlay-arrow-position
+                    (python-point 'bol) (current-buffer)))
+    (setq overlay-arrow-position nil
+          python-pdbtrack-is-tracking-p nil)))
 
 (defun python-pdbtrack-track-stack-file (text)
   "Show the file indicated by the pdb stack entry line, in a separate window.
@@ -2718,49 +2735,131 @@
 Activity is disabled if the buffer-local variable
 `python-pdbtrack-do-tracking-p' is nil.
 
-We depend on the pdb input prompt matching `python-pdbtrack-input-prompt'
-at the beginning of the line."
+We depend on the pdb input prompt being a match for
+`python-pdbtrack-input-prompt'.
+
+If the traceback target file path is invalid, we look for the
+most recently visited python-mode buffer which either has the
+name of the current function or class, or which defines the
+function or class.  This is to provide for scripts not in the
+local filesytem (e.g., Zope's 'Script \(Python)', but it's not
+Zope specific).  If you put a copy of the script in a buffer
+named for the script and activate python-mode, then pdbtrack will
+find it."
   ;; Instead of trying to piece things together from partial text
   ;; (which can be almost useless depending on Emacs version), we
   ;; monitor to the point where we have the next pdb prompt, and then
   ;; check all text from comint-last-input-end to process-mark.
   ;;
-  ;; KLM: It might be nice to provide an optional override, so this
-  ;; routine could be fed debugger output strings as the text
-  ;; argument, for deliberate application elsewhere.
-  ;;
-  ;; KLM: We're very conservative about clearing the overlay arrow, to
-  ;; minimize residue.  This means, for instance, that executing other
-  ;; pdb commands wipes out the highlight.
+  ;; Also, we're very conservative about clearing the overlay arrow,
+  ;; to minimize residue.  This means, for instance, that executing
+  ;; other pdb commands wipe out the highlight.  You can always do a
+  ;; 'where' (aka 'w') PDB command to reveal the overlay arrow.
+
   (let* ((origbuf (current-buffer))
 	 (currproc (get-buffer-process origbuf)))
+
     (if (not (and currproc python-pdbtrack-do-tracking-p))
         (python-pdbtrack-overlay-arrow nil)
-      (let* (;(origdir default-directory)
-             (procmark (process-mark currproc))
+
+      (let* ((procmark (process-mark currproc))
              (block (buffer-substring (max comint-last-input-end
                                            (- procmark
                                               python-pdbtrack-track-range))
                                       procmark))
-             fname lineno)
+             target target_fname target_lineno target_buffer)
+
         (if (not (string-match (concat python-pdbtrack-input-prompt "$") block))
             (python-pdbtrack-overlay-arrow nil)
-          (if (not (string-match
-                    (concat ".*" python-pdbtrack-stack-entry-regexp ".*")
-                    block))
-              (python-pdbtrack-overlay-arrow nil)
-            (setq fname (match-string 1 block)
-                  lineno (match-string 2 block))
-            (if (file-exists-p fname)
-                (progn
-                  (find-file-other-window fname)
-                  (goto-line (string-to-number lineno))
-                  (message "pdbtrack: line %s, file %s" lineno fname)
-                  (python-pdbtrack-overlay-arrow t)
-                  (pop-to-buffer origbuf t) )
-              (if (= (elt fname 0) ?\<)
-                  (message "pdbtrack: (Non-file source: '%s')" fname)
-                (message "pdbtrack: File not found: %s" fname)))))))))
+
+          (setq target (python-pdbtrack-get-source-buffer block))
+
+          (if (stringp target)
+              (progn
+                (python-pdbtrack-overlay-arrow nil)
+                (message "pdbtrack: %s" target))
+
+            (setq target_lineno (car target)
+                  target_buffer (cadr target)
+                  target_fname (buffer-file-name target_buffer))
+            (switch-to-buffer-other-window target_buffer)
+            (goto-line target_lineno)
+            (message "pdbtrack: line %s, file %s" target_lineno target_fname)
+            (python-pdbtrack-overlay-arrow t)
+            (pop-to-buffer origbuf t)
+            ;; in large shell buffers, above stuff may cause point to lag output
+            (goto-char procmark)
+            )))))
+  )
+
+(defun python-pdbtrack-get-source-buffer (block)
+  "Return line number and buffer of code indicated by block's traceback text.
+
+We look first to visit the file indicated in the trace.
+
+Failing that, we look for the most recently visited python-mode buffer
+with the same name or having the named function.
+
+If we're unable find the source code we return a string describing the
+problem."
+
+  (if (not (string-match python-pdbtrack-stack-entry-regexp block))
+
+      "Traceback cue not found"
+
+    (let* ((filename (match-string 1 block))
+           (lineno (string-to-number (match-string 2 block)))
+           (funcname (match-string 3 block))
+           funcbuffer)
+
+      (cond ((file-exists-p filename)
+             (list lineno (find-file-noselect filename)))
+
+            ((setq funcbuffer (python-pdbtrack-grub-for-buffer funcname lineno))
+             (if (string-match "/Script (Python)$" filename)
+                 ;; Add in number of lines for leading '##' comments:
+                 (setq lineno
+                       (+ lineno
+                          (save-excursion
+                            (set-buffer funcbuffer)
+                            (if (equal (point-min)(point-max))
+                                0
+                              (count-lines
+                               (point-min)
+                               (max (point-min)
+                                    (string-match "^\\([^#]\\|#[^#]\\|#$\\)"
+                                                  (buffer-substring
+                                                   (point-min) (point-max)))
+                                    )))))))
+               (list lineno funcbuffer))
+
+            ((= (elt filename 0) ?\<)
+             (format "(Non-file source: '%s')" filename))
+
+            (t (format "Not found: %s(), %s" funcname filename)))
+      )
+    )
+  )
+
+(defun python-pdbtrack-grub-for-buffer (funcname lineno)
+  "Find recent python-mode buffer named, or having function named funcname."
+  (let ((buffers (buffer-list))
+        buf
+        got)
+    (while (and buffers (not got))
+      (setq buf (car buffers)
+            buffers (cdr buffers))
+      (if (and (save-excursion (set-buffer buf)
+                               (string= major-mode "python-mode"))
+               (or (string-match funcname (buffer-name buf))
+                   (string-match (concat "^\\s-*\\(def\\|class\\)\\s-+"
+                                         funcname "\\s-*(")
+                                 (save-excursion
+                                   (set-buffer buf)
+                                   (buffer-substring (point-min)
+                                                     (point-max))))))
+          (setq got buf)))
+    got))
 
 (defun python-toggle-shells (arg)
   "Toggles between the CPython and JPython shells.
@@ -2857,14 +2956,12 @@
     (switch-to-buffer-other-window
      (apply 'make-comint python-which-bufname python-which-shell nil args))
     (make-local-variable 'comint-prompt-regexp)
-    (set-process-sentinel (get-buffer-process (current-buffer)) 'python-sentinel)
+    (set-process-sentinel (get-buffer-process (current-buffer))
+                          'python-sentinel)
     (setq comint-prompt-regexp "^>>> \\|^[.][.][.] \\|^(pdb) ")
     (add-hook 'comint-output-filter-functions
 	      'python-comint-output-filter-function nil t)
     ;; pdbtrack
-    (add-hook 'comint-output-filter-functions
-	      'python-pdbtrack-track-stack-file nil t)
-    (setq python-pdbtrack-do-tracking-p t)
     (set-syntax-table python-mode-syntax-table)
     (use-local-map python-shell-map)))