Added comment about where to find details on python-mode.el, pointing

to the Web site.

(py-defun-start-re, py-class-start-re): Changed to defconst.

(py-traceback-line-re): Regular expression describing what traceback
lines look like.

(py-point): New defsubst copied from CC Mode.

(py-highlight-line): Function which does the work of making a
traceback line mouseable.  This only works on XEmacs.  Someone familar
with Emacs text properties and such will have to do that port.

(py-mode-map): Added C-c- bound to py-up-exception and C-c= bound to
py-down-exception.  Also, more concise form for mapcar.

(py-mode-output-map): New keymap for the *Python Output* buffer which
only has keybindings for py-mouseto-exception and py-goto-exception.
All other self-insert-command's are bound to beep.  This is actually
bogus because the buffer should really be made read-only and the
functions that insert in that buffer should bind inhibit-read-only.
Also, this map should be bound to highlighted extents in a *Python*
shell buffer, but this stuff hasn't been migrated into there.

(py-postprocess-output-buffer): New function which extentifies the
*Python Output* buffer.  The bogosities are that this only runs when
the synchronous process in the buffer is finished (so it doesn't work
for async procs), and it should also be merged into py-process-filter
so the *Python* shell gets mouseable too.

(py-shell): Added C-c- and C-c= to the comint buffer's keymap.  The
bogosity is that py-goto-exception should also be bound, but it cannot
be bound to C-cC-c (since that interferes with
comint-interrupt-subjob's typical binding).  Also, traceback lines
aren't mouseable in this buffer.

(py-execute-region): Support for traceback jumping.  This really is
quite a kludge, but necessary based on the way all this stuff works.
There's bound to be broken interactions here.

(py-jump-to-exception, py-mouseto-exception, py-goto-exception,
py-find-next-exception, py-down-exception, py-up-exception): All new
commands and functions to implement traceback jumping.

(py-compute-indentation): Hope this change doesn't get lost in all the
noise above!!!!  This fixes broken non-indentation of a line when TAB
is hit inside a string that isn't a multi-line string.
This commit is contained in:
Barry Warsaw 1997-11-26 01:04:44 +00:00
parent ee9f820cf1
commit a0ee8cd982
1 changed files with 183 additions and 23 deletions

View File

@ -27,7 +27,8 @@
;; Note: this version of python-mode.el is no longer compatible with ;; Note: this version of python-mode.el is no longer compatible with
;; Emacs 18. For a gabazillion reasons, I highly recommend upgrading ;; Emacs 18. For a gabazillion reasons, I highly recommend upgrading
;; to X/Emacs 19 or X/Emacs 20. For older versions of the 19 series, ;; to X/Emacs 19 or X/Emacs 20. For older versions of the 19 series,
;; you may need to acquire the Custom library. ;; you may need to acquire the Custom library. Please see
;; <http://www.python.org/ftp/emacs/> for details.
;; python-mode.el is currently distributed with XEmacs 19 and XEmacs ;; python-mode.el is currently distributed with XEmacs 19 and XEmacs
;; 20. Since this file is not GPL'd it is not distributed with Emacs, ;; 20. Since this file is not GPL'd it is not distributed with Emacs,
@ -344,13 +345,17 @@ Currently-active file is at the head of the list.")
;; change this, you probably have to change `py-current-defun' as ;; change this, you probably have to change `py-current-defun' as
;; well. This is only used by `py-current-defun' to find the name for ;; well. This is only used by `py-current-defun' to find the name for
;; add-log.el. ;; add-log.el.
(defvar py-defun-start-re (defconst py-defun-start-re
"^\\([ \t]*\\)def[ \t]+\\([a-zA-Z_0-9]+\\)\\|\\(^[a-zA-Z_0-9]+\\)[ \t]*=") "^\\([ \t]*\\)def[ \t]+\\([a-zA-Z_0-9]+\\)\\|\\(^[a-zA-Z_0-9]+\\)[ \t]*=")
;; Regexp for finding a class name. If you change this, you probably ;; Regexp for finding a class name. If you change this, you probably
;; have to change `py-current-defun' as well. This is only used by ;; have to change `py-current-defun' as well. This is only used by
;; `py-current-defun' to find the name for add-log.el. ;; `py-current-defun' to find the name for add-log.el.
(defvar py-class-start-re "^class[ \t]*\\([a-zA-Z_0-9]+\\)") (defconst py-class-start-re "^class[ \t]*\\([a-zA-Z_0-9]+\\)")
;; Regexp that describes tracebacks
(defconst py-traceback-line-re
"[ \t]+File \"\\([^\"]+\\)\", line \\([0-9]+\\), in ")
@ -370,6 +375,43 @@ Currently-active file is at the head of the list.")
(and (boundp 'zmacs-region-stays) (and (boundp 'zmacs-region-stays)
(setq zmacs-region-stays t))) (setq zmacs-region-stays t)))
(defsubst py-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 defun
;; boi -- back to indentation
;;
;; 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) (beginning-of-python-def-or-class))
((eq position 'bob) (beginning-of-buffer))
((eq position 'eob) (end-of-buffer))
((eq position 'boi) (back-to-indentation))
(t (error "unknown buffer position requested: %s" position))
)
(prog1
(point)
(goto-char here))))
(defsubst py-highlight-line (from to file line)
(cond
((fboundp 'make-extent)
;; XEmacs
(let ((e (make-extent from to)))
(set-extent-property e 'mouse-face 'highlight)
(set-extent-property e 'py-exc-info (cons file line))
(set-extent-property e 'keymap py-mode-output-map)))
(t
;; Emacs -- Please port this!
)
))
;; Major mode boilerplate ;; Major mode boilerplate
@ -433,6 +475,8 @@ Currently-active file is at the head of the list.")
(define-key py-mode-map "\C-c\C-hm" 'py-describe-mode) (define-key py-mode-map "\C-c\C-hm" 'py-describe-mode)
(define-key py-mode-map "\e\C-a" 'beginning-of-python-def-or-class) (define-key py-mode-map "\e\C-a" 'beginning-of-python-def-or-class)
(define-key py-mode-map "\e\C-e" 'end-of-python-def-or-class) (define-key py-mode-map "\e\C-e" 'end-of-python-def-or-class)
(define-key py-mode-map "\C-c-" 'py-up-exception)
(define-key py-mode-map "\C-c=" 'py-down-exception)
;; information ;; information
(define-key py-mode-map "\C-c\C-b" 'py-submit-bug-report) (define-key py-mode-map "\C-c\C-b" 'py-submit-bug-report)
(define-key py-mode-map "\C-c\C-v" 'py-version) (define-key py-mode-map "\C-c\C-v" 'py-version)
@ -441,10 +485,24 @@ Currently-active file is at the head of the list.")
;; shadow global bindings for newline-and-indent w/ the py- version. ;; shadow global bindings for newline-and-indent w/ the py- version.
;; BAW - this is extremely bad form, but I'm not going to change it ;; BAW - this is extremely bad form, but I'm not going to change it
;; for now. ;; for now.
(mapcar (function (lambda (key) (mapcar #'(lambda (key)
(define-key (define-key py-mode-map key 'py-newline-and-indent))
py-mode-map key 'py-newline-and-indent))) (where-is-internal 'newline-and-indent))
(where-is-internal 'newline-and-indent)) )
(defvar py-mode-output-map nil
"Keymap used in *Python Output* buffers*")
(if py-mode-output-map
nil
(setq py-mode-output-map (make-sparse-keymap))
(define-key py-mode-output-map [button2] 'py-mouseto-exception)
(define-key py-mode-output-map "\C-c\C-c" 'py-goto-exception)
;; TBD: Disable all self-inserting keys. This is bogus, we should
;; really implement this as *Python Output* buffer being read-only
(mapcar #' (lambda (key)
(define-key py-mode-output-map key
#'(lambda () (interactive) (beep))))
(where-is-internal 'self-insert-command))
) )
(defvar py-mode-syntax-table nil (defvar py-mode-syntax-table nil
@ -964,9 +1022,24 @@ Electric behavior is inhibited inside a string or comment."
)) ))
(set-buffer curbuf)))) (set-buffer curbuf))))
(defun py-postprocess-output-buffer (buf)
(save-excursion
(set-buffer buf)
(beginning-of-buffer)
(while (re-search-forward py-traceback-line-re nil t)
(let ((file (match-string 1))
(line (string-to-int (match-string 2))))
(py-highlight-line (py-point 'bol) (py-point 'eol) file line))
)))
;;; Subprocess commands ;;; Subprocess commands
;; only used when (memq 'broken-temp-names py-emacs-features)
(defvar py-serial-number 0)
(defvar py-exception-buffer nil)
(defconst py-output-buffer "*Python Output*")
;;;###autoload ;;;###autoload
(defun py-shell () (defun py-shell ()
"Start an interactive Python interpreter in another window. "Start an interactive Python interpreter in another window.
@ -1004,9 +1077,12 @@ filter."
(setq comint-prompt-regexp "^>>> \\|^[.][.][.] ") (setq comint-prompt-regexp "^>>> \\|^[.][.][.] ")
(set-process-filter (get-buffer-process (current-buffer)) 'py-process-filter) (set-process-filter (get-buffer-process (current-buffer)) 'py-process-filter)
(set-syntax-table py-mode-syntax-table) (set-syntax-table py-mode-syntax-table)
(local-set-key [tab] 'self-insert-command)) ;; set up keybindings for this subshell
(local-set-key [tab] 'self-insert-command)
(local-set-key "\C-c-" 'py-up-exception)
(local-set-key "\C-c=" 'py-down-exception)
)
(defun py-clear-queue () (defun py-clear-queue ()
"Clear the queue of temporary files waiting to execute." "Clear the queue of temporary files waiting to execute."
(interactive) (interactive)
@ -1015,9 +1091,6 @@ filter."
(setq py-file-queue nil) (setq py-file-queue nil)
(message "%d pending files de-queued." n))) (message "%d pending files de-queued." n)))
;; only used when (memq 'broken-temp-names py-emacs-features)
(defvar py-serial-number 0)
(defun py-execute-region (start end &optional async) (defun py-execute-region (start end &optional async)
"Execute the the region in a Python interpreter. "Execute the the region in a Python interpreter.
The region is first copied into a temporary file (in the directory The region is first copied into a temporary file (in the directory
@ -1046,15 +1119,15 @@ is inserted at the end. See also the command `py-clear-queue'."
(format "python-%d" py-serial-number) (format "python-%d" py-serial-number)
(setq py-serial-number (1+ py-serial-number))) (setq py-serial-number (1+ py-serial-number)))
(make-temp-name "python"))) (make-temp-name "python")))
(file (concat (file-name-as-directory py-temp-directory) temp)) (file (concat (file-name-as-directory py-temp-directory) temp)))
(outbuf "*Python Output*"))
(write-region start end file nil 'nomsg) (write-region start end file nil 'nomsg)
(cond (cond
;; always run the code in it's own asynchronous subprocess ;; always run the code in it's own asynchronous subprocess
(async (async
(let* ((buf (generate-new-buffer-name "*Python Output*"))) (let* ((buf (generate-new-buffer-name py-output-buffer)))
(start-process "Python" buf py-python-command "-u" file) (start-process "Python" buf py-python-command "-u" file)
(pop-to-buffer buf) (pop-to-buffer buf)
(py-postprocess-output-buffer buf)
)) ))
;; if the Python interpreter shell is running, queue it up for ;; if the Python interpreter shell is running, queue it up for
;; execution there. ;; execution there.
@ -1063,10 +1136,13 @@ is inserted at the end. See also the command `py-clear-queue'."
(if (not py-file-queue) (if (not py-file-queue)
(py-execute-file proc file) (py-execute-file proc file)
(message "File %s queued for execution" file)) (message "File %s queued for execution" file))
(push file py-file-queue)) (push file py-file-queue)
(setq py-exception-buffer (cons file (current-buffer))))
(t (t
;; otherwise either run it synchronously in a subprocess ;; otherwise either run it synchronously in a subprocess
(shell-command-on-region start end py-python-command outbuf) (shell-command-on-region start end py-python-command py-output-buffer)
(setq py-exception-buffer (current-buffer))
(py-postprocess-output-buffer py-output-buffer)
)))) ))))
;; Code execution command ;; Code execution command
@ -1080,6 +1156,90 @@ See the `\\[py-execute-region]' docs for an account of some subtleties."
(interactive "P") (interactive "P")
(py-execute-region (point-min) (point-max) async)) (py-execute-region (point-min) (point-max) async))
(defun py-jump-to-exception (file line)
(let ((buffer (cond ((string-equal file "<stdin>")
py-exception-buffer)
((and (consp py-exception-buffer)
(string-equal file (car py-exception-buffer)))
(cdr py-exception-buffer))
((py-safe (find-file-noselect file)))
;; could not figure out what file the exception
;; is pointing to, so prompt for it
(t (find-file (read-file-name "Exception file: "
nil
file t))))))
(pop-to-buffer buffer)
(goto-line line)
(message "Jumping to exception in file %s on line %d" file line)))
(defun py-mouseto-exception (event)
(interactive "e")
(cond
((fboundp 'event-point)
;; XEmacs
(let* ((point (event-point event))
(buffer (event-buffer event))
(e (and point buffer (extent-at point buffer 'py-exc-info)))
(info (and e (extent-property e 'py-exc-info))))
(message "Event point: %d, info: %s" point info)
(and info
(py-jump-to-exception (car info) (cdr info)))
))
;; Emacs -- Please port this!
))
(defun py-goto-exception ()
"Go to the line indicated by the traceback."
(interactive)
(let (file line)
(save-excursion
(beginning-of-line)
(if (looking-at py-traceback-line-re)
(setq file (match-string 1)
line (string-to-int (match-string 2)))))
(if (not file)
(error "Not on a traceback line."))
(py-jump-to-exception file line)))
(defun py-find-next-exception (start buffer searchdir errwhere)
;; Go to start position in buffer, search in the specified
;; direction, and jump to the exception found. If at the end of the
;; exception, print error message
(let (file line)
(save-excursion
(set-buffer buffer)
(goto-char (py-point start))
(if (funcall searchdir py-traceback-line-re nil t)
(setq file (match-string 1)
line (string-to-int (match-string 2)))))
(if (and file line)
(py-jump-to-exception file line)
(error "%s of traceback" errwhere))))
(defun py-down-exception (&optional bottom)
"Go to the next line down in the traceback.
With optional \\[universal-argument], jump to the bottom (innermost)
exception in the exception stack."
(interactive "P")
(let* ((proc (get-process "Python"))
(buffer (if proc "*Python*" py-output-buffer)))
(if bottom
(py-find-next-exception 'eob buffer 're-search-backward "Bottom")
(py-find-next-exception 'eol buffer 're-search-forward "Bottom"))))
(defun py-up-exception (&optional top)
"Go to the previous line up in the traceback.
With optional \\[universal-argument], jump to the top (outermost)
exception in the exception stack."
(interactive "P")
(let* ((proc (get-process "Python"))
(buffer (if proc "*Python*" py-output-buffer)))
(if top
(py-find-next-exception 'bob buffer 're-search-forward "Top")
(py-find-next-exception 'boi buffer 're-search-backward "Top"))))
;; Electric deletion ;; Electric deletion
(defun py-electric-backspace (arg) (defun py-electric-backspace (arg)
@ -1198,14 +1358,14 @@ the new line indented."
;; honor-block-close-p is non-nil, statements such as return, raise, ;; honor-block-close-p is non-nil, statements such as return, raise,
;; break, continue, and pass force one level of outdenting. ;; break, continue, and pass force one level of outdenting.
(save-excursion (save-excursion
(let ((pps (parse-partial-sexp (save-excursion (let* ((bod (py-point 'bod))
(beginning-of-python-def-or-class) (pps (parse-partial-sexp bod (point))))
(point))
(point))))
(beginning-of-line) (beginning-of-line)
(cond (cond
;; are we inside a string or comment? ;; are we inside a multi-line string or comment?
((or (nth 3 pps) (nth 4 pps)) ((or (and (nth 3 pps)
(nth 3 (parse-partial-sexp bod (py-point 'boi))))
(nth 4 pps))
(save-excursion (save-excursion
(if (not py-align-multiline-strings-p) 0 (if (not py-align-multiline-strings-p) 0
;; skip back over blank & non-indenting comment lines ;; skip back over blank & non-indenting comment lines