+\f
+
+;; pdbtrack features
+
+(defun python-comint-output-filter-function (string)
+ "Watch output for Python prompt and exec next file waiting in queue.
+This function is appropriate for `comint-output-filter-functions'."
+ ;; TBD: this should probably use split-string
+ (when (and (string-match python--prompt-regexp string)
+ python-file-queue)
+ (condition-case nil
+ (delete-file (car python-file-queue))
+ (error nil))
+ (setq python-file-queue (cdr python-file-queue))
+ (if python-file-queue
+ (let ((pyproc (get-buffer-process (current-buffer))))
+ (python-execute-file pyproc (car python-file-queue))))))
+
+(defun python-pdbtrack-overlay-arrow (activation)
+ "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
+ (line-beginning-position)
+ (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.
+
+Activity is disabled if the buffer-local variable
+`python-pdbtrack-do-tracking-p' is nil.
+
+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.
+ ;;
+ ;; 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* ((procmark (process-mark currproc))
+ (block (buffer-substring (max comint-last-input-end
+ (- procmark
+ python-pdbtrack-track-range))
+ procmark))
+ target target_fname target_lineno target_buffer)
+
+ (if (not (string-match (concat python-pdbtrack-input-prompt "$") block))
+ (python-pdbtrack-overlay-arrow nil)
+
+ (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-char (point-min))
+ (forward-line (1- 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
+ (with-current-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 (with-current-buffer buf
+ (string= major-mode "python-mode"))
+ (or (string-match funcname (buffer-name buf))
+ (string-match (concat "^\\s-*\\(def\\|class\\)\\s-+"
+ funcname "\\s-*(")
+ (with-current-buffer buf
+ (buffer-substring (point-min)
+ (point-max))))))
+ (setq got buf)))
+ got))
+
+(defun python-toggle-shells (arg)
+ "Toggles between the CPython and JPython shells.
+
+With positive argument ARG (interactively \\[universal-argument]),
+uses the CPython shell, with negative ARG uses the JPython shell, and
+with a zero argument, toggles the shell.
+
+Programmatically, ARG can also be one of the symbols `cpython' or
+`jpython', equivalent to positive arg and negative arg respectively."
+ (interactive "P")
+ ;; default is to toggle
+ (if (null arg)
+ (setq arg 0))
+ ;; preprocess arg
+ (cond
+ ((equal arg 0)
+ ;; toggle
+ (if (string-equal python-which-bufname "Python")
+ (setq arg -1)
+ (setq arg 1)))
+ ((equal arg 'cpython) (setq arg 1))
+ ((equal arg 'jpython) (setq arg -1)))
+ (let (msg)
+ (cond
+ ((< 0 arg)
+ ;; set to CPython
+ (setq python-which-shell python-python-command
+ python-which-args python-python-command-args
+ python-which-bufname "Python"
+ msg "CPython"
+ mode-name "Python"))
+ ((> 0 arg)
+ (setq python-which-shell python-jython-command
+ python-which-args python-jython-command-args
+ python-which-bufname "JPython"
+ msg "JPython"
+ mode-name "JPython")))
+ (message "Using the %s shell" msg)))
+
+;; Python subprocess utilities and filters
+(defun python-execute-file (proc filename)
+ "Send to Python interpreter process PROC \"execfile('FILENAME')\".
+Make that process's buffer visible and force display. Also make
+comint believe the user typed this string so that
+`kill-output-from-shell' does The Right Thing."
+ (let ((curbuf (current-buffer))
+ (procbuf (process-buffer proc))
+; (comint-scroll-to-bottom-on-output t)
+ (msg (format "## working on region in file %s...\n" filename))
+ ;; add some comment, so that we can filter it out of history
+ (cmd (format "execfile(r'%s') # PYTHON-MODE\n" filename)))
+ (unwind-protect
+ (with-current-buffer procbuf
+ (goto-char (point-max))
+ (move-marker (process-mark proc) (point))
+ (funcall (process-filter proc) proc msg))
+ (set-buffer curbuf))
+ (process-send-string proc cmd)))
+
+;;;###autoload
+(defun python-shell (&optional argprompt)
+ "Start an interactive Python interpreter in another window.
+This is like Shell mode, except that Python is running in the window
+instead of a shell. See the `Interactive Shell' and `Shell Mode'
+sections of the Emacs manual for details, especially for the key
+bindings active in the `*Python*' buffer.
+
+With optional \\[universal-argument], the user is prompted for the
+flags to pass to the Python interpreter. This has no effect when this
+command is used to switch to an existing process, only when a new
+process is started. If you use this, you will probably want to ensure
+that the current arguments are retained (they will be included in the
+prompt). This argument is ignored when this function is called
+programmatically.
+
+Note: You can toggle between using the CPython interpreter and the
+JPython interpreter by hitting \\[python-toggle-shells]. This toggles
+buffer local variables which control whether all your subshell
+interactions happen to the `*JPython*' or `*Python*' buffers (the
+latter is the name used for the CPython buffer).
+
+Warning: Don't use an interactive Python if you change sys.ps1 or
+sys.ps2 from their default values, or if you're running code that
+prints `>>> ' or `... ' at the start of a line. `python-mode' can't
+distinguish your output from Python's output, and assumes that `>>> '
+at the start of a line is a prompt from Python. Similarly, the Emacs
+Shell mode code assumes that both `>>> ' and `... ' at the start of a
+line are Python prompts. Bad things can happen if you fool either
+mode.
+
+Warning: If you do any editing *in* the process buffer *while* the
+buffer is accepting output from Python, do NOT attempt to `undo' the
+changes. Some of the output (nowhere near the parts you changed!) may
+be lost if you do. This appears to be an Emacs bug, an unfortunate
+interaction between undo and process filters; the same problem exists in
+non-Python process buffers using the default (Emacs-supplied) process
+filter."
+ (interactive "P")
+ (require 'ansi-color) ; For ipython
+ ;; Set the default shell if not already set
+ (when (null python-which-shell)
+ (python-toggle-shells python-default-interpreter))
+ (let ((args python-which-args))
+ (when (and argprompt
+ (called-interactively-p 'interactive)
+ (fboundp 'split-string))
+ ;; TBD: Perhaps force "-i" in the final list?
+ (setq args (split-string
+ (read-string (concat python-which-bufname
+ " arguments: ")
+ (concat
+ (mapconcat 'identity python-which-args " ") " ")
+ ))))
+ (switch-to-buffer-other-window
+ (apply 'make-comint python-which-bufname python-which-shell nil args))
+ (set-process-sentinel (get-buffer-process (current-buffer))
+ 'python-sentinel)
+ (python--set-prompt-regexp)
+ (add-hook 'comint-output-filter-functions
+ 'python-comint-output-filter-function nil t)
+ ;; pdbtrack
+ (set-syntax-table python-mode-syntax-table)
+ (use-local-map python-shell-map)))
+
+(defun python-pdbtrack-toggle-stack-tracking (arg)
+ (interactive "P")
+ (if (not (get-buffer-process (current-buffer)))
+ (error "No process associated with buffer '%s'" (current-buffer)))
+ ;; missing or 0 is toggle, >0 turn on, <0 turn off
+ (if (or (not arg)
+ (zerop (setq arg (prefix-numeric-value arg))))
+ (setq python-pdbtrack-do-tracking-p (not python-pdbtrack-do-tracking-p))
+ (setq python-pdbtrack-do-tracking-p (> arg 0)))
+ (message "%sabled Python's pdbtrack"
+ (if python-pdbtrack-do-tracking-p "En" "Dis")))
+
+(defun turn-on-pdbtrack ()
+ (interactive)
+ (python-pdbtrack-toggle-stack-tracking 1))
+
+(defun turn-off-pdbtrack ()
+ (interactive)
+ (python-pdbtrack-toggle-stack-tracking 0))
+
+(defun python-sentinel (proc msg)
+ (setq overlay-arrow-position nil))
+