unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* python: Let pdb tracking not kill buffers
@ 2019-10-04 20:32 Andrii Kolomoiets
  2019-10-05  6:40 ` Eli Zaretskii
  0 siblings, 1 reply; 12+ messages in thread
From: Andrii Kolomoiets @ 2019-10-04 20:32 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1314 bytes --]

Hello,

Let's start from example:
1. echo "import pdb; pdb.set_trace()" > test.py
2. emacs -Q
3. M-x run-python
4. M-x python-shell-send-file <RET> test.py <RET>

Now there are two windows: one with pdb session and another one with
source code.
Now in pdb prompt: pass<RET>

The source code buffer is killed because pdb tracking comint output
filter doesn't found file name in the output and decides that tracking
session is over.

This behavior interferes with debug session. Moving frame up/down the
stack trace open new files but evaluating some code kill them when they
are still needed.

Another reason to not kill buffers is that I want the file opened during
debug session stay there when the debug session is over because I want
to change it.

My point is: it's hard to tell that debug session is over judging from
the output but it can be determined by inspecting the input.

Attached patch brings the following changes:

- New variable `python-pdbtrack-kill-buffers' that make buffers killing
  optional;

- Comint input filter which decides that the debug session is over;

- Process sentinel to finish tracking when python process is killed.

Please see attached patch. I certainly sure docstrings and naming are
not so good but they can be fine tuned later if the main idea will be
accepted.

Thanks.

