[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: python-mode patch
From: |
Nick Roberts |
Subject: |
Re: python-mode patch |
Date: |
Fri, 25 Aug 2006 10:06:38 +1200 |
> #> if you're interested in scoping it out, look for variables and
> #> functions that contain the string pdbtrack". i suspect most of it can
> #> be used exactly as is.
> #>
> #> at 150 lines of code, and since i wrote the original, i would be happy
> #> to sign papers for it.
>
> Sounds great... Just in case, do you have the latest version of your
> own code somewhere? If/when you sign papers, that is definitely
> something we can use, while the code which currently resides in
> python-mode.el might have been changed by other people as well, so I
> presume we would need to get assignments from everybody who worked on
> that.
Here's a first attempt at scoping it out. I don't know if everything I added
was necessary but it comes in at about 440 lines of extra code. I've also
changed the py namespace to python.
--
Nick http://www.inet.net.nz/~nickrob
*** python.el 21 Aug 2006 11:35:42 +1200 1.41
--- python.el 25 Aug 2006 09:59:47 +1200
***************
*** 196,201 ****
--- 196,202 ----
(define-key map "\C-c<" 'python-shift-left)
(define-key map "\C-c>" 'python-shift-right)
(define-key map "\C-c\C-k" 'python-mark-block)
+ (define-key map "\C-c\C-d" 'python-pdbtrack-toggle-stack-tracking)
(define-key map "\C-c\C-n" 'python-next-statement)
(define-key map "\C-c\C-p" 'python-previous-statement)
(define-key map "\C-c\C-u" 'python-beginning-of-block)
***************
*** 273,281 ****
;; eric has items including: (un)indent, (un)comment, restart script,
;; run script, debug script; also things for profiling, unit testing.
(defvar python-mode-syntax-table
(let ((table (make-syntax-table)))
! ;; Give punctuation syntax to ASCII that normally has symbol
;; syntax or has word syntax and isn't a letter.
(let ((symbol (string-to-syntax "_"))
(sst (standard-syntax-table)))
--- 274,290 ----
;; eric has items including: (un)indent, (un)comment, restart script,
;; run script, debug script; also things for profiling, unit testing.
+ (defvar python-shell-map
+ (let ((map (copy-keymap comint-mode-map)))
+ (define-key map [tab] 'tab-to-tab-stop)
+ (define-key map "\C-c-" 'py-up-exception)
+ (define-key map "\C-c=" 'py-down-exception)
+ map)
+ "Keymap used in *Python* shell buffers.")
+
(defvar python-mode-syntax-table
(let ((table (make-syntax-table)))
! ;; Give punctuAtion syntax to ASCII that normally has symbol
;; syntax or has word syntax and isn't a letter.
(let ((symbol (string-to-syntax "_"))
(sst (standard-syntax-table)))
***************
*** 436,441 ****
--- 445,511 ----
:group 'python
:type 'integer)
+
+ (defcustom python-default-interpreter 'cpython
+ "*Which Python interpreter is used by default.
+ The value for this variable can be either `cpython' or `jpython'.
+
+ When the value is `cpython', the variables `python-python-command' and
+ `python-python-command-args' are consulted to determine the interpreter
+ and arguments to use.
+
+ When the value is `jpython', the variables `python-jpython-command' and
+ `python-jpython-command-args' are consulted to determine the interpreter
+ and arguments to use.
+
+ Note that this variable is consulted only the first time that a Python
+ mode buffer is visited during an Emacs session. After that, use
+ \\[python-toggle-shells] to change the interpreter shell."
+ :type '(choice (const :tag "Python (a.k.a. CPython)" cpython)
+ (const :tag "JPython" jpython))
+ :group 'python)
+
+ (defcustom python-python-command-args '("-i")
+ "*List of string arguments to be used when starting a Python shell."
+ :type '(repeat string)
+ :group 'python)
+
+ ;; for toggling between CPython and JPython
+ (defvar python-which-shell nil)
+ (defvar python-which-args python-python-command-args)
+ (defvar python-which-bufname "Python")
+ (make-variable-buffer-local 'python-which-shell)
+ (make-variable-buffer-local 'python-which-args)
+ (make-variable-buffer-local 'python-which-bufname)
+
+ (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."
+ :type 'boolean
+ :group 'python)
+ (make-variable-buffer-local 'python-pdbtrack-do-tracking-p)
+
+ ;; Bind python-file-queue before installing the kill-emacs-hook.
+ (defvar python-file-queue nil
+ "Queue of Python temp files awaiting execution.
+ Currently-active file is at the head of the list.")
+
+ (defvar python-pdbtrack-is-tracking-p nil)
+
+ (defconst python-pdbtrack-stack-entry-regexp
+ "> \\([^(]+\\)(\\([0-9]+\\))[?a-zA-Z0-9_]+()"
+ "Regular expression pdbtrack uses to find a stack trace entry.")
+
+ (defconst python-pdbtrack-input-prompt "\n[(<]?pdb[>)]? "
+ "Regular expression pdbtrack uses to recognize a pdb prompt.")
+
+ (defconst python-pdbtrack-track-range 10000
+ "Max number of characters from end of buffer to search for stack entry.")
+
(defun python-guess-indent ()
"Guess step for indentation of current buffer.
Set `python-indent' locally to the value guessed."
***************
*** 2077,2083 ****
(condition-case data
(unless (fboundp 'brm-rename)
(pymacs-load "bikeemacs" "brm-") ; first line of normal recipe
! (let ((py-mode-map (make-sparse-keymap)) ; it assumes this
(features (cons 'python-mode features)) ; and requires this
menu)
(brm-init) ; second line of normal recipe
--- 2147,2153 ----
(condition-case data
(unless (fboundp 'brm-rename)
(pymacs-load "bikeemacs" "brm-") ; first line of normal recipe
! (let ((python-mode-map (make-sparse-keymap)) ; it assumes this
(features (cons 'python-mode features)) ; and requires this
menu)
(brm-init) ; second line of normal recipe
***************
*** 2247,2252 ****
--- 2317,2692 ----
:group 'python
(set (make-local-variable 'python-command) python-jython-command))
+
+
+ ;; pdbtrack features
+
+ (defsubst python-point (position)
+ "Returns the value of point at certain commonly referenced POSITIONs.
+ POSITION can be one of the following symbols:
+
+ bol -- beginning of line
+ eol -- end of line
+ bod -- beginning of def or class
+ eod -- end of def or class
+ bob -- beginning of buffer
+ eob -- end of buffer
+ boi -- back to indentation
+ bos -- beginning of statement
+
+ This function does not modify point or mark."
+ (let ((here (point)))
+ (cond
+ ((eq position 'bol) (beginning-of-line))
+ ((eq position 'eol) (end-of-line))
+ ((eq position 'bod) (python-beginning-of-def-or-class))
+ ((eq position 'eod) (python-end-of-def-or-class))
+ ;; Kind of funny, I know, but useful for python-up-exception.
+ ((eq position 'bob) (beginning-of-buffer))
+ ((eq position 'eob) (end-of-buffer))
+ ((eq position 'boi) (back-to-indentation))
+ ((eq position 'bos) (python-goto-initial-line))
+ (t (error "Unknown buffer position requested: %s" position)))
+ (prog1
+ (point)
+ (goto-char here))))
+
+ (defun python-end-of-def-or-class (&optional class count)
+ "Move point beyond end of `def' or `class' body.
+
+ By default, looks for an appropriate `def'. If you supply a prefix
+ arg, looks for a `class' instead. The docs below assume the `def'
+ case; just substitute `class' for `def' for the other case.
+ Programmatically, if CLASS is `either', then moves to either `class'
+ or `def'.
+
+ When second optional argument is given programmatically, move to the
+ COUNTth end of `def'.
+
+ If point is in a `def' statement already, this is the `def' we use.
+
+ Else, if the `def' found by `\\[python-beginning-of-def-or-class]'
+ contains the statement you started on, that's the `def' we use.
+
+ Otherwise, we search forward for the closest following `def', and use that.
+
+ If a `def' can be found by these rules, point is moved to the start of
+ the line immediately following the `def' block, and the position of the
+ start of the `def' is returned.
+
+ Else point is moved to the end of the buffer, and nil is returned.
+
+ Note that doing this command repeatedly will take you closer to the
+ end of the buffer each time.
+
+ To mark the current `def', see `\\[python-mark-def-or-class]'."
+ (interactive "P") ; raw prefix arg
+ (if (and count (/= count 1))
+ (python-beginning-of-def-or-class (- 1 count)))
+ (let ((start (progn (python-goto-initial-line) (point)))
+ (which (cond ((eq class 'either) "\\(class\\|def\\)")
+ (class "class")
+ (t "def")))
+ (state 'not-found))
+ ;; move point to start of appropriate def/class
+ (if (looking-at (concat "[ \t]*" which "\\>")) ; already on one
+ (setq state 'at-beginning)
+ ;; else see if python-beginning-of-def-or-class hits container
+ (if (and (python-beginning-of-def-or-class class)
+ (progn (python-goto-beyond-block)
+ (> (point) start)))
+ (setq state 'at-end)
+ ;; else search forward
+ (goto-char start)
+ (if (re-search-forward (concat "^[ \t]*" which "\\>") nil 'move)
+ (progn (setq state 'at-beginning)
+ (beginning-of-line)))))
+ (cond
+ ((eq state 'at-beginning) (python-goto-beyond-block) t)
+ ((eq state 'at-end) t)
+ ((eq state 'not-found) nil)
+ (t (error "Internal error in `python-end-of-def-or-class'")))))
+
+ (defun python-beginning-of-def-or-class (&optional class count)
+ "Move point to start of `def' or `class'.
+
+ Searches back for the closest preceding `def'. If you supply a prefix
+ arg, looks for a `class' instead. The docs below assume the `def'
+ case; just substitute `class' for `def' for the other case.
+ Programmatically, if CLASS is `either', then moves to either `class'
+ or `def'.
+
+ When second optional argument is given programmatically, move to the
+ COUNTth start of `def'.
+
+ If point is in a `def' statement already, and after the `d', simply
+ moves point to the start of the statement.
+
+ Otherwise (i.e. when point is not in a `def' statement, or at or
+ before the `d' of a `def' statement), searches for the closest
+ preceding `def' statement, and leaves point at its start. If no such
+ statement can be found, leaves point at the start of the buffer.
+
+ Returns t iff a `def' statement is found by these rules.
+
+ Note that doing this command repeatedly will take you closer to the
+ start of the buffer each time.
+
+ To mark the current `def', see `\\[python-mark-def-or-class]'."
+ (interactive "P") ; raw prefix arg
+ (setq count (or count 1))
+ (let ((at-or-before-p (<= (current-column) (current-indentation)))
+ (start-of-line (goto-char (python-point 'bol)))
+ (start-of-stmt (goto-char (python-point 'bos)))
+ (start-re (cond ((eq class 'either) "^[ \t]*\\(class\\|def\\)\\>")
+ (class "^[ \t]*class\\>")
+ (t "^[ \t]*def\\>")))
+ )
+ ;; searching backward
+ (if (and (< 0 count)
+ (or (/= start-of-stmt start-of-line)
+ (not at-or-before-p)))
+ (end-of-line))
+ ;; search forward
+ (if (and (> 0 count)
+ (zerop (current-column))
+ (looking-at start-re))
+ (end-of-line))
+ (if (re-search-backward start-re nil 'move count)
+ (goto-char (match-beginning 0)))))
+
+ (defun python-goto-initial-line ()
+ "Go to the initial line of the current statement.
+ Usually this is the line we're on, but if we're on the 2nd or
+ following lines of a continuation block, we need to go up to the first
+ line of the block."
+ ;; Tricky: We want to avoid quadratic-time behavior for long
+ ;; continued blocks, whether of the backslash or open-bracket
+ ;; varieties, or a mix of the two. The following manages to do that
+ ;; in the usual cases.
+ ;;
+ ;; Also, if we're sitting inside a triple quoted string, this will
+ ;; drop us at the line that begins the string.
+ (let (open-bracket-pos)
+ (while (python-continuation-line-p)
+ (beginning-of-line)
+ (if (python-backslash-continuation-line-p)
+ (while (python-backslash-continuation-line-p)
+ (forward-line -1))
+ ;; else zip out of nested brackets/braces/parens
+ (while (setq open-bracket-pos (python-nesting-level))
+ (goto-char open-bracket-pos)))))
+ (beginning-of-line))
+
+ (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 (or (string-equal string ">>> ")
+ (and (>= (length string) 5)
+ (string-equal (substring string -5) "\n>>> ")))
+ python-file-queue)
+ (python-safe (delete-file (car python-file-queue)))
+ (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 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))
+ ))
+
+ (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 matching `python-pdbtrack-input-prompt'
+ at the beginning of the line."
+ ;; 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.
+ (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))
+ (block (buffer-substring (max comint-last-input-end
+ (- procmark
+ python-pdbtrack-track-range))
+ procmark))
+ fname lineno)
+ (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-int 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))
+ )))))))
+
+ (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-jpython-command
+ python-which-args python-jpython-command-args
+ python-which-bufname "JPython"
+ msg "JPython"
+ mode-name "JPython"))
+ )
+ (message "Using the %s shell" msg)
+ (setq python-output-buffer (format "*%s Output*" python-which-bufname))))
+
+ ;;;###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, or when running in Emacs 19.34 or older.
+
+ 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")
+ ;; 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
+ (interactive-p)
+ (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))
+ (make-local-variable 'comint-prompt-regexp)
+ (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)))
+
+ (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))
+
(provide 'python)
(provide 'python-21)
;; arch-tag: 6fce1d99-a704-4de9-ba19-c6e4912b0554
- Re: python-mode patch, (continued)
- Re: python-mode patch, David Kastrup, 2006/08/25
- Re: python-mode patch, Richard Stallman, 2006/08/25
- Re: python-mode patch, Slawomir Nowaczyk, 2006/08/25
- Re: python-mode patch, Ken Manheimer, 2006/08/21
- Re: python-mode patch, Nick Roberts, 2006/08/23
- Re: python-mode patch, Ken Manheimer, 2006/08/23
- Re: python-mode patch, Slawomir Nowaczyk, 2006/08/24
- Re: python-mode patch,
Nick Roberts <=