[-- Attachment #2: pdb-track.patch --]
[-- Type: application/octet-stream, Size: 9135 bytes --]

diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index ae5aff351c..4f9dfba8b3 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -2795,7 +2795,6 @@ inferior-python-mode
   (set (make-local-variable 'comint-output-filter-functions)
        '(ansi-color-process-output
          python-shell-comint-watch-for-first-prompt-output-filter
-         python-pdbtrack-comint-output-filter-function
          python-comint-postoutput-scroll-to-bottom
          comint-watch-for-password-prompt))
   (set (make-local-variable 'compilation-error-regexp-alist)
@@ -2809,7 +2808,8 @@ inferior-python-mode
   (make-local-variable 'python-shell-internal-last-output)
   (when python-shell-font-lock-enable
     (python-shell-font-lock-turn-on))
-  (compilation-shell-minor-mode 1))
+  (compilation-shell-minor-mode 1)
+  (python-pdbtrack-setup-tracking))
 
 (defun python-shell-make-comint (cmd proc-name &optional show internal)
   "Create a Python shell comint buffer.
@@ -3744,13 +3744,29 @@ python-pdbtrack-activate
   :safe 'booleanp)
 
 (defcustom python-pdbtrack-stacktrace-info-regexp
-  "> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()"
+  "> \\([^\"(]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()"
   "Regular expression matching stacktrace information.
 Used to extract the current line and module being inspected."
   :type 'string
   :group 'python
   :safe 'stringp)
 
+(defcustom python-pdbtrack-continue-command '("c" "cont" "continue")
+  "Pdb continue command."
+  :type 'list
+  :group 'python)
+
+(defcustom python-pdbtrack-untrack-commands '("c" "cont" "continue" "q" "quit" "exit")
+  "Pdb commands that finish tracking session."
+  :type 'list
+  :group 'python)
+
+(defcustom python-pdbtrack-kill-buffers t
+  "Kill buffers when tracking is finished.
+Only buffers opened during tracking will be killed."
+  :type 'boolean
+  :group 'python)
+
 (defvar python-pdbtrack-tracked-buffer nil
   "Variable containing the value of the current tracked buffer.
 Never set this variable directly, use
@@ -3759,6 +3775,9 @@ python-pdbtrack-tracked-buffer
 (defvar python-pdbtrack-buffers-to-kill nil
   "List of buffers to be deleted after tracking finishes.")
 
+(defvar python-pdbtrack-prev-command-continue nil
+  "Is t if previous pdb command was 'continue'.")
+
 (defun python-pdbtrack-set-tracked-buffer (file-name)
   "Set the buffer for FILE-NAME as the tracked buffer.
 Internally it uses the `python-pdbtrack-tracked-buffer' variable.
@@ -3766,8 +3785,7 @@ python-pdbtrack-set-tracked-buffer
   (let* ((file-name-prospect (concat (file-remote-p default-directory)
                               file-name))
          (file-buffer (get-file-buffer file-name-prospect)))
-    (if file-buffer
-        (setq python-pdbtrack-tracked-buffer file-buffer)
+    (unless file-buffer
       (cond
        ((file-exists-p file-name-prospect)
         (setq file-buffer (find-file-noselect file-name-prospect)))
@@ -3775,10 +3793,52 @@ python-pdbtrack-set-tracked-buffer
              (file-exists-p file-name))
         ;; Fallback to a locally available copy of the file.
         (setq file-buffer (find-file-noselect file-name-prospect))))
-      (when (not (member file-buffer python-pdbtrack-buffers-to-kill))
+      (when (and python-pdbtrack-kill-buffers
+                 (not (member file-buffer python-pdbtrack-buffers-to-kill)))
         (add-to-list 'python-pdbtrack-buffers-to-kill file-buffer)))
+    (setq python-pdbtrack-tracked-buffer file-buffer)
     file-buffer))
 
+(defun python-pdbtrack-unset-tracked-buffer ()
+  "Untrack currently tracked buffer."
+  (when python-pdbtrack-tracked-buffer
+    (with-current-buffer python-pdbtrack-tracked-buffer
+      (set-marker overlay-arrow-position nil))
+    (setq python-pdbtrack-tracked-buffer nil)))
+
+(defun python-pdbtrack-tracking-finish ()
+  "Finish tracking."
+  (python-pdbtrack-unset-tracked-buffer)
+  (when python-pdbtrack-kill-buffers
+      (mapc #'(lambda (buffer)
+                (ignore-errors (kill-buffer buffer)))
+            python-pdbtrack-buffers-to-kill))
+  (setq python-pdbtrack-buffers-to-kill nil))
+
+(defun python-pdbtrack-process-sentinel (process _event)
+  "Untrack buffers when PROCESS is killed."
+  (unless (process-live-p process)
+    (python-pdbtrack-tracking-finish)))
+
+(defun python-pdbtrack-comint-input-filter-function (input)
+  "Finish tracking session depending on command in INPUT.
+Commands that must finish tracking session is listed in
+`python-pdbtrack-untracking-commands'."
+  (when (and python-pdbtrack-tracked-buffer
+             ;; Empty input is sent by C-d or `comint-send-eof'
+             (or (string-empty-p input)
+                 ;; "n some text" is "n" command for pdb. Split input and get firs part
+                 (let* ((command (car (split-string (string-trim input) " "))))
+                   (setq python-pdbtrack-prev-command-continue
+                         (or (member command python-pdbtrack-continue-command)
+                             ;; if command is empty and previous command was 'continue'
+                             ;; then current command is 'continue' too.
+                             (and (string-empty-p command)
+                                  python-pdbtrack-prev-command-continue)))
+                   (or python-pdbtrack-prev-command-continue
+                       (member command python-pdbtrack-untrack-commands)))))
+    (python-pdbtrack-tracking-finish)))
+
 (defun python-pdbtrack-comint-output-filter-function (output)
   "Move overlay arrow to current pdb line in tracked buffer.
 Argument OUTPUT is a string with the output from the comint process."
@@ -3798,19 +3858,27 @@ python-pdbtrack-comint-output-filter-function
               ;; the _last_ stack frame printed in the most recent
               ;; batch of output, then jump to the corresponding
               ;; file/line number.
+              ;; Parse output only if at pdb prompt to avoid double code
+              ;; run in situation when output and pdb prompt received in
+              ;; different hunks
               (goto-char (point-max))
-              (when (re-search-backward python-pdbtrack-stacktrace-info-regexp nil t)
+              (goto-char (line-beginning-position))
+              (when (and (looking-at python-shell-prompt-pdb-regexp)
+                         (re-search-backward python-pdbtrack-stacktrace-info-regexp nil t))
                 (setq line-number (string-to-number
                                    (match-string-no-properties 2)))
                 (match-string-no-properties 1)))))
-      (if (and file-name line-number)
-          (let* ((tracked-buffer
-                  (python-pdbtrack-set-tracked-buffer file-name))
+      (when (and file-name line-number)
+        (if (string-prefix-p "<" file-name)
+            ;; Finish tracking session if stacktrace info is like
+            ;; "> <stdin>(1)<module>()->None"
+            (python-pdbtrack-tracking-finish)
+          (python-pdbtrack-unset-tracked-buffer)
+          (let* ((tracked-buffer (python-pdbtrack-set-tracked-buffer file-name))
                  (shell-buffer (current-buffer))
                  (tracked-buffer-window (get-buffer-window tracked-buffer))
                  (tracked-buffer-line-pos))
             (with-current-buffer tracked-buffer
-              (set (make-local-variable 'overlay-arrow-string) "=>")
               (set (make-local-variable 'overlay-arrow-position) (make-marker))
               (setq tracked-buffer-line-pos (progn
                                               (goto-char (point-min))
@@ -3821,17 +3889,18 @@ python-pdbtrack-comint-output-filter-function
                  tracked-buffer-window tracked-buffer-line-pos))
               (set-marker overlay-arrow-position tracked-buffer-line-pos))
             (pop-to-buffer tracked-buffer)
-            (switch-to-buffer-other-window shell-buffer))
-        (when python-pdbtrack-tracked-buffer
-          (with-current-buffer python-pdbtrack-tracked-buffer
-            (set-marker overlay-arrow-position nil))
-          (mapc #'(lambda (buffer)
-                    (ignore-errors (kill-buffer buffer)))
-                python-pdbtrack-buffers-to-kill)
-          (setq python-pdbtrack-tracked-buffer nil
-                python-pdbtrack-buffers-to-kill nil)))))
+            (switch-to-buffer-other-window shell-buffer))))))
   output)
 
+(defun python-pdbtrack-setup-tracking ()
+  "Setup pdb tracking in current buffer."
+  (add-to-list (make-local-variable 'comint-input-filter-functions)
+               #'python-pdbtrack-comint-input-filter-function)
+  (add-to-list (make-local-variable 'comint-output-filter-functions)
+               #'python-pdbtrack-comint-output-filter-function)
+  (set-process-sentinel (get-buffer-process (current-buffer))
+                        #'python-pdbtrack-process-sentinel))
+
 \f
 ;;; Symbol completion
 

^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2019-11-07 16:57 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-10-04 20:32 python: Let pdb tracking not kill buffers Andrii Kolomoiets
2019-10-05  6:40 ` Eli Zaretskii
2019-10-07  4:33   ` Lawrence Liu
2019-10-07 16:19     ` Eli Zaretskii
2019-10-07 16:26       ` Lawrence Liu
2019-10-07 12:28   ` Andrii Kolomoiets
2019-10-30 19:14     ` Andrii Kolomoiets
2019-11-01  9:35       ` Eli Zaretskii
2019-11-02 16:37         ` Andrii Kolomoiets
2019-11-02 17:29           ` Eli Zaretskii
2019-11-02 18:51             ` Andrii Kolomoiets
2019-11-07 16:57               ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).