cvsdist b39f9d
;;; python-mode.el --- Major mode for editing Python programs
cvsdist b39f9d
cvsdist b39f9d
;; Copyright (C) 1992,1993,1994  Tim Peters
cvsdist b39f9d
cvsdist c08065
;; Author: 1995-2001 Barry A. Warsaw
cvsdist b39f9d
;;         1992-1994 Tim Peters
cvsdist b39f9d
;; Maintainer: python-mode@python.org
cvsdist b39f9d
;; Created:    Feb 1992
cvsdist b39f9d
;; Keywords:   python languages oop
cvsdist b39f9d
cvsdist 86923e
(defconst py-version "$Revision: 4.6.18.2 $"
cvsdist b39f9d
  "`python-mode' version number.")
cvsdist b39f9d
cvsdist b39f9d
;; This software is provided as-is, without express or implied
cvsdist b39f9d
;; warranty.  Permission to use, copy, modify, distribute or sell this
cvsdist b39f9d
;; software, without fee, for any purpose and by any individual or
cvsdist b39f9d
;; organization, is hereby granted, provided that the above copyright
cvsdist b39f9d
;; notice and this paragraph appear in all copies.
cvsdist b39f9d
cvsdist b39f9d
;;; Commentary:
cvsdist b39f9d
cvsdist b39f9d
;; This is a major mode for editing Python programs.  It was developed
cvsdist b39f9d
;; by Tim Peters after an original idea by Michael A. Guravage.  Tim
cvsdist b39f9d
;; subsequently left the net; in 1995, Barry Warsaw inherited the mode
cvsdist b39f9d
;; and is the current maintainer.  Tim's now back but disavows all
cvsdist b39f9d
;; responsibility for the mode.  Smart Tim :-)
cvsdist b39f9d
cvsdist c08065
;; pdbtrack support contributed by Ken Manheimer, April 2001.
cvsdist c08065
cvsdist c08065
;; This version of python-mode.el has only been tested with XEmacs
cvsdist c08065
;; 21.1.14 and Emacs 20.7 as these are the latest versions of these
cvsdist c08065
;; Emacsen as of this writing (11-Apr-2001).  I have no intent to test
cvsdist c08065
;; it with earlier Emacsen, but I will accept patches if they are
cvsdist c08065
;; small and reasonable.  Please use the SourceForge Python project to
cvsdist c08065
;; submit bugs or patches:
cvsdist c08065
;;
cvsdist c08065
;;     http://sourceforge.net/projects/python
cvsdist b39f9d
cvsdist b39f9d
;; FOR MORE INFORMATION:
cvsdist b39f9d
cvsdist c08065
;; There is some information on python-mode.el at
cvsdist c08065
cvsdist b39f9d
;;     http://www.python.org/emacs/python-mode/
cvsdist b39f9d
;;
cvsdist c08065
;; but this link is fairly out of date, due to the current difficulty
cvsdist c08065
;; in updating that site. It does contain links to other packages that
cvsdist c08065
;; you might find useful, such as pdb interfaces, OO-Browser links,
cvsdist c08065
;; etc.  Eventually, we'll be able to update it much more easily.
cvsdist b39f9d
cvsdist b39f9d
;; BUG REPORTING:
cvsdist b39f9d
cvsdist c08065
;; As mentioned above, please use the SourceForge Python project for
cvsdist c08065
;; submitting bug reports or patches.  The old recommendation, to use
cvsdist c08065
;; C-c C-b will still work, but those reports have a higher chance of
cvsdist c08065
;; getting buried in my mailbox.  Please include a complete, but
cvsdist b39f9d
;; concise code sample and a recipe for reproducing the bug.  Send
cvsdist b39f9d
;; suggestions and other comments to python-mode@python.org.
cvsdist b39f9d
cvsdist b39f9d
;; When in a Python mode buffer, do a C-h m for more help.  It's
cvsdist b39f9d
;; doubtful that a texinfo manual would be very useful, but if you
cvsdist b39f9d
;; want to contribute one, I'll certainly accept it!
cvsdist b39f9d
cvsdist b39f9d
;;; Code:
cvsdist b39f9d
cvsdist b39f9d
(require 'comint)
cvsdist b39f9d
(require 'custom)
cvsdist c08065
(require 'cl)
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; user definable variables
cvsdist b39f9d
;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
cvsdist b39f9d
cvsdist b39f9d
(defgroup python nil
cvsdist b39f9d
  "Support for the Python programming language, <http://www.python.org/>"
cvsdist b39f9d
  :group 'languages
cvsdist b39f9d
  :prefix "py-")
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-python-command "python"
cvsdist b39f9d
  "*Shell command used to start Python interpreter."
cvsdist b39f9d
  :type 'string
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-jpython-command "jpython"
cvsdist b39f9d
  "*Shell command used to start the JPython interpreter."
cvsdist b39f9d
  :type 'string
cvsdist b39f9d
  :group 'python
cvsdist b39f9d
  :tag "JPython Command")
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-default-interpreter 'cpython
cvsdist b39f9d
  "*Which Python interpreter is used by default.
cvsdist b39f9d
The value for this variable can be either `cpython' or `jpython'.
cvsdist b39f9d
cvsdist b39f9d
When the value is `cpython', the variables `py-python-command' and
cvsdist b39f9d
`py-python-command-args' are consulted to determine the interpreter
cvsdist b39f9d
and arguments to use.
cvsdist b39f9d
cvsdist b39f9d
When the value is `jpython', the variables `py-jpython-command' and
cvsdist b39f9d
`py-jpython-command-args' are consulted to determine the interpreter
cvsdist b39f9d
and arguments to use.
cvsdist b39f9d
cvsdist b39f9d
Note that this variable is consulted only the first time that a Python
cvsdist b39f9d
mode buffer is visited during an Emacs session.  After that, use
cvsdist b39f9d
\\[py-toggle-shells] to change the interpreter shell."
cvsdist b39f9d
  :type '(choice (const :tag "Python (a.k.a. CPython)" cpython)
cvsdist b39f9d
		 (const :tag "JPython" jpython))
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-python-command-args '("-i")
cvsdist b39f9d
  "*List of string arguments to be used when starting a Python shell."
cvsdist b39f9d
  :type '(repeat string)
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-jpython-command-args '("-i")
cvsdist b39f9d
  "*List of string arguments to be used when starting a JPython shell."
cvsdist b39f9d
  :type '(repeat string)
cvsdist b39f9d
  :group 'python
cvsdist b39f9d
  :tag "JPython Command Args")
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-indent-offset 4
cvsdist b39f9d
  "*Amount of offset per level of indentation.
cvsdist b39f9d
`\\[py-guess-indent-offset]' can usually guess a good value when
cvsdist b39f9d
you're editing someone else's Python code."
cvsdist b39f9d
  :type 'integer
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist c08065
(defcustom py-continuation-offset 4
cvsdist c08065
  "*Additional amount of offset to give for some continuation lines.
cvsdist c08065
Continuation lines are those that immediately follow a backslash
cvsdist c08065
terminated line.  Only those continuation lines for a block opening
cvsdist c08065
statement are given this extra offset."
cvsdist c08065
  :type 'integer
cvsdist c08065
  :group 'python)
cvsdist c08065
cvsdist b39f9d
(defcustom py-smart-indentation t
cvsdist b39f9d
  "*Should `python-mode' try to automagically set some indentation variables?
cvsdist b39f9d
When this variable is non-nil, two things happen when a buffer is set
cvsdist b39f9d
to `python-mode':
cvsdist b39f9d
cvsdist b39f9d
    1. `py-indent-offset' is guessed from existing code in the buffer.
cvsdist b39f9d
       Only guessed values between 2 and 8 are considered.  If a valid
cvsdist b39f9d
       guess can't be made (perhaps because you are visiting a new
cvsdist b39f9d
       file), then the value in `py-indent-offset' is used.
cvsdist b39f9d
cvsdist b39f9d
    2. `indent-tabs-mode' is turned off if `py-indent-offset' does not
cvsdist b39f9d
       equal `tab-width' (`indent-tabs-mode' is never turned on by
cvsdist b39f9d
       Python mode).  This means that for newly written code, tabs are
cvsdist b39f9d
       only inserted in indentation if one tab is one indentation
cvsdist b39f9d
       level, otherwise only spaces are used.
cvsdist b39f9d
cvsdist b39f9d
Note that both these settings occur *after* `python-mode-hook' is run,
cvsdist b39f9d
so if you want to defeat the automagic configuration, you must also
cvsdist b39f9d
set `py-smart-indentation' to nil in your `python-mode-hook'."
cvsdist b39f9d
  :type 'boolean
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-align-multiline-strings-p t
cvsdist b39f9d
  "*Flag describing how multi-line triple quoted strings are aligned.
cvsdist b39f9d
When this flag is non-nil, continuation lines are lined up under the
cvsdist b39f9d
preceding line's indentation.  When this flag is nil, continuation
cvsdist b39f9d
lines are aligned to column zero."
cvsdist b39f9d
  :type '(choice (const :tag "Align under preceding line" t)
cvsdist b39f9d
		 (const :tag "Align to column zero" nil))
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-block-comment-prefix "##"
cvsdist b39f9d
  "*String used by \\[comment-region] to comment out a block of code.
cvsdist b39f9d
This should follow the convention for non-indenting comment lines so
cvsdist b39f9d
that the indentation commands won't get confused (i.e., the string
cvsdist b39f9d
should be of the form `#x...' where `x' is not a blank or a tab, and
cvsdist b39f9d
`...' is arbitrary).  However, this string should not end in whitespace."
cvsdist b39f9d
  :type 'string
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-honor-comment-indentation t
cvsdist b39f9d
  "*Controls how comment lines influence subsequent indentation.
cvsdist b39f9d
cvsdist b39f9d
When nil, all comment lines are skipped for indentation purposes, and
cvsdist b39f9d
if possible, a faster algorithm is used (i.e. X/Emacs 19 and beyond).
cvsdist b39f9d
cvsdist b39f9d
When t, lines that begin with a single `#' are a hint to subsequent
cvsdist b39f9d
line indentation.  If the previous line is such a comment line (as
cvsdist b39f9d
opposed to one that starts with `py-block-comment-prefix'), then its
cvsdist b39f9d
indentation is used as a hint for this line's indentation.  Lines that
cvsdist b39f9d
begin with `py-block-comment-prefix' are ignored for indentation
cvsdist b39f9d
purposes.
cvsdist b39f9d
cvsdist 86923e
When not nil or t, comment lines that begin with a single `#' are used
cvsdist 86923e
as indentation hints, unless the comment character is in column zero."
cvsdist b39f9d
  :type '(choice
cvsdist b39f9d
	  (const :tag "Skip all comment lines (fast)" nil)
cvsdist b39f9d
	  (const :tag "Single # `sets' indentation for next line" t)
cvsdist b39f9d
	  (const :tag "Single # `sets' indentation except at column zero"
cvsdist b39f9d
		 other)
cvsdist b39f9d
	  )
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-temp-directory
cvsdist b39f9d
  (let ((ok '(lambda (x)
cvsdist b39f9d
	       (and x
cvsdist b39f9d
		    (setq x (expand-file-name x)) ; always true
cvsdist b39f9d
		    (file-directory-p x)
cvsdist b39f9d
		    (file-writable-p x)
cvsdist b39f9d
		    x))))
cvsdist b39f9d
    (or (funcall ok (getenv "TMPDIR"))
cvsdist b39f9d
	(funcall ok "/usr/tmp")
cvsdist b39f9d
	(funcall ok "/tmp")
cvsdist 86923e
	(funcall ok "/var/tmp")
cvsdist b39f9d
	(funcall ok  ".")
cvsdist b39f9d
	(error
cvsdist b39f9d
	 "Couldn't find a usable temp directory -- set `py-temp-directory'")))
cvsdist 86923e
  "*Directory used for temporary files created by a *Python* process.
cvsdist b39f9d
By default, the first directory from this list that exists and that you
cvsdist 86923e
can write into: the value (if any) of the environment variable TMPDIR,
cvsdist 86923e
/usr/tmp, /tmp, /var/tmp, or the current directory."
cvsdist b39f9d
  :type 'string
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-beep-if-tab-change t
cvsdist b39f9d
  "*Ring the bell if `tab-width' is changed.
cvsdist b39f9d
If a comment of the form
cvsdist b39f9d
cvsdist b39f9d
  \t# vi:set tabsize=<number>:
cvsdist b39f9d
cvsdist b39f9d
is found before the first code line when the file is entered, and the
cvsdist b39f9d
current value of (the general Emacs variable) `tab-width' does not
cvsdist b39f9d
equal <number>, `tab-width' is set to <number>, a message saying so is
cvsdist b39f9d
displayed in the echo area, and if `py-beep-if-tab-change' is non-nil
cvsdist b39f9d
the Emacs bell is also rung as a warning."
cvsdist b39f9d
  :type 'boolean
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-jump-on-exception t
cvsdist b39f9d
  "*Jump to innermost exception frame in *Python Output* buffer.
cvsdist b39f9d
When this variable is non-nil and an exception occurs when running
cvsdist b39f9d
Python code synchronously in a subprocess, jump immediately to the
cvsdist b39f9d
source code of the innermost traceback frame."
cvsdist b39f9d
  :type 'boolean
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-ask-about-save t
cvsdist b39f9d
  "If not nil, ask about which buffers to save before executing some code.
cvsdist b39f9d
Otherwise, all modified buffers are saved without asking."
cvsdist b39f9d
  :type 'boolean
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-backspace-function 'backward-delete-char-untabify
cvsdist b39f9d
  "*Function called by `py-electric-backspace' when deleting backwards."
cvsdist b39f9d
  :type 'function
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-delete-function 'delete-char
cvsdist b39f9d
  "*Function called by `py-electric-delete' when deleting forwards."
cvsdist b39f9d
  :type 'function
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
cvsdist b39f9d
(defcustom py-imenu-show-method-args-p nil 
cvsdist b39f9d
  "*Controls echoing of arguments of functions & methods in the Imenu buffer.
cvsdist b39f9d
When non-nil, arguments are printed."
cvsdist b39f9d
  :type 'boolean
cvsdist b39f9d
  :group 'python)
cvsdist b39f9d
(make-variable-buffer-local 'py-indent-offset)
cvsdist b39f9d
cvsdist c08065
(defcustom py-pdbtrack-do-tracking-p t
cvsdist c08065
  "*Controls whether the pdbtrack feature is enabled or not.
cvsdist c08065
When non-nil, pdbtrack is enabled in all comint-based buffers,
cvsdist c08065
e.g. shell buffers and the *Python* buffer.  When using pdb to debug a
cvsdist c08065
Python program, pdbtrack notices the pdb prompt and displays the
cvsdist c08065
source file and line that the program is stopped at, much the same way
cvsdist c08065
as gud-mode does for debugging C programs with gdb."
cvsdist c08065
  :type 'boolean
cvsdist c08065
  :group 'python)
cvsdist c08065
(make-variable-buffer-local 'py-pdbtrack-do-tracking-p)
cvsdist c08065
cvsdist c08065
(defcustom py-pdbtrack-minor-mode-string " PDB"
cvsdist c08065
  "*String to use in the minor mode list when pdbtrack is enabled."
cvsdist c08065
  :type 'string
cvsdist c08065
  :group 'python)
cvsdist c08065
cvsdist b39f9d
;; Not customizable
cvsdist b39f9d
(defvar py-master-file nil
cvsdist b39f9d
  "If non-nil, execute the named file instead of the buffer's file.
cvsdist b39f9d
The intent is to allow you to set this variable in the file's local
cvsdist b39f9d
variable section, e.g.:
cvsdist b39f9d
cvsdist b39f9d
    # Local Variables:
cvsdist b39f9d
    # py-master-file: \"master.py\"
cvsdist b39f9d
    # End:
cvsdist b39f9d
cvsdist b39f9d
so that typing \\[py-execute-buffer] in that buffer executes the named
cvsdist b39f9d
master file instead of the buffer's file.  If the file name has a
cvsdist b39f9d
relative path, the value of variable `default-directory' for the
cvsdist b39f9d
buffer is prepended to come up with a file name.")
cvsdist b39f9d
(make-variable-buffer-local 'py-master-file)
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cvsdist b39f9d
;; NO USER DEFINABLE VARIABLES BEYOND THIS POINT
cvsdist b39f9d
cvsdist b39f9d
(defconst py-emacs-features
cvsdist b39f9d
  (let (features)
cvsdist b39f9d
   features)
cvsdist b39f9d
  "A list of features extant in the Emacs you are using.
cvsdist b39f9d
There are many flavors of Emacs out there, with different levels of
cvsdist b39f9d
support for features needed by `python-mode'.")
cvsdist b39f9d
cvsdist b39f9d
(defvar python-font-lock-keywords
cvsdist b39f9d
  (let ((kw1 (mapconcat 'identity
cvsdist b39f9d
			'("and"      "assert"   "break"   "class"
cvsdist b39f9d
			  "continue" "def"      "del"     "elif"
cvsdist b39f9d
			  "else"     "except"   "exec"    "for"
cvsdist b39f9d
			  "from"     "global"   "if"      "import"
cvsdist b39f9d
			  "in"       "is"       "lambda"  "not"
cvsdist b39f9d
			  "or"       "pass"     "print"   "raise"
cvsdist c08065
			  "return"   "while"    "yield"
cvsdist b39f9d
			  )
cvsdist b39f9d
			"\\|"))
cvsdist b39f9d
	(kw2 (mapconcat 'identity
cvsdist b39f9d
			'("else:" "except:" "finally:" "try:")
cvsdist b39f9d
			"\\|"))
cvsdist b39f9d
	)
cvsdist b39f9d
    (list
cvsdist b39f9d
     ;; keywords
cvsdist b39f9d
     (cons (concat "\\b\\(" kw1 "\\)\\b[ \n\t(]") 1)
cvsdist b39f9d
     ;; block introducing keywords with immediately following colons.
cvsdist b39f9d
     ;; Yes "except" is in both lists.
cvsdist b39f9d
     (cons (concat "\\b\\(" kw2 "\\)[ \n\t(]") 1)
cvsdist c08065
     ;; `as' but only in "import foo as bar"
cvsdist c08065
     '("[ \t]*\\(\\bfrom\\b.*\\)?\\bimport\\b.*\\b\\(as\\)\\b" . 2)
cvsdist b39f9d
     ;; classes
cvsdist b39f9d
     '("\\bclass[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)"
cvsdist b39f9d
       1 font-lock-type-face)
cvsdist b39f9d
     ;; functions
cvsdist b39f9d
     '("\\bdef[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)"
cvsdist b39f9d
       1 font-lock-function-name-face)
cvsdist b39f9d
     ))
cvsdist b39f9d
  "Additional expressions to highlight in Python mode.")
cvsdist b39f9d
(put 'python-mode 'font-lock-defaults '(python-font-lock-keywords))
cvsdist b39f9d
cvsdist b39f9d
;; have to bind py-file-queue before installing the kill-emacs-hook
cvsdist b39f9d
(defvar py-file-queue nil
cvsdist b39f9d
  "Queue of Python temp files awaiting execution.
cvsdist b39f9d
Currently-active file is at the head of the list.")
cvsdist b39f9d
cvsdist c08065
(defvar py-pdbtrack-is-tracking-p nil)
cvsdist c08065
cvsdist c08065
cvsdist b39f9d

cvsdist b39f9d
;; Constants
cvsdist b39f9d
cvsdist b39f9d
(defconst py-stringlit-re
cvsdist b39f9d
  (concat
cvsdist b39f9d
   ;; These fail if backslash-quote ends the string (not worth
cvsdist b39f9d
   ;; fixing?).  They precede the short versions so that the first two
cvsdist b39f9d
   ;; quotes don't look like an empty short string.
cvsdist b39f9d
   ;;
cvsdist b39f9d
   ;; (maybe raw), long single quoted triple quoted strings (SQTQ),
cvsdist b39f9d
   ;; with potential embedded single quotes
cvsdist b39f9d
   "[rR]?'''[^']*\\(\\('[^']\\|''[^']\\)[^']*\\)*'''"
cvsdist b39f9d
   "\\|"
cvsdist b39f9d
   ;; (maybe raw), long double quoted triple quoted strings (DQTQ),
cvsdist b39f9d
   ;; with potential embedded double quotes
cvsdist b39f9d
   "[rR]?\"\"\"[^\"]*\\(\\(\"[^\"]\\|\"\"[^\"]\\)[^\"]*\\)*\"\"\""
cvsdist b39f9d
   "\\|"
cvsdist b39f9d
   "[rR]?'\\([^'\n\\]\\|\\\\.\\)*'"	; single-quoted
cvsdist b39f9d
   "\\|"				; or
cvsdist b39f9d
   "[rR]?\"\\([^\"\n\\]\\|\\\\.\\)*\""	; double-quoted
cvsdist b39f9d
   )
cvsdist b39f9d
  "Regular expression matching a Python string literal.")
cvsdist b39f9d
cvsdist b39f9d
(defconst py-continued-re
cvsdist b39f9d
  ;; This is tricky because a trailing backslash does not mean
cvsdist b39f9d
  ;; continuation if it's in a comment
cvsdist b39f9d
  (concat
cvsdist b39f9d
   "\\(" "[^#'\"\n\\]" "\\|" py-stringlit-re "\\)*"
cvsdist b39f9d
   "\\\\$")
cvsdist b39f9d
  "Regular expression matching Python backslash continuation lines.")
cvsdist b39f9d
  
cvsdist b39f9d
(defconst py-blank-or-comment-re "[ \t]*\\($\\|#\\)"
cvsdist b39f9d
  "Regular expression matching a blank or comment line.")
cvsdist b39f9d
cvsdist b39f9d
(defconst py-outdent-re
cvsdist b39f9d
  (concat "\\(" (mapconcat 'identity
cvsdist b39f9d
			   '("else:"
cvsdist b39f9d
			     "except\\(\\s +.*\\)?:"
cvsdist b39f9d
			     "finally:"
cvsdist b39f9d
			     "elif\\s +.*:")
cvsdist b39f9d
			   "\\|")
cvsdist b39f9d
	  "\\)")
cvsdist b39f9d
  "Regular expression matching statements to be dedented one level.")
cvsdist b39f9d
  
cvsdist b39f9d
(defconst py-block-closing-keywords-re
cvsdist b39f9d
  "\\(return\\|raise\\|break\\|continue\\|pass\\)"
cvsdist b39f9d
  "Regular expression matching keywords which typically close a block.")
cvsdist b39f9d
cvsdist b39f9d
(defconst py-no-outdent-re
cvsdist b39f9d
  (concat
cvsdist b39f9d
   "\\("
cvsdist b39f9d
   (mapconcat 'identity
cvsdist b39f9d
	      (list "try:"
cvsdist b39f9d
		    "except\\(\\s +.*\\)?:"
cvsdist b39f9d
		    "while\\s +.*:"
cvsdist b39f9d
		    "for\\s +.*:"
cvsdist b39f9d
		    "if\\s +.*:"
cvsdist b39f9d
		    "elif\\s +.*:"
cvsdist b39f9d
		    (concat py-block-closing-keywords-re "[ \t\n]")
cvsdist b39f9d
		    )
cvsdist b39f9d
	      "\\|")
cvsdist b39f9d
	  "\\)")
cvsdist b39f9d
  "Regular expression matching lines not to dedent after.")
cvsdist b39f9d
cvsdist b39f9d
(defconst py-defun-start-re
cvsdist b39f9d
  "^\\([ \t]*\\)def[ \t]+\\([a-zA-Z_0-9]+\\)\\|\\(^[a-zA-Z_0-9]+\\)[ \t]*="
cvsdist b39f9d
  ;; If you change this, you probably have to change py-current-defun
cvsdist b39f9d
  ;; as well.  This is only used by py-current-defun to find the name
cvsdist b39f9d
  ;; for add-log.el.
cvsdist b39f9d
  "Regular expression matching a function, method, or variable assignment.")
cvsdist b39f9d
cvsdist b39f9d
(defconst py-class-start-re "^class[ \t]*\\([a-zA-Z_0-9]+\\)"
cvsdist b39f9d
  ;; If you change this, you probably have to change py-current-defun
cvsdist b39f9d
  ;; as well.  This is only used by py-current-defun to find the name
cvsdist b39f9d
  ;; for add-log.el.
cvsdist b39f9d
  "Regular expression for finding a class name.")
cvsdist b39f9d
cvsdist b39f9d
(defconst py-traceback-line-re
cvsdist b39f9d
  "[ \t]+File \"\\([^\"]+\\)\", line \\([0-9]+\\)"
cvsdist b39f9d
  "Regular expression that describes tracebacks.")
cvsdist b39f9d
cvsdist c08065
;; pdbtrack contants
cvsdist c08065
(defconst py-pdbtrack-stack-entry-regexp
cvsdist c08065
  "> \\([^(]+\\)(\\([0-9]+\\))[?a-zA-Z0-9_]+()"
cvsdist c08065
  "Regular expression pdbtrack uses to find a stack trace entry.")
cvsdist c08065
cvsdist c08065
(defconst py-pdbtrack-input-prompt "\n[(<]?pdb[>)]? "
cvsdist c08065
  "Regular expression pdbtrack uses to recognize a pdb prompt.")
cvsdist c08065
cvsdist c08065
(defconst py-pdbtrack-track-range 10000
cvsdist c08065
  "Max number of characters from end of buffer to search for stack entry.")
cvsdist c08065
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Major mode boilerplate
cvsdist b39f9d
cvsdist b39f9d
;; define a mode-specific abbrev table for those who use such things
cvsdist b39f9d
(defvar python-mode-abbrev-table nil
cvsdist b39f9d
  "Abbrev table in use in `python-mode' buffers.")
cvsdist b39f9d
(define-abbrev-table 'python-mode-abbrev-table nil)
cvsdist b39f9d
cvsdist b39f9d
(defvar python-mode-hook nil
cvsdist b39f9d
  "*Hook called by `python-mode'.")
cvsdist b39f9d
cvsdist b39f9d
;; In previous version of python-mode.el, the hook was incorrectly
cvsdist b39f9d
;; called py-mode-hook, and was not defvar'd.  Deprecate its use.
cvsdist b39f9d
(and (fboundp 'make-obsolete-variable)
cvsdist b39f9d
     (make-obsolete-variable 'py-mode-hook 'python-mode-hook))
cvsdist b39f9d
cvsdist b39f9d
(defvar py-mode-map ()
cvsdist b39f9d
  "Keymap used in `python-mode' buffers.")
cvsdist b39f9d
(if py-mode-map
cvsdist b39f9d
    nil
cvsdist b39f9d
  (setq py-mode-map (make-sparse-keymap))
cvsdist b39f9d
  ;; electric keys
cvsdist b39f9d
  (define-key py-mode-map ":" 'py-electric-colon)
cvsdist b39f9d
  ;; indentation level modifiers
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-l"  'py-shift-region-left)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-r"  'py-shift-region-right)
cvsdist b39f9d
  (define-key py-mode-map "\C-c<"     'py-shift-region-left)
cvsdist b39f9d
  (define-key py-mode-map "\C-c>"     'py-shift-region-right)
cvsdist b39f9d
  ;; subprocess commands
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-c"  'py-execute-buffer)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-m"  'py-execute-import-or-reload)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-s"  'py-execute-string)
cvsdist b39f9d
  (define-key py-mode-map "\C-c|"     'py-execute-region)
cvsdist b39f9d
  (define-key py-mode-map "\e\C-x"    'py-execute-def-or-class)
cvsdist b39f9d
  (define-key py-mode-map "\C-c!"     'py-shell)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-t"  'py-toggle-shells)
cvsdist b39f9d
  ;; Caution!  Enter here at your own risk.  We are trying to support
cvsdist b39f9d
  ;; several behaviors and it gets disgusting. :-( This logic ripped
cvsdist b39f9d
  ;; largely from CC Mode.
cvsdist b39f9d
  ;;
cvsdist b39f9d
  ;; In XEmacs 19, Emacs 19, and Emacs 20, we use this to bind
cvsdist b39f9d
  ;; backwards deletion behavior to DEL, which both Delete and
cvsdist b39f9d
  ;; Backspace get translated to.  There's no way to separate this
cvsdist b39f9d
  ;; behavior in a clean way, so deal with it!  Besides, it's been
cvsdist b39f9d
  ;; this way since the dawn of time.
cvsdist b39f9d
  (if (not (boundp 'delete-key-deletes-forward))
cvsdist b39f9d
      (define-key py-mode-map "\177" 'py-electric-backspace)
cvsdist b39f9d
    ;; However, XEmacs 20 actually achieved enlightenment.  It is
cvsdist b39f9d
    ;; possible to sanely define both backward and forward deletion
cvsdist b39f9d
    ;; behavior under X separately (TTYs are forever beyond hope, but
cvsdist b39f9d
    ;; who cares?  XEmacs 20 does the right thing with these too).
cvsdist b39f9d
    (define-key py-mode-map [delete]    'py-electric-delete)
cvsdist b39f9d
    (define-key py-mode-map [backspace] 'py-electric-backspace))
cvsdist b39f9d
  ;; Separate M-BS from C-M-h.  The former should remain
cvsdist b39f9d
  ;; backward-kill-word.
cvsdist b39f9d
  (define-key py-mode-map [(control meta h)] 'py-mark-def-or-class)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-k"  'py-mark-block)
cvsdist b39f9d
  ;; Miscellaneous
cvsdist b39f9d
  (define-key py-mode-map "\C-c:"     'py-guess-indent-offset)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\t"    'py-indent-region)
cvsdist c08065
  (define-key py-mode-map "\C-c\C-d"  'py-pdbtrack-toggle-stack-tracking)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-n"  'py-next-statement)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-p"  'py-previous-statement)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-u"  'py-goto-block-up)
cvsdist b39f9d
  (define-key py-mode-map "\C-c#"     'py-comment-region)
cvsdist b39f9d
  (define-key py-mode-map "\C-c?"     'py-describe-mode)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-hm" 'py-describe-mode)
cvsdist b39f9d
  (define-key py-mode-map "\e\C-a"    'py-beginning-of-def-or-class)
cvsdist b39f9d
  (define-key py-mode-map "\e\C-e"    'py-end-of-def-or-class)
cvsdist b39f9d
  (define-key py-mode-map "\C-c-"     'py-up-exception)
cvsdist b39f9d
  (define-key py-mode-map "\C-c="     'py-down-exception)
cvsdist b39f9d
  ;; stuff that is `standard' but doesn't interface well with
cvsdist b39f9d
  ;; python-mode, which forces us to rebind to special commands
cvsdist b39f9d
  (define-key py-mode-map "\C-xnd"    'py-narrow-to-defun)
cvsdist b39f9d
  ;; information
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-b" 'py-submit-bug-report)
cvsdist b39f9d
  (define-key py-mode-map "\C-c\C-v" 'py-version)
cvsdist b39f9d
  ;; shadow global bindings for newline-and-indent w/ the py- version.
cvsdist b39f9d
  ;; BAW - this is extremely bad form, but I'm not going to change it
cvsdist b39f9d
  ;; for now.
cvsdist b39f9d
  (mapcar #'(lambda (key)
cvsdist b39f9d
	      (define-key py-mode-map key 'py-newline-and-indent))
cvsdist b39f9d
	  (where-is-internal 'newline-and-indent))
cvsdist b39f9d
  ;; Force RET to be py-newline-and-indent even if it didn't get
cvsdist b39f9d
  ;; mapped by the above code.  motivation: Emacs' default binding for
cvsdist b39f9d
  ;; RET is `newline' and C-j is `newline-and-indent'.  Most Pythoneers
cvsdist b39f9d
  ;; expect RET to do a `py-newline-and-indent' and any Emacsers who
cvsdist b39f9d
  ;; dislike this are probably knowledgeable enough to do a rebind.
cvsdist b39f9d
  ;; However, we do *not* change C-j since many Emacsers have already
cvsdist b39f9d
  ;; swapped RET and C-j and they don't want C-j bound to `newline' to 
cvsdist b39f9d
  ;; change.
cvsdist b39f9d
  (define-key py-mode-map "\C-m" 'py-newline-and-indent)
cvsdist b39f9d
  )
cvsdist b39f9d
cvsdist b39f9d
(defvar py-mode-output-map nil
cvsdist b39f9d
  "Keymap used in *Python Output* buffers.")
cvsdist b39f9d
(if py-mode-output-map
cvsdist b39f9d
    nil
cvsdist b39f9d
  (setq py-mode-output-map (make-sparse-keymap))
cvsdist b39f9d
  (define-key py-mode-output-map [button2]  'py-mouseto-exception)
cvsdist b39f9d
  (define-key py-mode-output-map "\C-c\C-c" 'py-goto-exception)
cvsdist b39f9d
  ;; TBD: Disable all self-inserting keys.  This is bogus, we should
cvsdist b39f9d
  ;; really implement this as *Python Output* buffer being read-only
cvsdist b39f9d
  (mapcar #' (lambda (key)
cvsdist b39f9d
	       (define-key py-mode-output-map key
cvsdist b39f9d
		 #'(lambda () (interactive) (beep))))
cvsdist b39f9d
	     (where-is-internal 'self-insert-command))
cvsdist b39f9d
  )
cvsdist b39f9d
cvsdist b39f9d
(defvar py-shell-map nil
cvsdist b39f9d
  "Keymap used in *Python* shell buffers.")
cvsdist b39f9d
(if py-shell-map
cvsdist b39f9d
    nil
cvsdist b39f9d
  (setq py-shell-map (copy-keymap comint-mode-map))
cvsdist b39f9d
  (define-key py-shell-map [tab]   'tab-to-tab-stop)
cvsdist b39f9d
  (define-key py-shell-map "\C-c-" 'py-up-exception)
cvsdist b39f9d
  (define-key py-shell-map "\C-c=" 'py-down-exception)
cvsdist b39f9d
  )
cvsdist b39f9d
cvsdist b39f9d
(defvar py-mode-syntax-table nil
cvsdist b39f9d
  "Syntax table used in `python-mode' buffers.")
cvsdist b39f9d
(if py-mode-syntax-table
cvsdist b39f9d
    nil
cvsdist b39f9d
  (setq py-mode-syntax-table (make-syntax-table))
cvsdist b39f9d
  (modify-syntax-entry ?\( "()" py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\) ")(" py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\[ "(]" py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\] ")[" py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\{ "(}" py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\} "){" py-mode-syntax-table)
cvsdist b39f9d
  ;; Add operator symbols misassigned in the std table
cvsdist b39f9d
  (modify-syntax-entry ?\$ "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\% "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\& "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\* "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\+ "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\- "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\/ "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\< "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\= "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\> "."  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\| "."  py-mode-syntax-table)
cvsdist b39f9d
  ;; For historical reasons, underscore is word class instead of
cvsdist b39f9d
  ;; symbol class.  GNU conventions say it should be symbol class, but
cvsdist b39f9d
  ;; there's a natural conflict between what major mode authors want
cvsdist b39f9d
  ;; and what users expect from `forward-word' and `backward-word'.
cvsdist b39f9d
  ;; Guido and I have hashed this out and have decided to keep
cvsdist b39f9d
  ;; underscore in word class.  If you're tempted to change it, try
cvsdist b39f9d
  ;; binding M-f and M-b to py-forward-into-nomenclature and
cvsdist b39f9d
  ;; py-backward-into-nomenclature instead.  This doesn't help in all
cvsdist b39f9d
  ;; situations where you'd want the different behavior
cvsdist b39f9d
  ;; (e.g. backward-kill-word).
cvsdist b39f9d
  (modify-syntax-entry ?\_ "w"  py-mode-syntax-table)
cvsdist b39f9d
  ;; Both single quote and double quote are string delimiters
cvsdist b39f9d
  (modify-syntax-entry ?\' "\"" py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\" "\"" py-mode-syntax-table)
cvsdist b39f9d
  ;; backquote is open and close paren
cvsdist b39f9d
  (modify-syntax-entry ?\` "$"  py-mode-syntax-table)
cvsdist b39f9d
  ;; comment delimiters
cvsdist b39f9d
  (modify-syntax-entry ?\# "<"  py-mode-syntax-table)
cvsdist b39f9d
  (modify-syntax-entry ?\n ">"  py-mode-syntax-table)
cvsdist b39f9d
  )
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Utilities
cvsdist b39f9d
cvsdist b39f9d
(defmacro py-safe (&rest body)
cvsdist b39f9d
  "Safely execute BODY, return nil if an error occurred."
cvsdist b39f9d
  (` (condition-case nil
cvsdist b39f9d
	 (progn (,@ body))
cvsdist b39f9d
       (error nil))))
cvsdist b39f9d
cvsdist b39f9d
(defsubst py-keep-region-active ()
cvsdist b39f9d
  "Keep the region active in XEmacs."
cvsdist b39f9d
  ;; Ignore byte-compiler warnings you might see.  Also note that
cvsdist b39f9d
  ;; FSF's Emacs 19 does it differently; its policy doesn't require us
cvsdist b39f9d
  ;; to take explicit action.
cvsdist b39f9d
  (and (boundp 'zmacs-region-stays)
cvsdist b39f9d
       (setq zmacs-region-stays t)))
cvsdist b39f9d
cvsdist b39f9d
(defsubst py-point (position)
cvsdist b39f9d
  "Returns the value of point at certain commonly referenced POSITIONs.
cvsdist b39f9d
POSITION can be one of the following symbols:
cvsdist b39f9d
cvsdist b39f9d
  bol  -- beginning of line
cvsdist b39f9d
  eol  -- end of line
cvsdist b39f9d
  bod  -- beginning of def or class
cvsdist b39f9d
  eod  -- end of def or class
cvsdist b39f9d
  bob  -- beginning of buffer
cvsdist b39f9d
  eob  -- end of buffer
cvsdist b39f9d
  boi  -- back to indentation
cvsdist b39f9d
  bos  -- beginning of statement
cvsdist b39f9d
cvsdist b39f9d
This function does not modify point or mark."
cvsdist b39f9d
  (let ((here (point)))
cvsdist b39f9d
    (cond
cvsdist b39f9d
     ((eq position 'bol) (beginning-of-line))
cvsdist b39f9d
     ((eq position 'eol) (end-of-line))
cvsdist b39f9d
     ((eq position 'bod) (py-beginning-of-def-or-class))
cvsdist b39f9d
     ((eq position 'eod) (py-end-of-def-or-class))
cvsdist b39f9d
     ;; Kind of funny, I know, but useful for py-up-exception.
cvsdist b39f9d
     ((eq position 'bob) (beginning-of-buffer))
cvsdist b39f9d
     ((eq position 'eob) (end-of-buffer))
cvsdist b39f9d
     ((eq position 'boi) (back-to-indentation))
cvsdist b39f9d
     ((eq position 'bos) (py-goto-initial-line))
cvsdist b39f9d
     (t (error "Unknown buffer position requested: %s" position))
cvsdist b39f9d
     )
cvsdist b39f9d
    (prog1
cvsdist b39f9d
	(point)
cvsdist b39f9d
      (goto-char here))))
cvsdist b39f9d
cvsdist b39f9d
(defsubst py-highlight-line (from to file line)
cvsdist b39f9d
  (cond
cvsdist b39f9d
   ((fboundp 'make-extent)
cvsdist b39f9d
    ;; XEmacs
cvsdist b39f9d
    (let ((e (make-extent from to)))
cvsdist b39f9d
      (set-extent-property e 'mouse-face 'highlight)
cvsdist b39f9d
      (set-extent-property e 'py-exc-info (cons file line))
cvsdist b39f9d
      (set-extent-property e 'keymap py-mode-output-map)))
cvsdist b39f9d
   (t
cvsdist b39f9d
    ;; Emacs -- Please port this!
cvsdist b39f9d
    )
cvsdist b39f9d
   ))
cvsdist b39f9d
cvsdist b39f9d
(defun py-in-literal (&optional lim)
cvsdist b39f9d
  "Return non-nil if point is in a Python literal (a comment or string).
cvsdist b39f9d
Optional argument LIM indicates the beginning of the containing form,
cvsdist b39f9d
i.e. the limit on how far back to scan."
cvsdist b39f9d
  ;; This is the version used for non-XEmacs, which has a nicer
cvsdist b39f9d
  ;; interface.
cvsdist b39f9d
  ;;
cvsdist b39f9d
  ;; WARNING: Watch out for infinite recursion.
cvsdist b39f9d
  (let* ((lim (or lim (py-point 'bod)))
cvsdist b39f9d
	 (state (parse-partial-sexp lim (point))))
cvsdist b39f9d
    (cond
cvsdist b39f9d
     ((nth 3 state) 'string)
cvsdist b39f9d
     ((nth 4 state) 'comment)
cvsdist b39f9d
     (t nil))))
cvsdist b39f9d
cvsdist b39f9d
;; XEmacs has a built-in function that should make this much quicker.
cvsdist b39f9d
;; In this case, lim is ignored
cvsdist b39f9d
(defun py-fast-in-literal (&optional lim)
cvsdist b39f9d
  "Fast version of `py-in-literal', used only by XEmacs.
cvsdist b39f9d
Optional LIM is ignored."
cvsdist b39f9d
  ;; don't have to worry about context == 'block-comment
cvsdist b39f9d
  (buffer-syntactic-context))
cvsdist b39f9d
cvsdist b39f9d
(if (fboundp 'buffer-syntactic-context)
cvsdist b39f9d
    (defalias 'py-in-literal 'py-fast-in-literal))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Menu definitions, only relevent if you have the easymenu.el package
cvsdist b39f9d
;; (standard in the latest Emacs 19 and XEmacs 19 distributions).
cvsdist b39f9d
(defvar py-menu nil
cvsdist b39f9d
  "Menu for Python Mode.
cvsdist b39f9d
This menu will get created automatically if you have the `easymenu'
cvsdist b39f9d
package.  Note that the latest X/Emacs releases contain this package.")
cvsdist b39f9d
cvsdist b39f9d
(and (py-safe (require 'easymenu) t)
cvsdist b39f9d
     (easy-menu-define
cvsdist b39f9d
      py-menu py-mode-map "Python Mode menu"
cvsdist b39f9d
      '("Python"
cvsdist b39f9d
	["Comment Out Region"   py-comment-region  (mark)]
cvsdist b39f9d
	["Uncomment Region"     (py-comment-region (point) (mark) '(4)) (mark)]
cvsdist b39f9d
	"-"
cvsdist b39f9d
	["Mark current block"   py-mark-block t]
cvsdist b39f9d
	["Mark current def"     py-mark-def-or-class t]
cvsdist b39f9d
	["Mark current class"   (py-mark-def-or-class t) t]
cvsdist b39f9d
	"-"
cvsdist b39f9d
	["Shift region left"    py-shift-region-left (mark)]
cvsdist b39f9d
	["Shift region right"   py-shift-region-right (mark)]
cvsdist b39f9d
	"-"
cvsdist b39f9d
	["Import/reload file"   py-execute-import-or-reload t]
cvsdist b39f9d
	["Execute buffer"       py-execute-buffer t]
cvsdist b39f9d
	["Execute region"       py-execute-region (mark)]
cvsdist b39f9d
	["Execute def or class" py-execute-def-or-class (mark)]
cvsdist b39f9d
	["Execute string"       py-execute-string t]
cvsdist b39f9d
	["Start interpreter..." py-shell t]
cvsdist b39f9d
	"-"
cvsdist b39f9d
	["Go to start of block" py-goto-block-up t]
cvsdist b39f9d
	["Go to start of class" (py-beginning-of-def-or-class t) t]
cvsdist b39f9d
	["Move to end of class" (py-end-of-def-or-class t) t]
cvsdist b39f9d
	["Move to start of def" py-beginning-of-def-or-class t]
cvsdist b39f9d
	["Move to end of def"   py-end-of-def-or-class t]
cvsdist b39f9d
	"-"
cvsdist b39f9d
	["Describe mode"        py-describe-mode t]
cvsdist b39f9d
	)))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Imenu definitions
cvsdist b39f9d
(defvar py-imenu-class-regexp
cvsdist b39f9d
  (concat				; <<classes>>
cvsdist b39f9d
   "\\("				;
cvsdist b39f9d
   "^[ \t]*"				; newline and maybe whitespace
cvsdist b39f9d
   "\\(class[ \t]+[a-zA-Z0-9_]+\\)"	; class name
cvsdist b39f9d
					; possibly multiple superclasses
cvsdist b39f9d
   "\\([ \t]*\\((\\([a-zA-Z0-9_,. \t\n]\\)*)\\)?\\)"
cvsdist b39f9d
   "[ \t]*:"				; and the final :
cvsdist b39f9d
   "\\)"				; >>classes<<
cvsdist b39f9d
   )
cvsdist b39f9d
  "Regexp for Python classes for use with the Imenu package."
cvsdist b39f9d
  )
cvsdist b39f9d
cvsdist b39f9d
(defvar py-imenu-method-regexp
cvsdist b39f9d
  (concat                               ; <<methods and functions>>
cvsdist b39f9d
   "\\("                                ; 
cvsdist b39f9d
   "^[ \t]*"                            ; new line and maybe whitespace
cvsdist b39f9d
   "\\(def[ \t]+"                       ; function definitions start with def
cvsdist b39f9d
   "\\([a-zA-Z0-9_]+\\)"                ;   name is here
cvsdist b39f9d
					;   function arguments...
cvsdist b39f9d
;;   "[ \t]*(\\([-+/a-zA-Z0-9_=,\* \t\n.()\"'#]*\\))"
cvsdist b39f9d
   "[ \t]*(\\([^:#]*\\))"
cvsdist b39f9d
   "\\)"                                ; end of def
cvsdist b39f9d
   "[ \t]*:"                            ; and then the :
cvsdist b39f9d
   "\\)"                                ; >>methods and functions<<
cvsdist b39f9d
   )
cvsdist b39f9d
  "Regexp for Python methods/functions for use with the Imenu package."
cvsdist b39f9d
  )
cvsdist b39f9d
cvsdist b39f9d
(defvar py-imenu-method-no-arg-parens '(2 8)
cvsdist b39f9d
  "Indices into groups of the Python regexp for use with Imenu.
cvsdist b39f9d
cvsdist b39f9d
Using these values will result in smaller Imenu lists, as arguments to
cvsdist b39f9d
functions are not listed.
cvsdist b39f9d
cvsdist b39f9d
See the variable `py-imenu-show-method-args-p' for more
cvsdist b39f9d
information.")
cvsdist b39f9d
cvsdist b39f9d
(defvar py-imenu-method-arg-parens '(2 7)
cvsdist b39f9d
  "Indices into groups of the Python regexp for use with imenu.
cvsdist b39f9d
Using these values will result in large Imenu lists, as arguments to
cvsdist b39f9d
functions are listed.
cvsdist b39f9d
cvsdist b39f9d
See the variable `py-imenu-show-method-args-p' for more
cvsdist b39f9d
information.")
cvsdist b39f9d
cvsdist b39f9d
;; Note that in this format, this variable can still be used with the
cvsdist b39f9d
;; imenu--generic-function. Otherwise, there is no real reason to have
cvsdist b39f9d
;; it.
cvsdist b39f9d
(defvar py-imenu-generic-expression
cvsdist b39f9d
  (cons
cvsdist b39f9d
   (concat 
cvsdist b39f9d
    py-imenu-class-regexp
cvsdist b39f9d
    "\\|"				; or...
cvsdist b39f9d
    py-imenu-method-regexp
cvsdist b39f9d
    )
cvsdist b39f9d
   py-imenu-method-no-arg-parens)
cvsdist b39f9d
  "Generic Python expression which may be used directly with Imenu.
cvsdist b39f9d
Used by setting the variable `imenu-generic-expression' to this value.
cvsdist b39f9d
Also, see the function \\[py-imenu-create-index] for a better
cvsdist b39f9d
alternative for finding the index.")
cvsdist b39f9d
cvsdist b39f9d
;; These next two variables are used when searching for the Python
cvsdist b39f9d
;; class/definitions. Just saving some time in accessing the
cvsdist b39f9d
;; generic-python-expression, really.
cvsdist b39f9d
(defvar py-imenu-generic-regexp nil)
cvsdist b39f9d
(defvar py-imenu-generic-parens nil)
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d
(defun py-imenu-create-index-function ()
cvsdist b39f9d
  "Python interface function for the Imenu package.
cvsdist b39f9d
Finds all Python classes and functions/methods. Calls function
cvsdist b39f9d
\\[py-imenu-create-index-engine].  See that function for the details
cvsdist b39f9d
of how this works."
cvsdist b39f9d
  (setq py-imenu-generic-regexp (car py-imenu-generic-expression)
cvsdist b39f9d
	py-imenu-generic-parens (if py-imenu-show-method-args-p
cvsdist b39f9d
				    py-imenu-method-arg-parens
cvsdist b39f9d
				  py-imenu-method-no-arg-parens))
cvsdist b39f9d
  (goto-char (point-min))
cvsdist b39f9d
  ;; Warning: When the buffer has no classes or functions, this will
cvsdist b39f9d
  ;; return nil, which seems proper according to the Imenu API, but
cvsdist b39f9d
  ;; causes an error in the XEmacs port of Imenu.  Sigh.
cvsdist b39f9d
  (py-imenu-create-index-engine nil))
cvsdist b39f9d
cvsdist b39f9d
(defun py-imenu-create-index-engine (&optional start-indent)
cvsdist b39f9d
  "Function for finding Imenu definitions in Python.
cvsdist b39f9d
cvsdist b39f9d
Finds all definitions (classes, methods, or functions) in a Python
cvsdist b39f9d
file for the Imenu package.
cvsdist b39f9d
cvsdist b39f9d
Returns a possibly nested alist of the form
cvsdist b39f9d
cvsdist b39f9d
	(INDEX-NAME . INDEX-POSITION)
cvsdist b39f9d
cvsdist b39f9d
The second element of the alist may be an alist, producing a nested
cvsdist b39f9d
list as in
cvsdist b39f9d
cvsdist b39f9d
	(INDEX-NAME . INDEX-ALIST)
cvsdist b39f9d
cvsdist b39f9d
This function should not be called directly, as it calls itself
cvsdist b39f9d
recursively and requires some setup.  Rather this is the engine for
cvsdist b39f9d
the function \\[py-imenu-create-index-function].
cvsdist b39f9d
cvsdist b39f9d
It works recursively by looking for all definitions at the current
cvsdist b39f9d
indention level.  When it finds one, it adds it to the alist.  If it
cvsdist b39f9d
finds a definition at a greater indentation level, it removes the
cvsdist b39f9d
previous definition from the alist. In its place it adds all
cvsdist b39f9d
definitions found at the next indentation level.  When it finds a
cvsdist b39f9d
definition that is less indented then the current level, it returns
cvsdist b39f9d
the alist it has created thus far.
cvsdist b39f9d
cvsdist b39f9d
The optional argument START-INDENT indicates the starting indentation
cvsdist b39f9d
at which to continue looking for Python classes, methods, or
cvsdist b39f9d
functions.  If this is not supplied, the function uses the indentation
cvsdist b39f9d
of the first definition found."
cvsdist b39f9d
  (let (index-alist
cvsdist b39f9d
	sub-method-alist
cvsdist b39f9d
	looking-p
cvsdist b39f9d
	def-name prev-name
cvsdist b39f9d
	cur-indent def-pos
cvsdist b39f9d
	(class-paren (first  py-imenu-generic-parens)) 
cvsdist b39f9d
	(def-paren   (second py-imenu-generic-parens)))
cvsdist b39f9d
    (setq looking-p
cvsdist b39f9d
	  (re-search-forward py-imenu-generic-regexp (point-max) t))
cvsdist b39f9d
    (while looking-p
cvsdist b39f9d
      (save-excursion
cvsdist b39f9d
	;; used to set def-name to this value but generic-extract-name
cvsdist b39f9d
	;; is new to imenu-1.14. this way it still works with
cvsdist b39f9d
	;; imenu-1.11
cvsdist b39f9d
	;;(imenu--generic-extract-name py-imenu-generic-parens))
cvsdist b39f9d
	(let ((cur-paren (if (match-beginning class-paren)
cvsdist b39f9d
			     class-paren def-paren)))
cvsdist b39f9d
	  (setq def-name
cvsdist b39f9d
		(buffer-substring-no-properties (match-beginning cur-paren)
cvsdist b39f9d
						(match-end cur-paren))))
cvsdist b39f9d
	(save-match-data
cvsdist b39f9d
	  (py-beginning-of-def-or-class 'either))
cvsdist b39f9d
	(beginning-of-line)
cvsdist b39f9d
	(setq cur-indent (current-indentation)))
cvsdist b39f9d
      ;; HACK: want to go to the next correct definition location.  We
cvsdist b39f9d
      ;; explicitly list them here but it would be better to have them
cvsdist b39f9d
      ;; in a list.
cvsdist b39f9d
      (setq def-pos
cvsdist b39f9d
	    (or (match-beginning class-paren)
cvsdist b39f9d
		(match-beginning def-paren)))
cvsdist b39f9d
      ;; if we don't have a starting indent level, take this one
cvsdist b39f9d
      (or start-indent
cvsdist b39f9d
	  (setq start-indent cur-indent))
cvsdist b39f9d
      ;; if we don't have class name yet, take this one
cvsdist b39f9d
      (or prev-name
cvsdist b39f9d
	  (setq prev-name def-name))
cvsdist b39f9d
      ;; what level is the next definition on?  must be same, deeper
cvsdist b39f9d
      ;; or shallower indentation
cvsdist b39f9d
      (cond
cvsdist b39f9d
       ;; at the same indent level, add it to the list...
cvsdist b39f9d
       ((= start-indent cur-indent)
cvsdist b39f9d
	(push (cons def-name def-pos) index-alist))
cvsdist b39f9d
       ;; deeper indented expression, recurse
cvsdist b39f9d
       ((< start-indent cur-indent)
cvsdist b39f9d
	;; the point is currently on the expression we're supposed to
cvsdist b39f9d
	;; start on, so go back to the last expression. The recursive
cvsdist b39f9d
	;; call will find this place again and add it to the correct
cvsdist b39f9d
	;; list
cvsdist b39f9d
	(re-search-backward py-imenu-generic-regexp (point-min) 'move)
cvsdist b39f9d
	(setq sub-method-alist (py-imenu-create-index-engine cur-indent))
cvsdist b39f9d
	(if sub-method-alist
cvsdist b39f9d
	    ;; we put the last element on the index-alist on the start
cvsdist b39f9d
	    ;; of the submethod alist so the user can still get to it.
cvsdist b39f9d
	    (let ((save-elmt (pop index-alist)))
cvsdist b39f9d
	      (push (cons prev-name
cvsdist b39f9d
			  (cons save-elmt sub-method-alist))
cvsdist b39f9d
		    index-alist))))
cvsdist b39f9d
       ;; found less indented expression, we're done.
cvsdist b39f9d
       (t 
cvsdist b39f9d
	(setq looking-p nil)
cvsdist b39f9d
	(re-search-backward py-imenu-generic-regexp (point-min) t)))
cvsdist b39f9d
      ;; end-cond
cvsdist b39f9d
      (setq prev-name def-name)
cvsdist b39f9d
      (and looking-p
cvsdist b39f9d
	   (setq looking-p
cvsdist b39f9d
		 (re-search-forward py-imenu-generic-regexp
cvsdist b39f9d
				    (point-max) 'move))))
cvsdist b39f9d
    (nreverse index-alist)))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;;;###autoload
cvsdist b39f9d
(defun python-mode ()
cvsdist b39f9d
  "Major mode for editing Python files.
cvsdist b39f9d
To submit a problem report, enter `\\[py-submit-bug-report]' from a
cvsdist b39f9d
`python-mode' buffer.  Do `\\[py-describe-mode]' for detailed
cvsdist b39f9d
documentation.  To see what version of `python-mode' you are running,
cvsdist b39f9d
enter `\\[py-version]'.
cvsdist b39f9d
cvsdist b39f9d
This mode knows about Python indentation, tokens, comments and
cvsdist b39f9d
continuation lines.  Paragraphs are separated by blank lines only.
cvsdist b39f9d
cvsdist b39f9d
COMMANDS
cvsdist b39f9d
\\{py-mode-map}
cvsdist b39f9d
VARIABLES
cvsdist b39f9d
cvsdist b39f9d
py-indent-offset\t\tindentation increment
cvsdist b39f9d
py-block-comment-prefix\t\tcomment string used by `comment-region'
cvsdist b39f9d
py-python-command\t\tshell command to invoke Python interpreter
cvsdist b39f9d
py-temp-directory\t\tdirectory used for temp files (if needed)
cvsdist b39f9d
py-beep-if-tab-change\t\tring the bell if `tab-width' is changed"
cvsdist b39f9d
  (interactive)
cvsdist b39f9d
  ;; set up local variables
cvsdist b39f9d
  (kill-all-local-variables)
cvsdist b39f9d
  (make-local-variable 'font-lock-defaults)
cvsdist b39f9d
  (make-local-variable 'paragraph-separate)
cvsdist b39f9d
  (make-local-variable 'paragraph-start)
cvsdist b39f9d
  (make-local-variable 'require-final-newline)
cvsdist b39f9d
  (make-local-variable 'comment-start)
cvsdist b39f9d
  (make-local-variable 'comment-end)
cvsdist b39f9d
  (make-local-variable 'comment-start-skip)
cvsdist b39f9d
  (make-local-variable 'comment-column)
cvsdist b39f9d
  (make-local-variable 'comment-indent-function)
cvsdist b39f9d
  (make-local-variable 'indent-region-function)
cvsdist b39f9d
  (make-local-variable 'indent-line-function)
cvsdist b39f9d
  (make-local-variable 'add-log-current-defun-function)
cvsdist b39f9d
  ;;
cvsdist b39f9d
  (set-syntax-table py-mode-syntax-table)
cvsdist b39f9d
  (setq major-mode              'python-mode
cvsdist b39f9d
	mode-name               "Python"
cvsdist b39f9d
	local-abbrev-table      python-mode-abbrev-table
cvsdist b39f9d
	font-lock-defaults      '(python-font-lock-keywords)
cvsdist b39f9d
	paragraph-separate      "^[ \t]*$"
cvsdist b39f9d
	paragraph-start         "^[ \t]*$"
cvsdist b39f9d
	require-final-newline   t
cvsdist b39f9d
	comment-start           "# "
cvsdist b39f9d
	comment-end             ""
cvsdist b39f9d
	comment-start-skip      "# *"
cvsdist b39f9d
	comment-column          40
cvsdist b39f9d
	comment-indent-function 'py-comment-indent-function
cvsdist b39f9d
	indent-region-function  'py-indent-region
cvsdist b39f9d
	indent-line-function    'py-indent-line
cvsdist b39f9d
	;; tell add-log.el how to find the current function/method/variable
cvsdist b39f9d
	add-log-current-defun-function 'py-current-defun
cvsdist b39f9d
	)
cvsdist b39f9d
  (use-local-map py-mode-map)
cvsdist b39f9d
  ;; add the menu
cvsdist b39f9d
  (if py-menu
cvsdist b39f9d
      (easy-menu-add py-menu))
cvsdist b39f9d
  ;; Emacs 19 requires this
cvsdist b39f9d
  (if (boundp 'comment-multi-line)
cvsdist b39f9d
      (setq comment-multi-line nil))
cvsdist b39f9d
  ;; Install Imenu if available
cvsdist b39f9d
  (when (py-safe (require 'imenu))
cvsdist b39f9d
    (setq imenu-create-index-function #'py-imenu-create-index-function)
cvsdist b39f9d
    (setq imenu-generic-expression py-imenu-generic-expression)
cvsdist b39f9d
    (if (fboundp 'imenu-add-to-menubar)
cvsdist b39f9d
	(imenu-add-to-menubar (format "%s-%s" "IM" mode-name)))
cvsdist b39f9d
    )
cvsdist b39f9d
  ;; Run the mode hook.  Note that py-mode-hook is deprecated.
cvsdist b39f9d
  (if python-mode-hook
cvsdist b39f9d
      (run-hooks 'python-mode-hook)
cvsdist b39f9d
    (run-hooks 'py-mode-hook))
cvsdist b39f9d
  ;; Now do the automagical guessing
cvsdist b39f9d
  (if py-smart-indentation
cvsdist b39f9d
    (let ((offset py-indent-offset))
cvsdist b39f9d
      ;; It's okay if this fails to guess a good value
cvsdist b39f9d
      (if (and (py-safe (py-guess-indent-offset))
cvsdist b39f9d
	       (<= py-indent-offset 8)
cvsdist b39f9d
	       (>= py-indent-offset 2))
cvsdist b39f9d
	  (setq offset py-indent-offset))
cvsdist b39f9d
      (setq py-indent-offset offset)
cvsdist b39f9d
      ;; Only turn indent-tabs-mode off if tab-width !=
cvsdist b39f9d
      ;; py-indent-offset.  Never turn it on, because the user must
cvsdist b39f9d
      ;; have explicitly turned it off.
cvsdist b39f9d
      (if (/= tab-width py-indent-offset)
cvsdist b39f9d
	  (setq indent-tabs-mode nil))
cvsdist b39f9d
      ))
cvsdist b39f9d
  ;; Set the default shell if not already set
cvsdist b39f9d
  (when (null py-which-shell)
cvsdist b39f9d
    (py-toggle-shells py-default-interpreter))
cvsdist b39f9d
  )
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; electric characters
cvsdist b39f9d
(defun py-outdent-p ()
cvsdist b39f9d
  "Returns non-nil if the current line should dedent one level."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (and (progn (back-to-indentation)
cvsdist b39f9d
		(looking-at py-outdent-re))
cvsdist b39f9d
	 ;; short circuit infloop on illegal construct
cvsdist b39f9d
	 (not (bobp))
cvsdist b39f9d
	 (progn (forward-line -1)
cvsdist b39f9d
		(py-goto-initial-line)
cvsdist b39f9d
		(back-to-indentation)
cvsdist b39f9d
		(while (or (looking-at py-blank-or-comment-re)
cvsdist b39f9d
			   (bobp))
cvsdist b39f9d
		  (backward-to-indentation 1))
cvsdist b39f9d
		(not (looking-at py-no-outdent-re)))
cvsdist b39f9d
	 )))
cvsdist b39f9d
      
cvsdist b39f9d
(defun py-electric-colon (arg)
cvsdist b39f9d
  "Insert a colon.
cvsdist b39f9d
In certain cases the line is dedented appropriately.  If a numeric
cvsdist b39f9d
argument ARG is provided, that many colons are inserted
cvsdist b39f9d
non-electrically.  Electric behavior is inhibited inside a string or
cvsdist b39f9d
comment."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  (self-insert-command (prefix-numeric-value arg))
cvsdist b39f9d
  ;; are we in a string or comment?
cvsdist b39f9d
  (if (save-excursion
cvsdist b39f9d
	(let ((pps (parse-partial-sexp (save-excursion
cvsdist b39f9d
					 (py-beginning-of-def-or-class)
cvsdist b39f9d
					 (point))
cvsdist b39f9d
				       (point))))
cvsdist b39f9d
	  (not (or (nth 3 pps) (nth 4 pps)))))
cvsdist b39f9d
      (save-excursion
cvsdist b39f9d
	(let ((here (point))
cvsdist b39f9d
	      (outdent 0)
cvsdist b39f9d
	      (indent (py-compute-indentation t)))
cvsdist b39f9d
	  (if (and (not arg)
cvsdist b39f9d
		   (py-outdent-p)
cvsdist b39f9d
		   (= indent (save-excursion
cvsdist b39f9d
			       (py-next-statement -1)
cvsdist b39f9d
			       (py-compute-indentation t)))
cvsdist b39f9d
		   )
cvsdist b39f9d
	      (setq outdent py-indent-offset))
cvsdist b39f9d
	  ;; Don't indent, only dedent.  This assumes that any lines
cvsdist b39f9d
	  ;; that are already dedented relative to
cvsdist b39f9d
	  ;; py-compute-indentation were put there on purpose.  It's
cvsdist b39f9d
	  ;; highly annoying to have `:' indent for you.  Use TAB, C-c
cvsdist b39f9d
	  ;; C-l or C-c C-r to adjust.  TBD: Is there a better way to
cvsdist b39f9d
	  ;; determine this???
cvsdist b39f9d
	  (if (< (current-indentation) indent) nil
cvsdist b39f9d
	    (goto-char here)
cvsdist b39f9d
	    (beginning-of-line)
cvsdist b39f9d
	    (delete-horizontal-space)
cvsdist b39f9d
	    (indent-to (- indent outdent))
cvsdist b39f9d
	    )))))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Python subprocess utilities and filters
cvsdist b39f9d
(defun py-execute-file (proc filename)
cvsdist b39f9d
  "Send to Python interpreter process PROC \"execfile('FILENAME')\".
cvsdist b39f9d
Make that process's buffer visible and force display.  Also make
cvsdist b39f9d
comint believe the user typed this string so that
cvsdist b39f9d
`kill-output-from-shell' does The Right Thing."
cvsdist b39f9d
  (let ((curbuf (current-buffer))
cvsdist b39f9d
	(procbuf (process-buffer proc))
cvsdist b39f9d
;	(comint-scroll-to-bottom-on-output t)
cvsdist b39f9d
	(msg (format "## working on region in file %s...\n" filename))
cvsdist b39f9d
	(cmd (format "execfile(r'%s')\n" filename)))
cvsdist b39f9d
    (unwind-protect
cvsdist b39f9d
	(save-excursion
cvsdist b39f9d
	  (set-buffer procbuf)
cvsdist b39f9d
	  (goto-char (point-max))
cvsdist b39f9d
	  (move-marker (process-mark proc) (point))
cvsdist b39f9d
	  (funcall (process-filter proc) proc msg))
cvsdist b39f9d
      (set-buffer curbuf))
cvsdist b39f9d
    (process-send-string proc cmd)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-comint-output-filter-function (string)
cvsdist b39f9d
  "Watch output for Python prompt and exec next file waiting in queue.
cvsdist b39f9d
This function is appropriate for `comint-output-filter-functions'."
cvsdist b39f9d
  ;; TBD: this should probably use split-string
cvsdist b39f9d
  (when (and (or (string-equal string ">>> ")
cvsdist b39f9d
		 (and (>= (length string) 5)
cvsdist b39f9d
		      (string-equal (substring string -5) "\n>>> ")))
cvsdist b39f9d
	     py-file-queue)
cvsdist b39f9d
    (py-safe (delete-file (car py-file-queue)))
cvsdist b39f9d
    (setq py-file-queue (cdr py-file-queue))
cvsdist b39f9d
    (if py-file-queue
cvsdist b39f9d
	(let ((pyproc (get-buffer-process (current-buffer))))
cvsdist b39f9d
	  (py-execute-file pyproc (car py-file-queue))))
cvsdist b39f9d
    ))
cvsdist b39f9d
cvsdist c08065
(defun py-pdbtrack-overlay-arrow (activation)
cvsdist c08065
  "Activate or de arrow at beginning-of-line in current buffer."
cvsdist c08065
  ;; This was derived/simplified from edebug-overlay-arrow
cvsdist c08065
  (cond (activation
cvsdist c08065
	 (setq overlay-arrow-position (make-marker))
cvsdist c08065
	 (setq overlay-arrow-string "=>")
cvsdist c08065
	 (set-marker overlay-arrow-position (py-point 'bol) (current-buffer))
cvsdist c08065
	 (setq py-pdbtrack-is-tracking-p t))
cvsdist 86923e
	(py-pdbtrack-is-tracking-p
cvsdist c08065
	 (setq overlay-arrow-position nil)
cvsdist c08065
	 (setq py-pdbtrack-is-tracking-p nil))
cvsdist c08065
	))
cvsdist c08065
cvsdist c08065
(defun py-pdbtrack-track-stack-file (text)
cvsdist c08065
  "Show the file indicated by the pdb stack entry line, in a separate window.
cvsdist c08065
cvsdist c08065
Activity is disabled if the buffer-local variable
cvsdist c08065
`py-pdbtrack-do-tracking-p' is nil.
cvsdist c08065
cvsdist c08065
We depend on the pdb input prompt matching `py-pdbtrack-input-prompt'
cvsdist c08065
at the beginning of the line."
cvsdist c08065
  ;; Instead of trying to piece things together from partial text
cvsdist c08065
  ;; (which can be almost useless depending on Emacs version), we
cvsdist c08065
  ;; monitor to the point where we have the next pdb prompt, and then
cvsdist c08065
  ;; check all text from comint-last-input-end to process-mark.
cvsdist c08065
  ;;
cvsdist c08065
  ;; KLM: It might be nice to provide an optional override, so this
cvsdist c08065
  ;; routine could be fed debugger output strings as the text
cvsdist c08065
  ;; argument, for deliberate application elsewhere.
cvsdist c08065
  ;;
cvsdist c08065
  ;; KLM: We're very conservative about clearing the overlay arrow, to
cvsdist c08065
  ;; minimize residue.  This means, for instance, that executing other
cvsdist c08065
  ;; pdb commands wipes out the highlight.
cvsdist c08065
  (let* ((origbuf (current-buffer))
cvsdist c08065
	 (currproc (get-buffer-process origbuf)))
cvsdist c08065
    (if (not (and currproc py-pdbtrack-do-tracking-p))
cvsdist c08065
        (py-pdbtrack-overlay-arrow nil)
cvsdist c08065
      (let* (;(origdir default-directory)
cvsdist c08065
             (procmark (process-mark currproc))
cvsdist c08065
             (block (buffer-substring (max comint-last-input-end
cvsdist c08065
                                           (- procmark
cvsdist c08065
                                              py-pdbtrack-track-range))
cvsdist c08065
                                      procmark))
cvsdist c08065
             fname lineno)
cvsdist c08065
        (if (not (string-match (concat py-pdbtrack-input-prompt "$") block))
cvsdist c08065
            (py-pdbtrack-overlay-arrow nil)
cvsdist c08065
          (if (not (string-match
cvsdist c08065
                    (concat ".*" py-pdbtrack-stack-entry-regexp ".*")
cvsdist c08065
                    block))
cvsdist c08065
              (py-pdbtrack-overlay-arrow nil)
cvsdist c08065
            (setq fname (match-string 1 block)
cvsdist c08065
                  lineno (match-string 2 block))
cvsdist c08065
            (if (file-exists-p fname)
cvsdist c08065
                (progn
cvsdist c08065
                  (find-file-other-window fname)
cvsdist c08065
                  (goto-line (string-to-int lineno))
cvsdist c08065
                  (message "pdbtrack: line %s, file %s" lineno fname)
cvsdist c08065
                  (py-pdbtrack-overlay-arrow t)
cvsdist c08065
                  (pop-to-buffer origbuf t) )
cvsdist c08065
              (if (= (elt fname 0) ?\<)
cvsdist c08065
                  (message "pdbtrack: (Non-file source: '%s')" fname)
cvsdist c08065
                (message "pdbtrack: File not found: %s" fname))
cvsdist c08065
              )))))))
cvsdist c08065
cvsdist b39f9d
(defun py-postprocess-output-buffer (buf)
cvsdist b39f9d
  "Highlight exceptions found in BUF.
cvsdist b39f9d
If an exception occurred return t, otherwise return nil.  BUF must exist."
cvsdist b39f9d
  (let (line file bol err-p)
cvsdist b39f9d
    (save-excursion
cvsdist b39f9d
      (set-buffer buf)
cvsdist b39f9d
      (beginning-of-buffer)
cvsdist b39f9d
      (while (re-search-forward py-traceback-line-re nil t)
cvsdist b39f9d
	(setq file (match-string 1)
cvsdist b39f9d
	      line (string-to-int (match-string 2))
cvsdist b39f9d
	      bol (py-point 'bol))
cvsdist b39f9d
	(py-highlight-line bol (py-point 'eol) file line)))
cvsdist b39f9d
    (when (and py-jump-on-exception line)
cvsdist b39f9d
      (beep)
cvsdist b39f9d
      (py-jump-to-exception file line)
cvsdist b39f9d
      (setq err-p t))
cvsdist b39f9d
    err-p))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;;; Subprocess commands
cvsdist b39f9d
cvsdist b39f9d
;; only used when (memq 'broken-temp-names py-emacs-features)
cvsdist b39f9d
(defvar py-serial-number 0)
cvsdist b39f9d
(defvar py-exception-buffer nil)
cvsdist b39f9d
(defconst py-output-buffer "*Python Output*")
cvsdist b39f9d
(make-variable-buffer-local 'py-output-buffer)
cvsdist b39f9d
cvsdist b39f9d
;; for toggling between CPython and JPython
cvsdist b39f9d
(defvar py-which-shell nil)
cvsdist b39f9d
(defvar py-which-args  py-python-command-args)
cvsdist b39f9d
(defvar py-which-bufname "Python")
cvsdist b39f9d
(make-variable-buffer-local 'py-which-shell)
cvsdist b39f9d
(make-variable-buffer-local 'py-which-args)
cvsdist b39f9d
(make-variable-buffer-local 'py-which-bufname)
cvsdist b39f9d
cvsdist b39f9d
(defun py-toggle-shells (arg)
cvsdist b39f9d
  "Toggles between the CPython and JPython shells.
cvsdist b39f9d
cvsdist b39f9d
With positive argument ARG (interactively \\[universal-argument]),
cvsdist b39f9d
uses the CPython shell, with negative ARG uses the JPython shell, and
cvsdist b39f9d
with a zero argument, toggles the shell.
cvsdist b39f9d
cvsdist b39f9d
Programmatically, ARG can also be one of the symbols `cpython' or
cvsdist b39f9d
`jpython', equivalent to positive arg and negative arg respectively."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  ;; default is to toggle
cvsdist b39f9d
  (if (null arg)
cvsdist b39f9d
      (setq arg 0))
cvsdist b39f9d
  ;; preprocess arg
cvsdist b39f9d
  (cond
cvsdist b39f9d
   ((equal arg 0)
cvsdist b39f9d
    ;; toggle
cvsdist b39f9d
    (if (string-equal py-which-bufname "Python")
cvsdist b39f9d
	(setq arg -1)
cvsdist b39f9d
      (setq arg 1)))
cvsdist b39f9d
   ((equal arg 'cpython) (setq arg 1))
cvsdist b39f9d
   ((equal arg 'jpython) (setq arg -1)))
cvsdist b39f9d
  (let (msg)
cvsdist b39f9d
    (cond
cvsdist b39f9d
     ((< 0 arg)
cvsdist b39f9d
      ;; set to CPython
cvsdist b39f9d
      (setq py-which-shell py-python-command
cvsdist b39f9d
	    py-which-args py-python-command-args
cvsdist b39f9d
	    py-which-bufname "Python"
cvsdist b39f9d
	    msg "CPython"
cvsdist b39f9d
	    mode-name "Python"))
cvsdist b39f9d
     ((> 0 arg)
cvsdist b39f9d
      (setq py-which-shell py-jpython-command
cvsdist b39f9d
	    py-which-args py-jpython-command-args
cvsdist b39f9d
	    py-which-bufname "JPython"
cvsdist b39f9d
	    msg "JPython"
cvsdist b39f9d
	    mode-name "JPython"))
cvsdist b39f9d
     )
cvsdist b39f9d
    (message "Using the %s shell" msg)
cvsdist b39f9d
    (setq py-output-buffer (format "*%s Output*" py-which-bufname))))
cvsdist b39f9d
cvsdist b39f9d
;;;###autoload
cvsdist b39f9d
(defun py-shell (&optional argprompt)
cvsdist b39f9d
  "Start an interactive Python interpreter in another window.
cvsdist b39f9d
This is like Shell mode, except that Python is running in the window
cvsdist b39f9d
instead of a shell.  See the `Interactive Shell' and `Shell Mode'
cvsdist b39f9d
sections of the Emacs manual for details, especially for the key
cvsdist b39f9d
bindings active in the `*Python*' buffer.
cvsdist b39f9d
cvsdist b39f9d
With optional \\[universal-argument], the user is prompted for the
cvsdist b39f9d
flags to pass to the Python interpreter.  This has no effect when this
cvsdist b39f9d
command is used to switch to an existing process, only when a new
cvsdist b39f9d
process is started.  If you use this, you will probably want to ensure
cvsdist b39f9d
that the current arguments are retained (they will be included in the
cvsdist b39f9d
prompt).  This argument is ignored when this function is called
cvsdist b39f9d
programmatically, or when running in Emacs 19.34 or older.
cvsdist b39f9d
cvsdist b39f9d
Note: You can toggle between using the CPython interpreter and the
cvsdist b39f9d
JPython interpreter by hitting \\[py-toggle-shells].  This toggles
cvsdist b39f9d
buffer local variables which control whether all your subshell
cvsdist b39f9d
interactions happen to the `*JPython*' or `*Python*' buffers (the
cvsdist b39f9d
latter is the name used for the CPython buffer).
cvsdist b39f9d
cvsdist b39f9d
Warning: Don't use an interactive Python if you change sys.ps1 or
cvsdist b39f9d
sys.ps2 from their default values, or if you're running code that
cvsdist b39f9d
prints `>>> ' or `... ' at the start of a line.  `python-mode' can't
cvsdist b39f9d
distinguish your output from Python's output, and assumes that `>>> '
cvsdist b39f9d
at the start of a line is a prompt from Python.  Similarly, the Emacs
cvsdist b39f9d
Shell mode code assumes that both `>>> ' and `... ' at the start of a
cvsdist b39f9d
line are Python prompts.  Bad things can happen if you fool either
cvsdist b39f9d
mode.
cvsdist b39f9d
cvsdist b39f9d
Warning:  If you do any editing *in* the process buffer *while* the
cvsdist b39f9d
buffer is accepting output from Python, do NOT attempt to `undo' the
cvsdist b39f9d
changes.  Some of the output (nowhere near the parts you changed!) may
cvsdist b39f9d
be lost if you do.  This appears to be an Emacs bug, an unfortunate
cvsdist b39f9d
interaction between undo and process filters; the same problem exists in
cvsdist b39f9d
non-Python process buffers using the default (Emacs-supplied) process
cvsdist b39f9d
filter."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  ;; Set the default shell if not already set
cvsdist b39f9d
  (when (null py-which-shell)
cvsdist b39f9d
    (py-toggle-shells py-default-interpreter))
cvsdist b39f9d
  (let ((args py-which-args))
cvsdist b39f9d
    (when (and argprompt
cvsdist b39f9d
	       (interactive-p)
cvsdist b39f9d
	       (fboundp 'split-string))
cvsdist b39f9d
      ;; TBD: Perhaps force "-i" in the final list?
cvsdist b39f9d
      (setq args (split-string
cvsdist b39f9d
		  (read-string (concat py-which-bufname
cvsdist b39f9d
				       " arguments: ")
cvsdist b39f9d
			       (concat
cvsdist b39f9d
				(mapconcat 'identity py-which-args " ") " ")
cvsdist b39f9d
			       ))))
cvsdist b39f9d
    (switch-to-buffer-other-window
cvsdist b39f9d
     (apply 'make-comint py-which-bufname py-which-shell nil args))
cvsdist b39f9d
    (make-local-variable 'comint-prompt-regexp)
cvsdist b39f9d
    (setq comint-prompt-regexp "^>>> \\|^[.][.][.] \\|^(pdb) ")
cvsdist b39f9d
    (add-hook 'comint-output-filter-functions
cvsdist b39f9d
	      'py-comint-output-filter-function)
cvsdist c08065
    ;; pdbtrack
cvsdist c08065
    (add-hook 'comint-output-filter-functions 'py-pdbtrack-track-stack-file)
cvsdist c08065
    (setq py-pdbtrack-do-tracking-p t)
cvsdist b39f9d
    (set-syntax-table py-mode-syntax-table)
cvsdist b39f9d
    (use-local-map py-shell-map)
cvsdist b39f9d
    ))
cvsdist b39f9d
cvsdist b39f9d
(defun py-clear-queue ()
cvsdist b39f9d
  "Clear the queue of temporary files waiting to execute."
cvsdist b39f9d
  (interactive)
cvsdist b39f9d
  (let ((n (length py-file-queue)))
cvsdist b39f9d
    (mapcar 'delete-file py-file-queue)
cvsdist b39f9d
    (setq py-file-queue nil)
cvsdist b39f9d
    (message "%d pending files de-queued." n)))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
(defun py-execute-region (start end &optional async)
cvsdist b39f9d
  "Execute the region in a Python interpreter.
cvsdist b39f9d
cvsdist b39f9d
The region is first copied into a temporary file (in the directory
cvsdist b39f9d
`py-temp-directory').  If there is no Python interpreter shell
cvsdist b39f9d
running, this file is executed synchronously using
cvsdist b39f9d
`shell-command-on-region'.  If the program is long running, use
cvsdist b39f9d
\\[universal-argument] to run the command asynchronously in its own
cvsdist b39f9d
buffer.
cvsdist b39f9d
cvsdist b39f9d
When this function is used programmatically, arguments START and END
cvsdist b39f9d
specify the region to execute, and optional third argument ASYNC, if
cvsdist b39f9d
non-nil, specifies to run the command asynchronously in its own
cvsdist b39f9d
buffer.
cvsdist b39f9d
cvsdist b39f9d
If the Python interpreter shell is running, the region is execfile()'d
cvsdist b39f9d
in that shell.  If you try to execute regions too quickly,
cvsdist b39f9d
`python-mode' will queue them up and execute them one at a time when
cvsdist b39f9d
it sees a `>>> ' prompt from Python.  Each time this happens, the
cvsdist b39f9d
process buffer is popped into a window (if it's not already in some
cvsdist b39f9d
window) so you can see it, and a comment of the form
cvsdist b39f9d
cvsdist b39f9d
    \t## working on region in file <name>...
cvsdist b39f9d
cvsdist b39f9d
is inserted at the end.  See also the command `py-clear-queue'."
cvsdist b39f9d
  (interactive "r\nP")
cvsdist b39f9d
  (or (< start end)
cvsdist b39f9d
      (error "Region is empty"))
cvsdist b39f9d
  (let* ((proc (get-process py-which-bufname))
cvsdist b39f9d
	 (temp (if (memq 'broken-temp-names py-emacs-features)
cvsdist b39f9d
		   (let
cvsdist b39f9d
		       ((sn py-serial-number)
cvsdist b39f9d
			(pid (and (fboundp 'emacs-pid) (emacs-pid))))
cvsdist b39f9d
		     (setq py-serial-number (1+ py-serial-number))
cvsdist b39f9d
		     (if pid
cvsdist b39f9d
			 (format "python-%d-%d" sn pid)
cvsdist b39f9d
		       (format "python-%d" sn)))
cvsdist b39f9d
		 (make-temp-name "python-")))
cvsdist c08065
	 (file (expand-file-name temp py-temp-directory))
cvsdist c08065
	 (cur (current-buffer))
cvsdist c08065
	 (buf (get-buffer-create file)))
cvsdist c08065
    ;; Write the contents of the buffer, watching out for indented regions.
cvsdist c08065
    (save-excursion
cvsdist c08065
      (goto-char start)
cvsdist c08065
      (let ((needs-if (/= (py-point 'bol) (py-point 'boi))))
cvsdist c08065
	(set-buffer buf)
cvsdist c08065
	(when needs-if
cvsdist c08065
	  (insert "if 1:\n"))
cvsdist c08065
	(insert-buffer-substring cur start end)))
cvsdist b39f9d
    (cond
cvsdist b39f9d
     ;; always run the code in its own asynchronous subprocess
cvsdist b39f9d
     (async
cvsdist c08065
      ;; User explicitly wants this to run in its own async subprocess
cvsdist c08065
      (save-excursion
cvsdist c08065
	(set-buffer buf)
cvsdist c08065
	(write-region (point-min) (point-max) file nil 'nomsg))
cvsdist b39f9d
      (let* ((buf (generate-new-buffer-name py-output-buffer))
cvsdist b39f9d
	     ;; TBD: a horrible hack, but why create new Custom variables?
cvsdist b39f9d
	     (arg (if (string-equal py-which-bufname "Python")
cvsdist b39f9d
		      "-u" "")))
cvsdist b39f9d
	(start-process py-which-bufname buf py-which-shell arg file)
cvsdist b39f9d
	(pop-to-buffer buf)
cvsdist b39f9d
	(py-postprocess-output-buffer buf)
cvsdist c08065
	;; TBD: clean up the temporary file!
cvsdist b39f9d
	))
cvsdist b39f9d
     ;; if the Python interpreter shell is running, queue it up for
cvsdist b39f9d
     ;; execution there.
cvsdist b39f9d
     (proc
cvsdist b39f9d
      ;; use the existing python shell
cvsdist c08065
      (save-excursion
cvsdist c08065
	(set-buffer buf)
cvsdist c08065
	(write-region (point-min) (point-max) file nil 'nomsg))
cvsdist b39f9d
      (if (not py-file-queue)
cvsdist b39f9d
	  (py-execute-file proc file)
cvsdist b39f9d
	(message "File %s queued for execution" file))
cvsdist b39f9d
      (setq py-file-queue (append py-file-queue (list file)))
cvsdist b39f9d
      (setq py-exception-buffer (cons file (current-buffer))))
cvsdist b39f9d
     (t
cvsdist b39f9d
      ;; TBD: a horrible hack, buy why create new Custom variables?
cvsdist b39f9d
      (let ((cmd (concat py-which-shell
cvsdist b39f9d
			 (if (string-equal py-which-bufname "JPython")
cvsdist b39f9d
			     " -" ""))))
cvsdist b39f9d
	;; otherwise either run it synchronously in a subprocess
cvsdist c08065
	(save-excursion
cvsdist c08065
	  (set-buffer buf)
cvsdist c08065
	  (shell-command-on-region (point-min) (point-max)
cvsdist c08065
				   cmd py-output-buffer))
cvsdist b39f9d
	;; shell-command-on-region kills the output buffer if it never
cvsdist b39f9d
	;; existed and there's no output from the command
cvsdist b39f9d
	(if (not (get-buffer py-output-buffer))
cvsdist b39f9d
	    (message "No output.")
cvsdist b39f9d
	  (setq py-exception-buffer (current-buffer))
cvsdist b39f9d
	  (let ((err-p (py-postprocess-output-buffer py-output-buffer)))
cvsdist b39f9d
	    (pop-to-buffer py-output-buffer)
cvsdist b39f9d
	    (if err-p
cvsdist b39f9d
		(pop-to-buffer py-exception-buffer)))
cvsdist c08065
	  ))
cvsdist c08065
      ;; TBD: delete the buffer
cvsdist c08065
      )
cvsdist c08065
     )
cvsdist c08065
    ;; Clean up after ourselves.
cvsdist c08065
    (kill-buffer buf)))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Code execution commands
cvsdist b39f9d
(defun py-execute-buffer (&optional async)
cvsdist b39f9d
  "Send the contents of the buffer to a Python interpreter.
cvsdist b39f9d
If the file local variable `py-master-file' is non-nil, execute the
cvsdist b39f9d
named file instead of the buffer's file.
cvsdist b39f9d
cvsdist b39f9d
If there is a *Python* process buffer it is used.  If a clipping
cvsdist b39f9d
restriction is in effect, only the accessible portion of the buffer is
cvsdist b39f9d
sent.  A trailing newline will be supplied if needed.
cvsdist b39f9d
cvsdist b39f9d
See the `\\[py-execute-region]' docs for an account of some
cvsdist b39f9d
subtleties, including the use of the optional ASYNC argument."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  (if py-master-file
cvsdist b39f9d
      (let* ((filename (expand-file-name py-master-file))
cvsdist b39f9d
	     (buffer (or (get-file-buffer filename)
cvsdist b39f9d
			 (find-file-noselect filename))))
cvsdist b39f9d
	(set-buffer buffer)))
cvsdist b39f9d
  (py-execute-region (point-min) (point-max) async))
cvsdist b39f9d
cvsdist b39f9d
(defun py-execute-import-or-reload (&optional async)
cvsdist b39f9d
  "Import the current buffer's file in a Python interpreter.
cvsdist b39f9d
cvsdist b39f9d
If the file has already been imported, then do reload instead to get
cvsdist b39f9d
the latest version.
cvsdist b39f9d
cvsdist b39f9d
If the file's name does not end in \".py\", then do execfile instead.
cvsdist b39f9d
cvsdist b39f9d
If the current buffer is not visiting a file, do `py-execute-buffer'
cvsdist b39f9d
instead.
cvsdist b39f9d
cvsdist b39f9d
If the file local variable `py-master-file' is non-nil, import or
cvsdist b39f9d
reload the named file instead of the buffer's file.  The file may be
cvsdist b39f9d
saved based on the value of `py-execute-import-or-reload-save-p'.
cvsdist b39f9d
cvsdist b39f9d
See the `\\[py-execute-region]' docs for an account of some
cvsdist b39f9d
subtleties, including the use of the optional ASYNC argument.
cvsdist b39f9d
cvsdist b39f9d
This may be preferable to `\\[py-execute-buffer]' because:
cvsdist b39f9d
cvsdist b39f9d
 - Definitions stay in their module rather than appearing at top
cvsdist b39f9d
   level, where they would clutter the global namespace and not affect
cvsdist b39f9d
   uses of qualified names (MODULE.NAME).
cvsdist b39f9d
cvsdist b39f9d
 - The Python debugger gets line number information about the functions."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  ;; Check file local variable py-master-file
cvsdist b39f9d
  (if py-master-file
cvsdist b39f9d
      (let* ((filename (expand-file-name py-master-file))
cvsdist b39f9d
             (buffer (or (get-file-buffer filename)
cvsdist b39f9d
                         (find-file-noselect filename))))
cvsdist b39f9d
        (set-buffer buffer)))
cvsdist b39f9d
  (let ((file (buffer-file-name (current-buffer))))
cvsdist b39f9d
    (if file
cvsdist b39f9d
        (progn
cvsdist b39f9d
	  ;; Maybe save some buffers
cvsdist b39f9d
	  (save-some-buffers (not py-ask-about-save) nil)
cvsdist b39f9d
          (py-execute-string
cvsdist b39f9d
           (if (string-match "\\.py$" file)
cvsdist b39f9d
               (let ((f (file-name-sans-extension
cvsdist b39f9d
			 (file-name-nondirectory file))))
cvsdist b39f9d
                 (format "if globals().has_key('%s'):\n    reload(%s)\nelse:\n    import %s\n"
cvsdist b39f9d
                         f f f))
cvsdist b39f9d
             (format "execfile(r'%s')\n" file))
cvsdist b39f9d
           async))
cvsdist b39f9d
      ;; else
cvsdist b39f9d
      (py-execute-buffer async))))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d
(defun py-execute-def-or-class (&optional async)
cvsdist b39f9d
  "Send the current function or class definition to a Python interpreter.
cvsdist b39f9d
cvsdist b39f9d
If there is a *Python* process buffer it is used.
cvsdist b39f9d
cvsdist b39f9d
See the `\\[py-execute-region]' docs for an account of some
cvsdist b39f9d
subtleties, including the use of the optional ASYNC argument."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (py-mark-def-or-class)
cvsdist b39f9d
    ;; mark is before point
cvsdist b39f9d
    (py-execute-region (mark) (point) async)))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d
(defun py-execute-string (string &optional async)
cvsdist b39f9d
  "Send the argument STRING to a Python interpreter.
cvsdist b39f9d
cvsdist b39f9d
If there is a *Python* process buffer it is used.
cvsdist b39f9d
cvsdist b39f9d
See the `\\[py-execute-region]' docs for an account of some
cvsdist b39f9d
subtleties, including the use of the optional ASYNC argument."
cvsdist b39f9d
  (interactive "sExecute Python command: ")
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (set-buffer (get-buffer-create
cvsdist b39f9d
                 (generate-new-buffer-name " *Python Command*")))
cvsdist b39f9d
    (insert string)
cvsdist b39f9d
    (py-execute-region (point-min) (point-max) async)))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
(defun py-jump-to-exception (file line)
cvsdist b39f9d
  "Jump to the Python code in FILE at LINE."
cvsdist b39f9d
  (let ((buffer (cond ((string-equal file "<stdin>")
cvsdist b39f9d
		       (if (consp py-exception-buffer)
cvsdist b39f9d
			   (cdr py-exception-buffer)
cvsdist b39f9d
			 py-exception-buffer))
cvsdist b39f9d
		      ((and (consp py-exception-buffer)
cvsdist b39f9d
			    (string-equal file (car py-exception-buffer)))
cvsdist b39f9d
		       (cdr py-exception-buffer))
cvsdist b39f9d
		      ((py-safe (find-file-noselect file)))
cvsdist b39f9d
		      ;; could not figure out what file the exception
cvsdist b39f9d
		      ;; is pointing to, so prompt for it
cvsdist b39f9d
		      (t (find-file (read-file-name "Exception file: "
cvsdist b39f9d
						    nil
cvsdist b39f9d
						    file t))))))
cvsdist b39f9d
    (pop-to-buffer buffer)
cvsdist b39f9d
    ;; Force Python mode
cvsdist b39f9d
    (if (not (eq major-mode 'python-mode))
cvsdist b39f9d
	(python-mode))
cvsdist b39f9d
    (goto-line line)
cvsdist b39f9d
    (message "Jumping to exception in file %s on line %d" file line)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-mouseto-exception (event)
cvsdist b39f9d
  "Jump to the code which caused the Python exception at EVENT.
cvsdist b39f9d
EVENT is usually a mouse click."
cvsdist b39f9d
  (interactive "e")
cvsdist b39f9d
  (cond
cvsdist b39f9d
   ((fboundp 'event-point)
cvsdist b39f9d
    ;; XEmacs
cvsdist b39f9d
    (let* ((point (event-point event))
cvsdist b39f9d
	   (buffer (event-buffer event))
cvsdist b39f9d
	   (e (and point buffer (extent-at point buffer 'py-exc-info)))
cvsdist b39f9d
	   (info (and e (extent-property e 'py-exc-info))))
cvsdist b39f9d
      (message "Event point: %d, info: %s" point info)
cvsdist b39f9d
      (and info
cvsdist b39f9d
	   (py-jump-to-exception (car info) (cdr info)))
cvsdist b39f9d
      ))
cvsdist b39f9d
   ;; Emacs -- Please port this!
cvsdist b39f9d
   ))
cvsdist b39f9d
cvsdist b39f9d
(defun py-goto-exception ()
cvsdist b39f9d
  "Go to the line indicated by the traceback."
cvsdist b39f9d
  (interactive)
cvsdist b39f9d
  (let (file line)
cvsdist b39f9d
    (save-excursion
cvsdist b39f9d
      (beginning-of-line)
cvsdist b39f9d
      (if (looking-at py-traceback-line-re)
cvsdist b39f9d
	  (setq file (match-string 1)
cvsdist b39f9d
		line (string-to-int (match-string 2)))))
cvsdist b39f9d
    (if (not file)
cvsdist b39f9d
	(error "Not on a traceback line"))
cvsdist b39f9d
    (py-jump-to-exception file line)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-find-next-exception (start buffer searchdir errwhere)
cvsdist b39f9d
  "Find the next Python exception and jump to the code that caused it.
cvsdist b39f9d
START is the buffer position in BUFFER from which to begin searching
cvsdist b39f9d
for an exception.  SEARCHDIR is a function, either
cvsdist b39f9d
`re-search-backward' or `re-search-forward' indicating the direction
cvsdist b39f9d
to search.  ERRWHERE is used in an error message if the limit (top or
cvsdist b39f9d
bottom) of the trackback stack is encountered."
cvsdist b39f9d
  (let (file line)
cvsdist b39f9d
    (save-excursion
cvsdist b39f9d
      (set-buffer buffer)
cvsdist b39f9d
      (goto-char (py-point start))
cvsdist b39f9d
      (if (funcall searchdir py-traceback-line-re nil t)
cvsdist b39f9d
	  (setq file (match-string 1)
cvsdist b39f9d
		line (string-to-int (match-string 2)))))
cvsdist b39f9d
    (if (and file line)
cvsdist b39f9d
	(py-jump-to-exception file line)
cvsdist b39f9d
      (error "%s of traceback" errwhere))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-down-exception (&optional bottom)
cvsdist b39f9d
  "Go to the next line down in the traceback.
cvsdist b39f9d
With \\[univeral-argument] (programmatically, optional argument
cvsdist b39f9d
BOTTOM), jump to the bottom (innermost) exception in the exception
cvsdist b39f9d
stack."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  (let* ((proc (get-process "Python"))
cvsdist b39f9d
	 (buffer (if proc "*Python*" py-output-buffer)))
cvsdist b39f9d
    (if bottom
cvsdist b39f9d
	(py-find-next-exception 'eob buffer 're-search-backward "Bottom")
cvsdist b39f9d
      (py-find-next-exception 'eol buffer 're-search-forward "Bottom"))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-up-exception (&optional top)
cvsdist b39f9d
  "Go to the previous line up in the traceback.
cvsdist b39f9d
With \\[universal-argument] (programmatically, optional argument TOP)
cvsdist b39f9d
jump to the top (outermost) exception in the exception stack."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  (let* ((proc (get-process "Python"))
cvsdist b39f9d
	 (buffer (if proc "*Python*" py-output-buffer)))
cvsdist b39f9d
    (if top
cvsdist b39f9d
	(py-find-next-exception 'bob buffer 're-search-forward "Top")
cvsdist b39f9d
      (py-find-next-exception 'bol buffer 're-search-backward "Top"))))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Electric deletion
cvsdist b39f9d
(defun py-electric-backspace (arg)
cvsdist b39f9d
  "Delete preceding character or levels of indentation.
cvsdist b39f9d
Deletion is performed by calling the function in `py-backspace-function'
cvsdist b39f9d
with a single argument (the number of characters to delete).
cvsdist b39f9d
cvsdist b39f9d
If point is at the leftmost column, delete the preceding newline.
cvsdist b39f9d
cvsdist b39f9d
Otherwise, if point is at the leftmost non-whitespace character of a
cvsdist b39f9d
line that is neither a continuation line nor a non-indenting comment
cvsdist b39f9d
line, or if point is at the end of a blank line, this command reduces
cvsdist b39f9d
the indentation to match that of the line that opened the current
cvsdist b39f9d
block of code.  The line that opened the block is displayed in the
cvsdist b39f9d
echo area to help you keep track of where you are.  With
cvsdist b39f9d
\\[universal-argument] dedents that many blocks (but not past column
cvsdist b39f9d
zero).
cvsdist b39f9d
cvsdist b39f9d
Otherwise the preceding character is deleted, converting a tab to
cvsdist b39f9d
spaces if needed so that only a single column position is deleted.
cvsdist b39f9d
\\[universal-argument] specifies how many characters to delete;
cvsdist b39f9d
default is 1.
cvsdist b39f9d
cvsdist b39f9d
When used programmatically, argument ARG specifies the number of
cvsdist b39f9d
blocks to dedent, or the number of characters to delete, as indicated
cvsdist b39f9d
above."
cvsdist b39f9d
  (interactive "*p")
cvsdist b39f9d
  (if (or (/= (current-indentation) (current-column))
cvsdist b39f9d
	  (bolp)
cvsdist b39f9d
	  (py-continuation-line-p)
cvsdist b39f9d
;	  (not py-honor-comment-indentation)
cvsdist b39f9d
;	  (looking-at "#[^ \t\n]")	; non-indenting #
cvsdist b39f9d
	  )
cvsdist b39f9d
      (funcall py-backspace-function arg)
cvsdist b39f9d
    ;; else indent the same as the colon line that opened the block
cvsdist b39f9d
    ;; force non-blank so py-goto-block-up doesn't ignore it
cvsdist b39f9d
    (insert-char ?* 1)
cvsdist b39f9d
    (backward-char)
cvsdist b39f9d
    (let ((base-indent 0)		; indentation of base line
cvsdist b39f9d
	  (base-text "")		; and text of base line
cvsdist b39f9d
	  (base-found-p nil))
cvsdist b39f9d
      (save-excursion
cvsdist b39f9d
	(while (< 0 arg)
cvsdist b39f9d
	  (condition-case nil		; in case no enclosing block
cvsdist b39f9d
	      (progn
cvsdist b39f9d
		(py-goto-block-up 'no-mark)
cvsdist b39f9d
		(setq base-indent (current-indentation)
cvsdist b39f9d
		      base-text   (py-suck-up-leading-text)
cvsdist b39f9d
		      base-found-p t))
cvsdist b39f9d
	    (error nil))
cvsdist b39f9d
	  (setq arg (1- arg))))
cvsdist b39f9d
      (delete-char 1)			; toss the dummy character
cvsdist b39f9d
      (delete-horizontal-space)
cvsdist b39f9d
      (indent-to base-indent)
cvsdist b39f9d
      (if base-found-p
cvsdist b39f9d
	  (message "Closes block: %s" base-text)))))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d
(defun py-electric-delete (arg)
cvsdist b39f9d
  "Delete preceding or following character or levels of whitespace.
cvsdist b39f9d
cvsdist b39f9d
The behavior of this function depends on the variable
cvsdist b39f9d
`delete-key-deletes-forward'.  If this variable is nil (or does not
cvsdist b39f9d
exist, as in older Emacsen and non-XEmacs versions), then this
cvsdist b39f9d
function behaves identically to \\[c-electric-backspace].
cvsdist b39f9d
cvsdist b39f9d
If `delete-key-deletes-forward' is non-nil and is supported in your
cvsdist b39f9d
Emacs, then deletion occurs in the forward direction, by calling the
cvsdist b39f9d
function in `py-delete-function'.
cvsdist b39f9d
cvsdist b39f9d
\\[universal-argument] (programmatically, argument ARG) specifies the
cvsdist b39f9d
number of characters to delete (default is 1)."
cvsdist b39f9d
  (interactive "*p")
cvsdist b39f9d
  (if (or (and (fboundp 'delete-forward-p) ;XEmacs 21
cvsdist b39f9d
	       (delete-forward-p))
cvsdist b39f9d
	  (and (boundp 'delete-key-deletes-forward) ;XEmacs 20
cvsdist b39f9d
	       delete-key-deletes-forward))
cvsdist b39f9d
      (funcall py-delete-function arg)
cvsdist b39f9d
    (py-electric-backspace arg)))
cvsdist b39f9d
cvsdist b39f9d
;; required for pending-del and delsel modes
cvsdist b39f9d
(put 'py-electric-backspace 'delete-selection 'supersede) ;delsel
cvsdist b39f9d
(put 'py-electric-backspace 'pending-delete   'supersede) ;pending-del
cvsdist b39f9d
(put 'py-electric-delete    'delete-selection 'supersede) ;delsel
cvsdist b39f9d
(put 'py-electric-delete    'pending-delete   'supersede) ;pending-del
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
(defun py-indent-line (&optional arg)
cvsdist b39f9d
  "Fix the indentation of the current line according to Python rules.
cvsdist b39f9d
With \\[universal-argument] (programmatically, the optional argument
cvsdist b39f9d
ARG non-nil), ignore dedenting rules for block closing statements
cvsdist b39f9d
(e.g. return, raise, break, continue, pass)
cvsdist b39f9d
cvsdist b39f9d
This function is normally bound to `indent-line-function' so
cvsdist b39f9d
\\[indent-for-tab-command] will call it."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  (let* ((ci (current-indentation))
cvsdist b39f9d
	 (move-to-indentation-p (<= (current-column) ci))
cvsdist b39f9d
	 (need (py-compute-indentation (not arg))))
cvsdist b39f9d
    ;; see if we need to dedent
cvsdist b39f9d
    (if (py-outdent-p)
cvsdist b39f9d
	(setq need (- need py-indent-offset)))
cvsdist b39f9d
    (if (/= ci need)
cvsdist b39f9d
	(save-excursion
cvsdist b39f9d
	  (beginning-of-line)
cvsdist b39f9d
	  (delete-horizontal-space)
cvsdist b39f9d
	  (indent-to need)))
cvsdist b39f9d
    (if move-to-indentation-p (back-to-indentation))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-newline-and-indent ()
cvsdist b39f9d
  "Strives to act like the Emacs `newline-and-indent'.
cvsdist b39f9d
This is just `strives to' because correct indentation can't be computed
cvsdist b39f9d
from scratch for Python code.  In general, deletes the whitespace before
cvsdist b39f9d
point, inserts a newline, and takes an educated guess as to how you want
cvsdist b39f9d
the new line indented."
cvsdist b39f9d
  (interactive)
cvsdist b39f9d
  (let ((ci (current-indentation)))
cvsdist b39f9d
    (if (< ci (current-column))		; if point beyond indentation
cvsdist b39f9d
	(newline-and-indent)
cvsdist b39f9d
      ;; else try to act like newline-and-indent "normally" acts
cvsdist b39f9d
      (beginning-of-line)
cvsdist b39f9d
      (insert-char ?\n 1)
cvsdist b39f9d
      (move-to-column ci))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-compute-indentation (honor-block-close-p)
cvsdist b39f9d
  "Compute Python indentation.
cvsdist b39f9d
When HONOR-BLOCK-CLOSE-P is non-nil, statements such as `return',
cvsdist b39f9d
`raise', `break', `continue', and `pass' force one level of
cvsdist b39f9d
dedenting."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (beginning-of-line)
cvsdist b39f9d
    (let* ((bod (py-point 'bod))
cvsdist b39f9d
	   (pps (parse-partial-sexp bod (point)))
cvsdist b39f9d
	   (boipps (parse-partial-sexp bod (py-point 'boi)))
cvsdist b39f9d
	   placeholder)
cvsdist b39f9d
      (cond
cvsdist b39f9d
       ;; are we inside a multi-line string or comment?
cvsdist b39f9d
       ((or (and (nth 3 pps) (nth 3 boipps))
cvsdist b39f9d
	    (and (nth 4 pps) (nth 4 boipps)))
cvsdist b39f9d
	(save-excursion
cvsdist b39f9d
	  (if (not py-align-multiline-strings-p) 0
cvsdist b39f9d
	    ;; skip back over blank & non-indenting comment lines
cvsdist b39f9d
	    ;; note: will skip a blank or non-indenting comment line
cvsdist b39f9d
	    ;; that happens to be a continuation line too
cvsdist b39f9d
	    (re-search-backward "^[ \t]*\\([^ \t\n#]\\|#[ \t\n]\\)" nil 'move)
cvsdist b39f9d
	    (back-to-indentation)
cvsdist b39f9d
	    (current-column))))
cvsdist b39f9d
       ;; are we on a continuation line?
cvsdist b39f9d
       ((py-continuation-line-p)
cvsdist b39f9d
	(let ((startpos (point))
cvsdist b39f9d
	      (open-bracket-pos (py-nesting-level))
cvsdist b39f9d
	      endpos searching found state)
cvsdist b39f9d
	  (if open-bracket-pos
cvsdist b39f9d
	      (progn
cvsdist b39f9d
		;; align with first item in list; else a normal
cvsdist b39f9d
		;; indent beyond the line with the open bracket
cvsdist b39f9d
		(goto-char (1+ open-bracket-pos)) ; just beyond bracket
cvsdist b39f9d
		;; is the first list item on the same line?
cvsdist b39f9d
		(skip-chars-forward " \t")
cvsdist b39f9d
		(if (null (memq (following-char) '(?\n ?# ?\\)))
cvsdist b39f9d
					; yes, so line up with it
cvsdist b39f9d
		    (current-column)
cvsdist b39f9d
		  ;; first list item on another line, or doesn't exist yet
cvsdist b39f9d
		  (forward-line 1)
cvsdist b39f9d
		  (while (and (< (point) startpos)
cvsdist b39f9d
			      (looking-at "[ \t]*[#\n\\\\]")) ; skip noise
cvsdist b39f9d
		    (forward-line 1))
cvsdist b39f9d
		  (if (and (< (point) startpos)
cvsdist b39f9d
			   (/= startpos
cvsdist b39f9d
			       (save-excursion
cvsdist b39f9d
				 (goto-char (1+ open-bracket-pos))
cvsdist b39f9d
				 (forward-comment (point-max))
cvsdist b39f9d
				 (point))))
cvsdist b39f9d
		      ;; again mimic the first list item
cvsdist b39f9d
		      (current-indentation)
cvsdist b39f9d
		    ;; else they're about to enter the first item
cvsdist b39f9d
		    (goto-char open-bracket-pos)
cvsdist b39f9d
		    (setq placeholder (point))
cvsdist b39f9d
		    (py-goto-initial-line)
cvsdist b39f9d
		    (py-goto-beginning-of-tqs
cvsdist b39f9d
		     (save-excursion (nth 3 (parse-partial-sexp
cvsdist b39f9d
					     placeholder (point)))))
cvsdist b39f9d
		    (+ (current-indentation) py-indent-offset))))
cvsdist b39f9d
cvsdist b39f9d
	    ;; else on backslash continuation line
cvsdist b39f9d
	    (forward-line -1)
cvsdist b39f9d
	    (if (py-continuation-line-p) ; on at least 3rd line in block
cvsdist b39f9d
		(current-indentation)	; so just continue the pattern
cvsdist b39f9d
	      ;; else started on 2nd line in block, so indent more.
cvsdist b39f9d
	      ;; if base line is an assignment with a start on a RHS,
cvsdist b39f9d
	      ;; indent to 2 beyond the leftmost "="; else skip first
cvsdist b39f9d
	      ;; chunk of non-whitespace characters on base line, + 1 more
cvsdist b39f9d
	      ;; column
cvsdist b39f9d
	      (end-of-line)
cvsdist c08065
	      (setq endpos (point)
cvsdist c08065
		    searching t)
cvsdist b39f9d
	      (back-to-indentation)
cvsdist b39f9d
	      (setq startpos (point))
cvsdist b39f9d
	      ;; look at all "=" from left to right, stopping at first
cvsdist b39f9d
	      ;; one not nested in a list or string
cvsdist b39f9d
	      (while searching
cvsdist b39f9d
		(skip-chars-forward "^=" endpos)
cvsdist b39f9d
		(if (= (point) endpos)
cvsdist b39f9d
		    (setq searching nil)
cvsdist b39f9d
		  (forward-char 1)
cvsdist b39f9d
		  (setq state (parse-partial-sexp startpos (point)))
cvsdist b39f9d
		  (if (and (zerop (car state)) ; not in a bracket
cvsdist b39f9d
			   (null (nth 3 state))) ; & not in a string
cvsdist b39f9d
		      (progn
cvsdist b39f9d
			(setq searching nil) ; done searching in any case
cvsdist b39f9d
			(setq found
cvsdist b39f9d
			      (not (or
cvsdist b39f9d
				    (eq (following-char) ?=)
cvsdist b39f9d
				    (memq (char-after (- (point) 2))
cvsdist b39f9d
					  '(?< ?> ?!)))))))))
cvsdist b39f9d
	      (if (or (not found)	; not an assignment
cvsdist b39f9d
		      (looking-at "[ \t]*\\\\")) ; <=><spaces><backslash>
cvsdist b39f9d
		  (progn
cvsdist b39f9d
		    (goto-char startpos)
cvsdist b39f9d
		    (skip-chars-forward "^ \t\n")))
cvsdist c08065
	      ;; if this is a continuation for a block opening
cvsdist c08065
	      ;; statement, add some extra offset.
cvsdist c08065
	      (+ (current-column) (if (py-statement-opens-block-p)
cvsdist c08065
				      py-continuation-offset 0)
cvsdist c08065
		 1)
cvsdist c08065
	      ))))
cvsdist b39f9d
cvsdist b39f9d
       ;; not on a continuation line
cvsdist b39f9d
       ((bobp) (current-indentation))
cvsdist b39f9d
cvsdist b39f9d
       ;; Dfn: "Indenting comment line".  A line containing only a
cvsdist b39f9d
       ;; comment, but which is treated like a statement for
cvsdist b39f9d
       ;; indentation calculation purposes.  Such lines are only
cvsdist b39f9d
       ;; treated specially by the mode; they are not treated
cvsdist b39f9d
       ;; specially by the Python interpreter.
cvsdist b39f9d
cvsdist b39f9d
       ;; The rules for indenting comment lines are a line where:
cvsdist b39f9d
       ;;   - the first non-whitespace character is `#', and
cvsdist b39f9d
       ;;   - the character following the `#' is whitespace, and
cvsdist b39f9d
       ;;   - the line is dedented with respect to (i.e. to the left
cvsdist b39f9d
       ;;     of) the indentation of the preceding non-blank line.
cvsdist b39f9d
cvsdist b39f9d
       ;; The first non-blank line following an indenting comment
cvsdist b39f9d
       ;; line is given the same amount of indentation as the
cvsdist b39f9d
       ;; indenting comment line.
cvsdist b39f9d
cvsdist b39f9d
       ;; All other comment-only lines are ignored for indentation
cvsdist b39f9d
       ;; purposes.
cvsdist b39f9d
cvsdist b39f9d
       ;; Are we looking at a comment-only line which is *not* an
cvsdist b39f9d
       ;; indenting comment line?  If so, we assume that it's been
cvsdist b39f9d
       ;; placed at the desired indentation, so leave it alone.
cvsdist b39f9d
       ;; Indenting comment lines are aligned as statements down
cvsdist b39f9d
       ;; below.
cvsdist b39f9d
       ((and (looking-at "[ \t]*#[^ \t\n]")
cvsdist b39f9d
	     ;; NOTE: this test will not be performed in older Emacsen
cvsdist b39f9d
	     (fboundp 'forward-comment)
cvsdist b39f9d
	     (<= (current-indentation)
cvsdist b39f9d
		 (save-excursion
cvsdist b39f9d
		   (forward-comment (- (point-max)))
cvsdist b39f9d
		   (current-indentation))))
cvsdist b39f9d
	(current-indentation))
cvsdist b39f9d
cvsdist b39f9d
       ;; else indentation based on that of the statement that
cvsdist b39f9d
       ;; precedes us; use the first line of that statement to
cvsdist b39f9d
       ;; establish the base, in case the user forced a non-std
cvsdist b39f9d
       ;; indentation for the continuation lines (if any)
cvsdist b39f9d
       (t
cvsdist b39f9d
	;; skip back over blank & non-indenting comment lines note:
cvsdist b39f9d
	;; will skip a blank or non-indenting comment line that
cvsdist b39f9d
	;; happens to be a continuation line too.  use fast Emacs 19
cvsdist b39f9d
	;; function if it's there.
cvsdist b39f9d
	(if (and (eq py-honor-comment-indentation nil)
cvsdist b39f9d
		 (fboundp 'forward-comment))
cvsdist b39f9d
	    (forward-comment (- (point-max)))
cvsdist b39f9d
	  (let ((prefix-re (concat py-block-comment-prefix "[ \t]*"))
cvsdist b39f9d
		done)
cvsdist b39f9d
	    (while (not done)
cvsdist b39f9d
	      (re-search-backward "^[ \t]*\\([^ \t\n#]\\|#\\)" nil 'move)
cvsdist b39f9d
	      (setq done (or (bobp)
cvsdist b39f9d
			     (and (eq py-honor-comment-indentation t)
cvsdist b39f9d
				  (save-excursion
cvsdist b39f9d
				    (back-to-indentation)
cvsdist b39f9d
				    (not (looking-at prefix-re))
cvsdist b39f9d
				    ))
cvsdist b39f9d
			     (and (not (eq py-honor-comment-indentation t))
cvsdist b39f9d
				  (save-excursion
cvsdist b39f9d
				    (back-to-indentation)
cvsdist 86923e
				    (and (not (looking-at prefix-re))
cvsdist 86923e
					 (or (looking-at "[^#]")
cvsdist 86923e
					     (not (zerop (current-column)))
cvsdist 86923e
					     ))
cvsdist 86923e
				    ))
cvsdist b39f9d
			     ))
cvsdist b39f9d
	      )))
cvsdist b39f9d
	;; if we landed inside a string, go to the beginning of that
cvsdist b39f9d
	;; string. this handles triple quoted, multi-line spanning
cvsdist b39f9d
	;; strings.
cvsdist b39f9d
	(py-goto-beginning-of-tqs (nth 3 (parse-partial-sexp bod (point))))
cvsdist b39f9d
	;; now skip backward over continued lines
cvsdist b39f9d
	(setq placeholder (point))
cvsdist b39f9d
	(py-goto-initial-line)
cvsdist b39f9d
	;; we may *now* have landed in a TQS, so find the beginning of
cvsdist b39f9d
	;; this string.
cvsdist b39f9d
	(py-goto-beginning-of-tqs
cvsdist b39f9d
	 (save-excursion (nth 3 (parse-partial-sexp
cvsdist b39f9d
				 placeholder (point)))))
cvsdist b39f9d
	(+ (current-indentation)
cvsdist b39f9d
	   (if (py-statement-opens-block-p)
cvsdist b39f9d
	       py-indent-offset
cvsdist b39f9d
	     (if (and honor-block-close-p (py-statement-closes-block-p))
cvsdist b39f9d
		 (- py-indent-offset)
cvsdist b39f9d
	       0)))
cvsdist b39f9d
	)))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-guess-indent-offset (&optional global)
cvsdist b39f9d
  "Guess a good value for, and change, `py-indent-offset'.
cvsdist b39f9d
cvsdist b39f9d
By default, make a buffer-local copy of `py-indent-offset' with the
cvsdist b39f9d
new value, so that other Python buffers are not affected.  With
cvsdist b39f9d
\\[universal-argument] (programmatically, optional argument GLOBAL),
cvsdist b39f9d
change the global value of `py-indent-offset'.  This affects all
cvsdist b39f9d
Python buffers (that don't have their own buffer-local copy), both
cvsdist b39f9d
those currently existing and those created later in the Emacs session.
cvsdist b39f9d
cvsdist b39f9d
Some people use a different value for `py-indent-offset' than you use.
cvsdist b39f9d
There's no excuse for such foolishness, but sometimes you have to deal
cvsdist b39f9d
with their ugly code anyway.  This function examines the file and sets
cvsdist b39f9d
`py-indent-offset' to what it thinks it was when they created the
cvsdist b39f9d
mess.
cvsdist b39f9d
cvsdist b39f9d
Specifically, it searches forward from the statement containing point,
cvsdist b39f9d
looking for a line that opens a block of code.  `py-indent-offset' is
cvsdist b39f9d
set to the difference in indentation between that line and the Python
cvsdist b39f9d
statement following it.  If the search doesn't succeed going forward,
cvsdist b39f9d
it's tried again going backward."
cvsdist b39f9d
  (interactive "P")			; raw prefix arg
cvsdist b39f9d
  (let (new-value
cvsdist b39f9d
	(start (point))
cvsdist b39f9d
	(restart (point))
cvsdist b39f9d
	(found nil)
cvsdist b39f9d
	colon-indent)
cvsdist b39f9d
    (py-goto-initial-line)
cvsdist b39f9d
    (while (not (or found (eobp)))
cvsdist b39f9d
      (when (and (re-search-forward ":[ \t]*\\($\\|[#\\]\\)" nil 'move)
cvsdist b39f9d
		 (not (py-in-literal restart)))
cvsdist b39f9d
	(setq restart (point))
cvsdist b39f9d
	(py-goto-initial-line)
cvsdist b39f9d
	(if (py-statement-opens-block-p)
cvsdist b39f9d
	    (setq found t)
cvsdist b39f9d
	  (goto-char restart))))
cvsdist b39f9d
    (unless found
cvsdist b39f9d
      (goto-char start)
cvsdist b39f9d
      (py-goto-initial-line)
cvsdist b39f9d
      (while (not (or found (bobp)))
cvsdist b39f9d
	(setq found (and
cvsdist b39f9d
		     (re-search-backward ":[ \t]*\\($\\|[#\\]\\)" nil 'move)
cvsdist b39f9d
		     (or (py-goto-initial-line) t) ; always true -- side effect
cvsdist b39f9d
		     (py-statement-opens-block-p)))))
cvsdist b39f9d
    (setq colon-indent (current-indentation)
cvsdist b39f9d
	  found (and found (zerop (py-next-statement 1)))
cvsdist b39f9d
	  new-value (- (current-indentation) colon-indent))
cvsdist b39f9d
    (goto-char start)
cvsdist b39f9d
    (if (not found)
cvsdist b39f9d
	(error "Sorry, couldn't guess a value for py-indent-offset")
cvsdist b39f9d
      (funcall (if global 'kill-local-variable 'make-local-variable)
cvsdist b39f9d
	       'py-indent-offset)
cvsdist b39f9d
      (setq py-indent-offset new-value)
cvsdist b39f9d
      (or noninteractive
cvsdist b39f9d
	  (message "%s value of py-indent-offset set to %d"
cvsdist b39f9d
		   (if global "Global" "Local")
cvsdist b39f9d
		   py-indent-offset)))
cvsdist b39f9d
    ))
cvsdist b39f9d
cvsdist b39f9d
(defun py-comment-indent-function ()
cvsdist b39f9d
  "Python version of `comment-indent-function'."
cvsdist b39f9d
  ;; This is required when filladapt is turned off.  Without it, when
cvsdist b39f9d
  ;; filladapt is not used, comments which start in column zero
cvsdist b39f9d
  ;; cascade one character to the right
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (beginning-of-line)
cvsdist b39f9d
    (let ((eol (py-point 'eol)))
cvsdist b39f9d
      (and comment-start-skip
cvsdist b39f9d
	   (re-search-forward comment-start-skip eol t)
cvsdist b39f9d
	   (setq eol (match-beginning 0)))
cvsdist b39f9d
      (goto-char eol)
cvsdist b39f9d
      (skip-chars-backward " \t")
cvsdist b39f9d
      (max comment-column (+ (current-column) (if (bolp) 0 1)))
cvsdist b39f9d
      )))
cvsdist b39f9d
cvsdist b39f9d
(defun py-narrow-to-defun (&optional class)
cvsdist b39f9d
  "Make text outside current defun invisible.
cvsdist b39f9d
The defun visible is the one that contains point or follows point.
cvsdist b39f9d
Optional CLASS is passed directly to `py-beginning-of-def-or-class'."
cvsdist b39f9d
  (interactive "P")
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (widen)
cvsdist b39f9d
    (py-end-of-def-or-class class)
cvsdist b39f9d
    (let ((end (point)))
cvsdist b39f9d
      (py-beginning-of-def-or-class class)
cvsdist b39f9d
      (narrow-to-region (point) end))))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
(defun py-shift-region (start end count)
cvsdist b39f9d
  "Indent lines from START to END by COUNT spaces."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (goto-char end)
cvsdist b39f9d
    (beginning-of-line)
cvsdist b39f9d
    (setq end (point))
cvsdist b39f9d
    (goto-char start)
cvsdist b39f9d
    (beginning-of-line)
cvsdist b39f9d
    (setq start (point))
cvsdist b39f9d
    (indent-rigidly start end count)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-shift-region-left (start end &optional count)
cvsdist b39f9d
  "Shift region of Python code to the left.
cvsdist b39f9d
The lines from the line containing the start of the current region up
cvsdist b39f9d
to (but not including) the line containing the end of the region are
cvsdist b39f9d
shifted to the left, by `py-indent-offset' columns.
cvsdist b39f9d
cvsdist b39f9d
If a prefix argument is given, the region is instead shifted by that
cvsdist b39f9d
many columns.  With no active region, dedent only the current line.
cvsdist b39f9d
You cannot dedent the region if any line is already at column zero."
cvsdist b39f9d
  (interactive
cvsdist b39f9d
   (let ((p (point))
cvsdist b39f9d
	 (m (mark))
cvsdist b39f9d
	 (arg current-prefix-arg))
cvsdist b39f9d
     (if m
cvsdist b39f9d
	 (list (min p m) (max p m) arg)
cvsdist b39f9d
       (list p (save-excursion (forward-line 1) (point)) arg))))
cvsdist b39f9d
  ;; if any line is at column zero, don't shift the region
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (goto-char start)
cvsdist b39f9d
    (while (< (point) end)
cvsdist b39f9d
      (back-to-indentation)
cvsdist b39f9d
      (if (and (zerop (current-column))
cvsdist b39f9d
	       (not (looking-at "\\s *$")))
cvsdist b39f9d
	  (error "Region is at left edge"))
cvsdist b39f9d
      (forward-line 1)))
cvsdist b39f9d
  (py-shift-region start end (- (prefix-numeric-value
cvsdist b39f9d
				 (or count py-indent-offset))))
cvsdist b39f9d
  (py-keep-region-active))
cvsdist b39f9d
cvsdist b39f9d
(defun py-shift-region-right (start end &optional count)
cvsdist b39f9d
  "Shift region of Python code to the right.
cvsdist b39f9d
The lines from the line containing the start of the current region up
cvsdist b39f9d
to (but not including) the line containing the end of the region are
cvsdist b39f9d
shifted to the right, by `py-indent-offset' columns.
cvsdist b39f9d
cvsdist b39f9d
If a prefix argument is given, the region is instead shifted by that
cvsdist b39f9d
many columns.  With no active region, indent only the current line."
cvsdist b39f9d
  (interactive
cvsdist b39f9d
   (let ((p (point))
cvsdist b39f9d
	 (m (mark))
cvsdist b39f9d
	 (arg current-prefix-arg))
cvsdist b39f9d
     (if m
cvsdist b39f9d
	 (list (min p m) (max p m) arg)
cvsdist b39f9d
       (list p (save-excursion (forward-line 1) (point)) arg))))
cvsdist b39f9d
  (py-shift-region start end (prefix-numeric-value
cvsdist b39f9d
			      (or count py-indent-offset)))
cvsdist b39f9d
  (py-keep-region-active))
cvsdist b39f9d
cvsdist b39f9d
(defun py-indent-region (start end &optional indent-offset)
cvsdist b39f9d
  "Reindent a region of Python code.
cvsdist b39f9d
cvsdist b39f9d
The lines from the line containing the start of the current region up
cvsdist b39f9d
to (but not including) the line containing the end of the region are
cvsdist b39f9d
reindented.  If the first line of the region has a non-whitespace
cvsdist b39f9d
character in the first column, the first line is left alone and the
cvsdist b39f9d
rest of the region is reindented with respect to it.  Else the entire
cvsdist b39f9d
region is reindented with respect to the (closest code or indenting
cvsdist b39f9d
comment) statement immediately preceding the region.
cvsdist b39f9d
cvsdist b39f9d
This is useful when code blocks are moved or yanked, when enclosing
cvsdist b39f9d
control structures are introduced or removed, or to reformat code
cvsdist b39f9d
using a new value for the indentation offset.
cvsdist b39f9d
cvsdist b39f9d
If a numeric prefix argument is given, it will be used as the value of
cvsdist b39f9d
the indentation offset.  Else the value of `py-indent-offset' will be
cvsdist b39f9d
used.
cvsdist b39f9d
cvsdist b39f9d
Warning: The region must be consistently indented before this function
cvsdist b39f9d
is called!  This function does not compute proper indentation from
cvsdist b39f9d
scratch (that's impossible in Python), it merely adjusts the existing
cvsdist b39f9d
indentation to be correct in context.
cvsdist b39f9d
cvsdist b39f9d
Warning: This function really has no idea what to do with
cvsdist b39f9d
non-indenting comment lines, and shifts them as if they were indenting
cvsdist b39f9d
comment lines.  Fixing this appears to require telepathy.
cvsdist b39f9d
cvsdist b39f9d
Special cases: whitespace is deleted from blank lines; continuation
cvsdist b39f9d
lines are shifted by the same amount their initial line was shifted,
cvsdist b39f9d
in order to preserve their relative indentation with respect to their
cvsdist b39f9d
initial line; and comment lines beginning in column 1 are ignored."
cvsdist b39f9d
  (interactive "*r\nP")			; region; raw prefix arg
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (goto-char end)   (beginning-of-line) (setq end (point-marker))
cvsdist b39f9d
    (goto-char start) (beginning-of-line)
cvsdist b39f9d
    (let ((py-indent-offset (prefix-numeric-value
cvsdist b39f9d
			     (or indent-offset py-indent-offset)))
cvsdist b39f9d
	  (indents '(-1))		; stack of active indent levels
cvsdist b39f9d
	  (target-column 0)		; column to which to indent
cvsdist b39f9d
	  (base-shifted-by 0)		; amount last base line was shifted
cvsdist b39f9d
	  (indent-base (if (looking-at "[ \t\n]")
cvsdist b39f9d
			   (py-compute-indentation t)
cvsdist b39f9d
			 0))
cvsdist b39f9d
	  ci)
cvsdist b39f9d
      (while (< (point) end)
cvsdist b39f9d
	(setq ci (current-indentation))
cvsdist b39f9d
	;; figure out appropriate target column
cvsdist b39f9d
	(cond
cvsdist b39f9d
	 ((or (eq (following-char) ?#)	; comment in column 1
cvsdist b39f9d
	      (looking-at "[ \t]*$"))	; entirely blank
cvsdist b39f9d
	  (setq target-column 0))
cvsdist b39f9d
	 ((py-continuation-line-p)	; shift relative to base line
cvsdist b39f9d
	  (setq target-column (+ ci base-shifted-by)))
cvsdist b39f9d
	 (t				; new base line
cvsdist b39f9d
	  (if (> ci (car indents))	; going deeper; push it
cvsdist b39f9d
	      (setq indents (cons ci indents))
cvsdist b39f9d
	    ;; else we should have seen this indent before
cvsdist b39f9d
	    (setq indents (memq ci indents)) ; pop deeper indents
cvsdist b39f9d
	    (if (null indents)
cvsdist b39f9d
		(error "Bad indentation in region, at line %d"
cvsdist b39f9d
		       (save-restriction
cvsdist b39f9d
			 (widen)
cvsdist b39f9d
			 (1+ (count-lines 1 (point)))))))
cvsdist b39f9d
	  (setq target-column (+ indent-base
cvsdist b39f9d
				 (* py-indent-offset
cvsdist b39f9d
				    (- (length indents) 2))))
cvsdist b39f9d
	  (setq base-shifted-by (- target-column ci))))
cvsdist b39f9d
	;; shift as needed
cvsdist b39f9d
	(if (/= ci target-column)
cvsdist b39f9d
	    (progn
cvsdist b39f9d
	      (delete-horizontal-space)
cvsdist b39f9d
	      (indent-to target-column)))
cvsdist b39f9d
	(forward-line 1))))
cvsdist b39f9d
  (set-marker end nil))
cvsdist b39f9d
cvsdist b39f9d
(defun py-comment-region (beg end &optional arg)
cvsdist b39f9d
  "Like `comment-region' but uses double hash (`#') comment starter."
cvsdist b39f9d
  (interactive "r\nP")
cvsdist b39f9d
  (let ((comment-start py-block-comment-prefix))
cvsdist b39f9d
    (comment-region beg end arg)))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Functions for moving point
cvsdist b39f9d
(defun py-previous-statement (count)
cvsdist b39f9d
  "Go to the start of the COUNTth preceding Python statement.
cvsdist b39f9d
By default, goes to the previous statement.  If there is no such
cvsdist b39f9d
statement, goes to the first statement.  Return count of statements
cvsdist b39f9d
left to move.  `Statements' do not include blank, comment, or
cvsdist b39f9d
continuation lines."
cvsdist b39f9d
  (interactive "p")			; numeric prefix arg
cvsdist b39f9d
  (if (< count 0) (py-next-statement (- count))
cvsdist b39f9d
    (py-goto-initial-line)
cvsdist b39f9d
    (let (start)
cvsdist b39f9d
      (while (and
cvsdist b39f9d
	      (setq start (point))	; always true -- side effect
cvsdist b39f9d
	      (> count 0)
cvsdist b39f9d
	      (zerop (forward-line -1))
cvsdist b39f9d
	      (py-goto-statement-at-or-above))
cvsdist b39f9d
	(setq count (1- count)))
cvsdist b39f9d
      (if (> count 0) (goto-char start)))
cvsdist b39f9d
    count))
cvsdist b39f9d
cvsdist b39f9d
(defun py-next-statement (count)
cvsdist b39f9d
  "Go to the start of next Python statement.
cvsdist b39f9d
If the statement at point is the i'th Python statement, goes to the
cvsdist b39f9d
start of statement i+COUNT.  If there is no such statement, goes to the
cvsdist b39f9d
last statement.  Returns count of statements left to move.  `Statements'
cvsdist b39f9d
do not include blank, comment, or continuation lines."
cvsdist b39f9d
  (interactive "p")			; numeric prefix arg
cvsdist b39f9d
  (if (< count 0) (py-previous-statement (- count))
cvsdist b39f9d
    (beginning-of-line)
cvsdist b39f9d
    (let (start)
cvsdist b39f9d
      (while (and
cvsdist b39f9d
	      (setq start (point))	; always true -- side effect
cvsdist b39f9d
	      (> count 0)
cvsdist b39f9d
	      (py-goto-statement-below))
cvsdist b39f9d
	(setq count (1- count)))
cvsdist b39f9d
      (if (> count 0) (goto-char start)))
cvsdist b39f9d
    count))
cvsdist b39f9d
cvsdist b39f9d
(defun py-goto-block-up (&optional nomark)
cvsdist b39f9d
  "Move up to start of current block.
cvsdist b39f9d
Go to the statement that starts the smallest enclosing block; roughly
cvsdist b39f9d
speaking, this will be the closest preceding statement that ends with a
cvsdist b39f9d
colon and is indented less than the statement you started on.  If
cvsdist b39f9d
successful, also sets the mark to the starting point.
cvsdist b39f9d
cvsdist b39f9d
`\\[py-mark-block]' can be used afterward to mark the whole code
cvsdist b39f9d
block, if desired.
cvsdist b39f9d
cvsdist b39f9d
If called from a program, the mark will not be set if optional argument
cvsdist b39f9d
NOMARK is not nil."
cvsdist b39f9d
  (interactive)
cvsdist b39f9d
  (let ((start (point))
cvsdist b39f9d
	(found nil)
cvsdist b39f9d
	initial-indent)
cvsdist b39f9d
    (py-goto-initial-line)
cvsdist b39f9d
    ;; if on blank or non-indenting comment line, use the preceding stmt
cvsdist b39f9d
    (if (looking-at "[ \t]*\\($\\|#[^ \t\n]\\)")
cvsdist b39f9d
	(progn
cvsdist b39f9d
	  (py-goto-statement-at-or-above)
cvsdist b39f9d
	  (setq found (py-statement-opens-block-p))))
cvsdist b39f9d
    ;; search back for colon line indented less
cvsdist b39f9d
    (setq initial-indent (current-indentation))
cvsdist b39f9d
    (if (zerop initial-indent)
cvsdist b39f9d
	;; force fast exit
cvsdist b39f9d
	(goto-char (point-min)))
cvsdist b39f9d
    (while (not (or found (bobp)))
cvsdist b39f9d
      (setq found
cvsdist b39f9d
	    (and
cvsdist b39f9d
	     (re-search-backward ":[ \t]*\\($\\|[#\\]\\)" nil 'move)
cvsdist b39f9d
	     (or (py-goto-initial-line) t) ; always true -- side effect
cvsdist b39f9d
	     (< (current-indentation) initial-indent)
cvsdist b39f9d
	     (py-statement-opens-block-p))))
cvsdist b39f9d
    (if found
cvsdist b39f9d
	(progn
cvsdist b39f9d
	  (or nomark (push-mark start))
cvsdist b39f9d
	  (back-to-indentation))
cvsdist b39f9d
      (goto-char start)
cvsdist b39f9d
      (error "Enclosing block not found"))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-beginning-of-def-or-class (&optional class count)
cvsdist b39f9d
  "Move point to start of `def' or `class'.
cvsdist b39f9d
cvsdist b39f9d
Searches back for the closest preceding `def'.  If you supply a prefix
cvsdist b39f9d
arg, looks for a `class' instead.  The docs below assume the `def'
cvsdist b39f9d
case; just substitute `class' for `def' for the other case.
cvsdist b39f9d
Programmatically, if CLASS is `either', then moves to either `class'
cvsdist b39f9d
or `def'.
cvsdist b39f9d
cvsdist b39f9d
When second optional argument is given programmatically, move to the
cvsdist b39f9d
COUNTth start of `def'.
cvsdist b39f9d
cvsdist b39f9d
If point is in a `def' statement already, and after the `d', simply
cvsdist b39f9d
moves point to the start of the statement.
cvsdist b39f9d
cvsdist b39f9d
Otherwise (i.e. when point is not in a `def' statement, or at or
cvsdist b39f9d
before the `d' of a `def' statement), searches for the closest
cvsdist b39f9d
preceding `def' statement, and leaves point at its start.  If no such
cvsdist b39f9d
statement can be found, leaves point at the start of the buffer.
cvsdist b39f9d
cvsdist b39f9d
Returns t iff a `def' statement is found by these rules.
cvsdist b39f9d
cvsdist b39f9d
Note that doing this command repeatedly will take you closer to the
cvsdist b39f9d
start of the buffer each time.
cvsdist b39f9d
cvsdist b39f9d
To mark the current `def', see `\\[py-mark-def-or-class]'."
cvsdist b39f9d
  (interactive "P")			; raw prefix arg
cvsdist b39f9d
  (setq count (or count 1))
cvsdist b39f9d
  (let ((at-or-before-p (<= (current-column) (current-indentation)))
cvsdist b39f9d
	(start-of-line (goto-char (py-point 'bol)))
cvsdist b39f9d
	(start-of-stmt (goto-char (py-point 'bos)))
cvsdist b39f9d
	(start-re (cond ((eq class 'either) "^[ \t]*\\(class\\|def\\)\\>")
cvsdist b39f9d
			(class "^[ \t]*class\\>")
cvsdist b39f9d
			(t "^[ \t]*def\\>")))
cvsdist b39f9d
	)
cvsdist b39f9d
    ;; searching backward
cvsdist b39f9d
    (if (and (< 0 count)
cvsdist b39f9d
	     (or (/= start-of-stmt start-of-line)
cvsdist b39f9d
		 (not at-or-before-p)))
cvsdist b39f9d
	(end-of-line))
cvsdist b39f9d
    ;; search forward
cvsdist b39f9d
    (if (and (> 0 count)
cvsdist b39f9d
	     (zerop (current-column))
cvsdist b39f9d
	     (looking-at start-re))
cvsdist b39f9d
	(end-of-line))
cvsdist b39f9d
    (if (re-search-backward start-re nil 'move count)
cvsdist b39f9d
	(goto-char (match-beginning 0)))))
cvsdist b39f9d
cvsdist b39f9d
;; Backwards compatibility
cvsdist b39f9d
(defalias 'beginning-of-python-def-or-class 'py-beginning-of-def-or-class)
cvsdist b39f9d
cvsdist b39f9d
(defun py-end-of-def-or-class (&optional class count)
cvsdist b39f9d
  "Move point beyond end of `def' or `class' body.
cvsdist b39f9d
cvsdist b39f9d
By default, looks for an appropriate `def'.  If you supply a prefix
cvsdist b39f9d
arg, looks for a `class' instead.  The docs below assume the `def'
cvsdist b39f9d
case; just substitute `class' for `def' for the other case.
cvsdist b39f9d
Programmatically, if CLASS is `either', then moves to either `class'
cvsdist b39f9d
or `def'.
cvsdist b39f9d
cvsdist b39f9d
When second optional argument is given programmatically, move to the
cvsdist b39f9d
COUNTth end of `def'.
cvsdist b39f9d
cvsdist b39f9d
If point is in a `def' statement already, this is the `def' we use.
cvsdist b39f9d
cvsdist b39f9d
Else, if the `def' found by `\\[py-beginning-of-def-or-class]'
cvsdist b39f9d
contains the statement you started on, that's the `def' we use.
cvsdist b39f9d
cvsdist b39f9d
Otherwise, we search forward for the closest following `def', and use that.
cvsdist b39f9d
cvsdist b39f9d
If a `def' can be found by these rules, point is moved to the start of
cvsdist b39f9d
the line immediately following the `def' block, and the position of the
cvsdist b39f9d
start of the `def' is returned.
cvsdist b39f9d
cvsdist b39f9d
Else point is moved to the end of the buffer, and nil is returned.
cvsdist b39f9d
cvsdist b39f9d
Note that doing this command repeatedly will take you closer to the
cvsdist b39f9d
end of the buffer each time.
cvsdist b39f9d
cvsdist b39f9d
To mark the current `def', see `\\[py-mark-def-or-class]'."
cvsdist b39f9d
  (interactive "P")			; raw prefix arg
cvsdist b39f9d
  (if (and count (/= count 1))
cvsdist b39f9d
      (py-beginning-of-def-or-class (- 1 count)))
cvsdist b39f9d
  (let ((start (progn (py-goto-initial-line) (point)))
cvsdist b39f9d
	(which (cond ((eq class 'either) "\\(class\\|def\\)")
cvsdist b39f9d
		     (class "class")
cvsdist b39f9d
		     (t "def")))
cvsdist b39f9d
	(state 'not-found))
cvsdist b39f9d
    ;; move point to start of appropriate def/class
cvsdist b39f9d
    (if (looking-at (concat "[ \t]*" which "\\>")) ; already on one
cvsdist b39f9d
	(setq state 'at-beginning)
cvsdist b39f9d
      ;; else see if py-beginning-of-def-or-class hits container
cvsdist b39f9d
      (if (and (py-beginning-of-def-or-class class)
cvsdist b39f9d
	       (progn (py-goto-beyond-block)
cvsdist b39f9d
		      (> (point) start)))
cvsdist b39f9d
	  (setq state 'at-end)
cvsdist b39f9d
	;; else search forward
cvsdist b39f9d
	(goto-char start)
cvsdist b39f9d
	(if (re-search-forward (concat "^[ \t]*" which "\\>") nil 'move)
cvsdist b39f9d
	    (progn (setq state 'at-beginning)
cvsdist b39f9d
		   (beginning-of-line)))))
cvsdist b39f9d
    (cond
cvsdist b39f9d
     ((eq state 'at-beginning) (py-goto-beyond-block) t)
cvsdist b39f9d
     ((eq state 'at-end) t)
cvsdist b39f9d
     ((eq state 'not-found) nil)
cvsdist b39f9d
     (t (error "Internal error in `py-end-of-def-or-class'")))))
cvsdist b39f9d
cvsdist b39f9d
;; Backwards compabitility
cvsdist b39f9d
(defalias 'end-of-python-def-or-class 'py-end-of-def-or-class)
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Functions for marking regions
cvsdist b39f9d
(defun py-mark-block (&optional extend just-move)
cvsdist b39f9d
  "Mark following block of lines.  With prefix arg, mark structure.
cvsdist b39f9d
Easier to use than explain.  It sets the region to an `interesting'
cvsdist b39f9d
block of succeeding lines.  If point is on a blank line, it goes down to
cvsdist b39f9d
the next non-blank line.  That will be the start of the region.  The end
cvsdist b39f9d
of the region depends on the kind of line at the start:
cvsdist b39f9d
cvsdist b39f9d
 - If a comment, the region will include all succeeding comment lines up
cvsdist b39f9d
   to (but not including) the next non-comment line (if any).
cvsdist b39f9d
cvsdist b39f9d
 - Else if a prefix arg is given, and the line begins one of these
cvsdist b39f9d
   structures:
cvsdist b39f9d
cvsdist b39f9d
     if elif else try except finally for while def class
cvsdist b39f9d
cvsdist b39f9d
   the region will be set to the body of the structure, including
cvsdist b39f9d
   following blocks that `belong' to it, but excluding trailing blank
cvsdist b39f9d
   and comment lines.  E.g., if on a `try' statement, the `try' block
cvsdist b39f9d
   and all (if any) of the following `except' and `finally' blocks
cvsdist b39f9d
   that belong to the `try' structure will be in the region.  Ditto
cvsdist b39f9d
   for if/elif/else, for/else and while/else structures, and (a bit
cvsdist b39f9d
   degenerate, since they're always one-block structures) def and
cvsdist b39f9d
   class blocks.
cvsdist b39f9d
cvsdist b39f9d
 - Else if no prefix argument is given, and the line begins a Python
cvsdist b39f9d
   block (see list above), and the block is not a `one-liner' (i.e.,
cvsdist b39f9d
   the statement ends with a colon, not with code), the region will
cvsdist b39f9d
   include all succeeding lines up to (but not including) the next
cvsdist b39f9d
   code statement (if any) that's indented no more than the starting
cvsdist b39f9d
   line, except that trailing blank and comment lines are excluded.
cvsdist b39f9d
   E.g., if the starting line begins a multi-statement `def'
cvsdist b39f9d
   structure, the region will be set to the full function definition,
cvsdist b39f9d
   but without any trailing `noise' lines.
cvsdist b39f9d
cvsdist b39f9d
 - Else the region will include all succeeding lines up to (but not
cvsdist b39f9d
   including) the next blank line, or code or indenting-comment line
cvsdist b39f9d
   indented strictly less than the starting line.  Trailing indenting
cvsdist b39f9d
   comment lines are included in this case, but not trailing blank
cvsdist b39f9d
   lines.
cvsdist b39f9d
cvsdist b39f9d
A msg identifying the location of the mark is displayed in the echo
cvsdist b39f9d
area; or do `\\[exchange-point-and-mark]' to flip down to the end.
cvsdist b39f9d
cvsdist b39f9d
If called from a program, optional argument EXTEND plays the role of
cvsdist b39f9d
the prefix arg, and if optional argument JUST-MOVE is not nil, just
cvsdist b39f9d
moves to the end of the block (& does not set mark or display a msg)."
cvsdist b39f9d
  (interactive "P")			; raw prefix arg
cvsdist b39f9d
  (py-goto-initial-line)
cvsdist b39f9d
  ;; skip over blank lines
cvsdist b39f9d
  (while (and
cvsdist b39f9d
	  (looking-at "[ \t]*$")	; while blank line
cvsdist b39f9d
	  (not (eobp)))			; & somewhere to go
cvsdist b39f9d
    (forward-line 1))
cvsdist b39f9d
  (if (eobp)
cvsdist b39f9d
      (error "Hit end of buffer without finding a non-blank stmt"))
cvsdist b39f9d
  (let ((initial-pos (point))
cvsdist b39f9d
	(initial-indent (current-indentation))
cvsdist b39f9d
	last-pos			; position of last stmt in region
cvsdist b39f9d
	(followers
cvsdist b39f9d
	 '((if elif else) (elif elif else) (else)
cvsdist b39f9d
	   (try except finally) (except except) (finally)
cvsdist b39f9d
	   (for else) (while else)
cvsdist b39f9d
	   (def) (class) ) )
cvsdist b39f9d
	first-symbol next-symbol)
cvsdist b39f9d
cvsdist b39f9d
    (cond
cvsdist b39f9d
     ;; if comment line, suck up the following comment lines
cvsdist b39f9d
     ((looking-at "[ \t]*#")
cvsdist b39f9d
      (re-search-forward "^[ \t]*[^ \t#]" nil 'move) ; look for non-comment
cvsdist b39f9d
      (re-search-backward "^[ \t]*#")	; and back to last comment in block
cvsdist b39f9d
      (setq last-pos (point)))
cvsdist b39f9d
cvsdist b39f9d
     ;; else if line is a block line and EXTEND given, suck up
cvsdist b39f9d
     ;; the whole structure
cvsdist b39f9d
     ((and extend
cvsdist b39f9d
	   (setq first-symbol (py-suck-up-first-keyword) )
cvsdist b39f9d
	   (assq first-symbol followers))
cvsdist b39f9d
      (while (and
cvsdist b39f9d
	      (or (py-goto-beyond-block) t) ; side effect
cvsdist b39f9d
	      (forward-line -1)		; side effect
cvsdist b39f9d
	      (setq last-pos (point))	; side effect
cvsdist b39f9d
	      (py-goto-statement-below)
cvsdist b39f9d
	      (= (current-indentation) initial-indent)
cvsdist b39f9d
	      (setq next-symbol (py-suck-up-first-keyword))
cvsdist b39f9d
	      (memq next-symbol (cdr (assq first-symbol followers))))
cvsdist b39f9d
	(setq first-symbol next-symbol)))
cvsdist b39f9d
cvsdist b39f9d
     ;; else if line *opens* a block, search for next stmt indented <=
cvsdist b39f9d
     ((py-statement-opens-block-p)
cvsdist b39f9d
      (while (and
cvsdist b39f9d
	      (setq last-pos (point))	; always true -- side effect
cvsdist b39f9d
	      (py-goto-statement-below)
cvsdist b39f9d
	      (> (current-indentation) initial-indent))
cvsdist b39f9d
	nil))
cvsdist b39f9d
cvsdist b39f9d
     ;; else plain code line; stop at next blank line, or stmt or
cvsdist b39f9d
     ;; indenting comment line indented <
cvsdist b39f9d
     (t
cvsdist b39f9d
      (while (and
cvsdist b39f9d
	      (setq last-pos (point))	; always true -- side effect
cvsdist b39f9d
	      (or (py-goto-beyond-final-line) t)
cvsdist b39f9d
	      (not (looking-at "[ \t]*$")) ; stop at blank line
cvsdist b39f9d
	      (or
cvsdist b39f9d
	       (>= (current-indentation) initial-indent)
cvsdist b39f9d
	       (looking-at "[ \t]*#[^ \t\n]"))) ; ignore non-indenting #
cvsdist b39f9d
	nil)))
cvsdist b39f9d
cvsdist b39f9d
    ;; skip to end of last stmt
cvsdist b39f9d
    (goto-char last-pos)
cvsdist b39f9d
    (py-goto-beyond-final-line)
cvsdist b39f9d
cvsdist b39f9d
    ;; set mark & display
cvsdist b39f9d
    (if just-move
cvsdist b39f9d
	()				; just return
cvsdist b39f9d
      (push-mark (point) 'no-msg)
cvsdist b39f9d
      (forward-line -1)
cvsdist b39f9d
      (message "Mark set after: %s" (py-suck-up-leading-text))
cvsdist b39f9d
      (goto-char initial-pos))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-mark-def-or-class (&optional class)
cvsdist b39f9d
  "Set region to body of def (or class, with prefix arg) enclosing point.
cvsdist b39f9d
Pushes the current mark, then point, on the mark ring (all language
cvsdist b39f9d
modes do this, but although it's handy it's never documented ...).
cvsdist b39f9d
cvsdist b39f9d
In most Emacs language modes, this function bears at least a
cvsdist b39f9d
hallucinogenic resemblance to `\\[py-end-of-def-or-class]' and
cvsdist b39f9d
`\\[py-beginning-of-def-or-class]'.
cvsdist b39f9d
cvsdist b39f9d
And in earlier versions of Python mode, all 3 were tightly connected.
cvsdist b39f9d
Turned out that was more confusing than useful: the `goto start' and
cvsdist b39f9d
`goto end' commands are usually used to search through a file, and
cvsdist b39f9d
people expect them to act a lot like `search backward' and `search
cvsdist b39f9d
forward' string-search commands.  But because Python `def' and `class'
cvsdist b39f9d
can nest to arbitrary levels, finding the smallest def containing
cvsdist b39f9d
point cannot be done via a simple backward search: the def containing
cvsdist b39f9d
point may not be the closest preceding def, or even the closest
cvsdist b39f9d
preceding def that's indented less.  The fancy algorithm required is
cvsdist b39f9d
appropriate for the usual uses of this `mark' command, but not for the
cvsdist b39f9d
`goto' variations.
cvsdist b39f9d
cvsdist b39f9d
So the def marked by this command may not be the one either of the
cvsdist b39f9d
`goto' commands find: If point is on a blank or non-indenting comment
cvsdist b39f9d
line, moves back to start of the closest preceding code statement or
cvsdist b39f9d
indenting comment line.  If this is a `def' statement, that's the def
cvsdist b39f9d
we use.  Else searches for the smallest enclosing `def' block and uses
cvsdist b39f9d
that.  Else signals an error.
cvsdist b39f9d
cvsdist b39f9d
When an enclosing def is found: The mark is left immediately beyond
cvsdist b39f9d
the last line of the def block.  Point is left at the start of the
cvsdist b39f9d
def, except that: if the def is preceded by a number of comment lines
cvsdist b39f9d
followed by (at most) one optional blank line, point is left at the
cvsdist b39f9d
start of the comments; else if the def is preceded by a blank line,
cvsdist b39f9d
point is left at its start.
cvsdist b39f9d
cvsdist b39f9d
The intent is to mark the containing def/class and its associated
cvsdist b39f9d
documentation, to make moving and duplicating functions and classes
cvsdist b39f9d
pleasant."
cvsdist b39f9d
  (interactive "P")			; raw prefix arg
cvsdist b39f9d
  (let ((start (point))
cvsdist b39f9d
	(which (cond ((eq class 'either) "\\(class\\|def\\)")
cvsdist b39f9d
		     (class "class")
cvsdist b39f9d
		     (t "def"))))
cvsdist b39f9d
    (push-mark start)
cvsdist b39f9d
    (if (not (py-go-up-tree-to-keyword which))
cvsdist b39f9d
	(progn (goto-char start)
cvsdist b39f9d
	       (error "Enclosing %s not found"
cvsdist b39f9d
		      (if (eq class 'either)
cvsdist b39f9d
			  "def or class"
cvsdist b39f9d
			which)))
cvsdist b39f9d
      ;; else enclosing def/class found
cvsdist b39f9d
      (setq start (point))
cvsdist b39f9d
      (py-goto-beyond-block)
cvsdist b39f9d
      (push-mark (point))
cvsdist b39f9d
      (goto-char start)
cvsdist b39f9d
      (if (zerop (forward-line -1))	; if there is a preceding line
cvsdist b39f9d
	  (progn
cvsdist b39f9d
	    (if (looking-at "[ \t]*$")	; it's blank
cvsdist b39f9d
		(setq start (point))	; so reset start point
cvsdist b39f9d
	      (goto-char start))	; else try again
cvsdist b39f9d
	    (if (zerop (forward-line -1))
cvsdist b39f9d
		(if (looking-at "[ \t]*#") ; a comment
cvsdist b39f9d
		    ;; look back for non-comment line
cvsdist b39f9d
		    ;; tricky: note that the regexp matches a blank
cvsdist b39f9d
		    ;; line, cuz \n is in the 2nd character class
cvsdist b39f9d
		    (and
cvsdist b39f9d
		     (re-search-backward "^[ \t]*[^ \t#]" nil 'move)
cvsdist b39f9d
		     (forward-line 1))
cvsdist b39f9d
		  ;; no comment, so go back
cvsdist b39f9d
		  (goto-char start)))))))
cvsdist b39f9d
  (exchange-point-and-mark)
cvsdist b39f9d
  (py-keep-region-active))
cvsdist b39f9d
cvsdist b39f9d
;; ripped from cc-mode
cvsdist b39f9d
(defun py-forward-into-nomenclature (&optional arg)
cvsdist b39f9d
  "Move forward to end of a nomenclature section or word.
cvsdist b39f9d
With \\[universal-argument] (programmatically, optional argument ARG), 
cvsdist b39f9d
do it that many times.
cvsdist b39f9d
cvsdist b39f9d
A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores."
cvsdist b39f9d
  (interactive "p")
cvsdist b39f9d
  (let ((case-fold-search nil))
cvsdist b39f9d
    (if (> arg 0)
cvsdist b39f9d
	(re-search-forward
cvsdist b39f9d
	 "\\(\\W\\|[_]\\)*\\([A-Z]*[a-z0-9]*\\)"
cvsdist b39f9d
	 (point-max) t arg)
cvsdist b39f9d
      (while (and (< arg 0)
cvsdist b39f9d
		  (re-search-backward
cvsdist b39f9d
		   "\\(\\W\\|[a-z0-9]\\)[A-Z]+\\|\\(\\W\\|[_]\\)\\w+"
cvsdist b39f9d
		   (point-min) 0))
cvsdist b39f9d
	(forward-char 1)
cvsdist b39f9d
	(setq arg (1+ arg)))))
cvsdist b39f9d
  (py-keep-region-active))
cvsdist b39f9d
cvsdist b39f9d
(defun py-backward-into-nomenclature (&optional arg)
cvsdist b39f9d
  "Move backward to beginning of a nomenclature section or word.
cvsdist b39f9d
With optional ARG, move that many times.  If ARG is negative, move
cvsdist b39f9d
forward.
cvsdist b39f9d
cvsdist b39f9d
A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores."
cvsdist b39f9d
  (interactive "p")
cvsdist b39f9d
  (py-forward-into-nomenclature (- arg))
cvsdist b39f9d
  (py-keep-region-active))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist c08065
;; pdbtrack functions
cvsdist c08065
(defun py-pdbtrack-toggle-stack-tracking (arg)
cvsdist c08065
  (interactive "P")
cvsdist c08065
  (if (not (get-buffer-process (current-buffer)))
cvsdist c08065
      (error "No process associated with buffer '%s'" (current-buffer)))
cvsdist c08065
  ;; missing or 0 is toggle, >0 turn on, <0 turn off
cvsdist c08065
  (if (or (not arg)
cvsdist c08065
	  (zerop (setq arg (prefix-numeric-value arg))))
cvsdist c08065
      (setq py-pdbtrack-do-tracking-p (not py-pdbtrack-do-tracking-p))
cvsdist c08065
    (setq py-pdbtrack-do-tracking-p (> arg 0)))
cvsdist c08065
  (message "%sabled Python's pdbtrack"
cvsdist c08065
           (if py-pdbtrack-do-tracking-p "En" "Dis")))
cvsdist c08065
cvsdist c08065
(defun turn-on-pdbtrack ()
cvsdist c08065
  (interactive)
cvsdist c08065
  (py-pdbtrack-toggle-stack-tracking 1))
cvsdist c08065
cvsdist c08065
(defun turn-off-pdbtrack ()
cvsdist c08065
  (interactive)
cvsdist c08065
  (py-pdbtrack-toggle-stack-tracking 0))
cvsdist c08065
cvsdist c08065
cvsdist c08065

cvsdist b39f9d
;; Documentation functions
cvsdist b39f9d
cvsdist b39f9d
;; dump the long form of the mode blurb; does the usual doc escapes,
cvsdist b39f9d
;; plus lines of the form ^[vc]:name$ to suck variable & command docs
cvsdist b39f9d
;; out of the right places, along with the keys they're on & current
cvsdist b39f9d
;; values
cvsdist b39f9d
(defun py-dump-help-string (str)
cvsdist b39f9d
  (with-output-to-temp-buffer "*Help*"
cvsdist b39f9d
    (let ((locals (buffer-local-variables))
cvsdist b39f9d
	  funckind funcname func funcdoc
cvsdist b39f9d
	  (start 0) mstart end
cvsdist b39f9d
	  keys )
cvsdist b39f9d
      (while (string-match "^%\\([vc]\\):\\(.+\\)\n" str start)
cvsdist b39f9d
	(setq mstart (match-beginning 0)  end (match-end 0)
cvsdist b39f9d
	      funckind (substring str (match-beginning 1) (match-end 1))
cvsdist b39f9d
	      funcname (substring str (match-beginning 2) (match-end 2))
cvsdist b39f9d
	      func (intern funcname))
cvsdist b39f9d
	(princ (substitute-command-keys (substring str start mstart)))
cvsdist b39f9d
	(cond
cvsdist b39f9d
	 ((equal funckind "c")		; command
cvsdist b39f9d
	  (setq funcdoc (documentation func)
cvsdist b39f9d
		keys (concat
cvsdist b39f9d
		      "Key(s): "
cvsdist b39f9d
		      (mapconcat 'key-description
cvsdist b39f9d
				 (where-is-internal func py-mode-map)
cvsdist b39f9d
				 ", "))))
cvsdist b39f9d
	 ((equal funckind "v")		; variable
cvsdist b39f9d
	  (setq funcdoc (documentation-property func 'variable-documentation)
cvsdist b39f9d
		keys (if (assq func locals)
cvsdist b39f9d
			 (concat
cvsdist b39f9d
			  "Local/Global values: "
cvsdist b39f9d
			  (prin1-to-string (symbol-value func))
cvsdist b39f9d
			  " / "
cvsdist b39f9d
			  (prin1-to-string (default-value func)))
cvsdist b39f9d
		       (concat
cvsdist b39f9d
			"Value: "
cvsdist b39f9d
			(prin1-to-string (symbol-value func))))))
cvsdist b39f9d
	 (t				; unexpected
cvsdist b39f9d
	  (error "Error in py-dump-help-string, tag `%s'" funckind)))
cvsdist b39f9d
	(princ (format "\n-> %s:\t%s\t%s\n\n"
cvsdist b39f9d
		       (if (equal funckind "c") "Command" "Variable")
cvsdist b39f9d
		       funcname keys))
cvsdist b39f9d
	(princ funcdoc)
cvsdist b39f9d
	(terpri)
cvsdist b39f9d
	(setq start end))
cvsdist b39f9d
      (princ (substitute-command-keys (substring str start))))
cvsdist b39f9d
    (print-help-return-message)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-describe-mode ()
cvsdist b39f9d
  "Dump long form of Python-mode docs."
cvsdist b39f9d
  (interactive)
cvsdist b39f9d
  (py-dump-help-string "Major mode for editing Python files.
cvsdist b39f9d
Knows about Python indentation, tokens, comments and continuation lines.
cvsdist b39f9d
Paragraphs are separated by blank lines only.
cvsdist b39f9d
cvsdist b39f9d
Major sections below begin with the string `@'; specific function and
cvsdist b39f9d
variable docs begin with `->'.
cvsdist b39f9d
cvsdist b39f9d
@EXECUTING PYTHON CODE
cvsdist b39f9d
cvsdist b39f9d
\\[py-execute-import-or-reload]\timports or reloads the file in the Python interpreter
cvsdist b39f9d
\\[py-execute-buffer]\tsends the entire buffer to the Python interpreter
cvsdist b39f9d
\\[py-execute-region]\tsends the current region
cvsdist b39f9d
\\[py-execute-def-or-class]\tsends the current function or class definition
cvsdist b39f9d
\\[py-execute-string]\tsends an arbitrary string
cvsdist b39f9d
\\[py-shell]\tstarts a Python interpreter window; this will be used by
cvsdist b39f9d
\tsubsequent Python execution commands
cvsdist b39f9d
%c:py-execute-import-or-reload
cvsdist b39f9d
%c:py-execute-buffer
cvsdist b39f9d
%c:py-execute-region
cvsdist b39f9d
%c:py-execute-def-or-class
cvsdist b39f9d
%c:py-execute-string
cvsdist b39f9d
%c:py-shell
cvsdist b39f9d
cvsdist b39f9d
@VARIABLES
cvsdist b39f9d
cvsdist b39f9d
py-indent-offset\tindentation increment
cvsdist b39f9d
py-block-comment-prefix\tcomment string used by comment-region
cvsdist b39f9d
cvsdist b39f9d
py-python-command\tshell command to invoke Python interpreter
cvsdist b39f9d
py-temp-directory\tdirectory used for temp files (if needed)
cvsdist b39f9d
cvsdist b39f9d
py-beep-if-tab-change\tring the bell if tab-width is changed
cvsdist b39f9d
%v:py-indent-offset
cvsdist b39f9d
%v:py-block-comment-prefix
cvsdist b39f9d
%v:py-python-command
cvsdist b39f9d
%v:py-temp-directory
cvsdist b39f9d
%v:py-beep-if-tab-change
cvsdist b39f9d
cvsdist b39f9d
@KINDS OF LINES
cvsdist b39f9d
cvsdist b39f9d
Each physical line in the file is either a `continuation line' (the
cvsdist b39f9d
preceding line ends with a backslash that's not part of a comment, or
cvsdist b39f9d
the paren/bracket/brace nesting level at the start of the line is
cvsdist b39f9d
non-zero, or both) or an `initial line' (everything else).
cvsdist b39f9d
cvsdist b39f9d
An initial line is in turn a `blank line' (contains nothing except
cvsdist b39f9d
possibly blanks or tabs), a `comment line' (leftmost non-blank
cvsdist b39f9d
character is `#'), or a `code line' (everything else).
cvsdist b39f9d
cvsdist b39f9d
Comment Lines
cvsdist b39f9d
cvsdist b39f9d
Although all comment lines are treated alike by Python, Python mode
cvsdist b39f9d
recognizes two kinds that act differently with respect to indentation.
cvsdist b39f9d
cvsdist b39f9d
An `indenting comment line' is a comment line with a blank, tab or
cvsdist b39f9d
nothing after the initial `#'.  The indentation commands (see below)
cvsdist b39f9d
treat these exactly as if they were code lines: a line following an
cvsdist b39f9d
indenting comment line will be indented like the comment line.  All
cvsdist b39f9d
other comment lines (those with a non-whitespace character immediately
cvsdist b39f9d
following the initial `#') are `non-indenting comment lines', and
cvsdist b39f9d
their indentation is ignored by the indentation commands.
cvsdist b39f9d
cvsdist b39f9d
Indenting comment lines are by far the usual case, and should be used
cvsdist b39f9d
whenever possible.  Non-indenting comment lines are useful in cases
cvsdist b39f9d
like these:
cvsdist b39f9d
cvsdist b39f9d
\ta = b   # a very wordy single-line comment that ends up being
cvsdist b39f9d
\t        #... continued onto another line
cvsdist b39f9d
cvsdist b39f9d
\tif a == b:
cvsdist b39f9d
##\t\tprint 'panic!' # old code we've `commented out'
cvsdist b39f9d
\t\treturn a
cvsdist b39f9d
cvsdist b39f9d
Since the `#...' and `##' comment lines have a non-whitespace
cvsdist b39f9d
character following the initial `#', Python mode ignores them when
cvsdist b39f9d
computing the proper indentation for the next line.
cvsdist b39f9d
cvsdist b39f9d
Continuation Lines and Statements
cvsdist b39f9d
cvsdist b39f9d
The Python-mode commands generally work on statements instead of on
cvsdist b39f9d
individual lines, where a `statement' is a comment or blank line, or a
cvsdist b39f9d
code line and all of its following continuation lines (if any)
cvsdist b39f9d
considered as a single logical unit.  The commands in this mode
cvsdist b39f9d
generally (when it makes sense) automatically move to the start of the
cvsdist b39f9d
statement containing point, even if point happens to be in the middle
cvsdist b39f9d
of some continuation line.
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d
@INDENTATION
cvsdist b39f9d
cvsdist b39f9d
Primarily for entering new code:
cvsdist b39f9d
\t\\[indent-for-tab-command]\t indent line appropriately
cvsdist b39f9d
\t\\[py-newline-and-indent]\t insert newline, then indent
cvsdist b39f9d
\t\\[py-electric-backspace]\t reduce indentation, or delete single character
cvsdist b39f9d
cvsdist b39f9d
Primarily for reindenting existing code:
cvsdist b39f9d
\t\\[py-guess-indent-offset]\t guess py-indent-offset from file content; change locally
cvsdist b39f9d
\t\\[universal-argument] \\[py-guess-indent-offset]\t ditto, but change globally
cvsdist b39f9d
cvsdist b39f9d
\t\\[py-indent-region]\t reindent region to match its context
cvsdist b39f9d
\t\\[py-shift-region-left]\t shift region left by py-indent-offset
cvsdist b39f9d
\t\\[py-shift-region-right]\t shift region right by py-indent-offset
cvsdist b39f9d
cvsdist b39f9d
Unlike most programming languages, Python uses indentation, and only
cvsdist b39f9d
indentation, to specify block structure.  Hence the indentation supplied
cvsdist b39f9d
automatically by Python-mode is just an educated guess:  only you know
cvsdist b39f9d
the block structure you intend, so only you can supply correct
cvsdist b39f9d
indentation.
cvsdist b39f9d
cvsdist b39f9d
The \\[indent-for-tab-command] and \\[py-newline-and-indent] keys try to suggest plausible indentation, based on
cvsdist b39f9d
the indentation of preceding statements.  E.g., assuming
cvsdist b39f9d
py-indent-offset is 4, after you enter
cvsdist b39f9d
\tif a > 0: \\[py-newline-and-indent]
cvsdist b39f9d
the cursor will be moved to the position of the `_' (_ is not a
cvsdist b39f9d
character in the file, it's just used here to indicate the location of
cvsdist b39f9d
the cursor):
cvsdist b39f9d
\tif a > 0:
cvsdist b39f9d
\t    _
cvsdist b39f9d
If you then enter `c = d' \\[py-newline-and-indent], the cursor will move
cvsdist b39f9d
to
cvsdist b39f9d
\tif a > 0:
cvsdist b39f9d
\t    c = d
cvsdist b39f9d
\t    _
cvsdist b39f9d
Python-mode cannot know whether that's what you intended, or whether
cvsdist b39f9d
\tif a > 0:
cvsdist b39f9d
\t    c = d
cvsdist b39f9d
\t_
cvsdist b39f9d
was your intent.  In general, Python-mode either reproduces the
cvsdist b39f9d
indentation of the (closest code or indenting-comment) preceding
cvsdist b39f9d
statement, or adds an extra py-indent-offset blanks if the preceding
cvsdist b39f9d
statement has `:' as its last significant (non-whitespace and non-
cvsdist b39f9d
comment) character.  If the suggested indentation is too much, use
cvsdist b39f9d
\\[py-electric-backspace] to reduce it.
cvsdist b39f9d
cvsdist b39f9d
Continuation lines are given extra indentation.  If you don't like the
cvsdist b39f9d
suggested indentation, change it to something you do like, and Python-
cvsdist b39f9d
mode will strive to indent later lines of the statement in the same way.
cvsdist b39f9d
cvsdist b39f9d
If a line is a continuation line by virtue of being in an unclosed
cvsdist b39f9d
paren/bracket/brace structure (`list', for short), the suggested
cvsdist b39f9d
indentation depends on whether the current line contains the first item
cvsdist b39f9d
in the list.  If it does, it's indented py-indent-offset columns beyond
cvsdist b39f9d
the indentation of the line containing the open bracket.  If you don't
cvsdist b39f9d
like that, change it by hand.  The remaining items in the list will mimic
cvsdist b39f9d
whatever indentation you give to the first item.
cvsdist b39f9d
cvsdist b39f9d
If a line is a continuation line because the line preceding it ends with
cvsdist b39f9d
a backslash, the third and following lines of the statement inherit their
cvsdist b39f9d
indentation from the line preceding them.  The indentation of the second
cvsdist b39f9d
line in the statement depends on the form of the first (base) line:  if
cvsdist b39f9d
the base line is an assignment statement with anything more interesting
cvsdist b39f9d
than the backslash following the leftmost assigning `=', the second line
cvsdist b39f9d
is indented two columns beyond that `='.  Else it's indented to two
cvsdist b39f9d
columns beyond the leftmost solid chunk of non-whitespace characters on
cvsdist b39f9d
the base line.
cvsdist b39f9d
cvsdist b39f9d
Warning:  indent-region should not normally be used!  It calls \\[indent-for-tab-command]
cvsdist b39f9d
repeatedly, and as explained above, \\[indent-for-tab-command] can't guess the block
cvsdist b39f9d
structure you intend.
cvsdist b39f9d
%c:indent-for-tab-command
cvsdist b39f9d
%c:py-newline-and-indent
cvsdist b39f9d
%c:py-electric-backspace
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d
The next function may be handy when editing code you didn't write:
cvsdist b39f9d
%c:py-guess-indent-offset
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d
The remaining `indent' functions apply to a region of Python code.  They
cvsdist b39f9d
assume the block structure (equals indentation, in Python) of the region
cvsdist b39f9d
is correct, and alter the indentation in various ways while preserving
cvsdist b39f9d
the block structure:
cvsdist b39f9d
%c:py-indent-region
cvsdist b39f9d
%c:py-shift-region-left
cvsdist b39f9d
%c:py-shift-region-right
cvsdist b39f9d
cvsdist b39f9d
@MARKING & MANIPULATING REGIONS OF CODE
cvsdist b39f9d
cvsdist b39f9d
\\[py-mark-block]\t mark block of lines
cvsdist b39f9d
\\[py-mark-def-or-class]\t mark smallest enclosing def
cvsdist b39f9d
\\[universal-argument] \\[py-mark-def-or-class]\t mark smallest enclosing class
cvsdist b39f9d
\\[comment-region]\t comment out region of code
cvsdist b39f9d
\\[universal-argument] \\[comment-region]\t uncomment region of code
cvsdist b39f9d
%c:py-mark-block
cvsdist b39f9d
%c:py-mark-def-or-class
cvsdist b39f9d
%c:comment-region
cvsdist b39f9d
cvsdist b39f9d
@MOVING POINT
cvsdist b39f9d
cvsdist b39f9d
\\[py-previous-statement]\t move to statement preceding point
cvsdist b39f9d
\\[py-next-statement]\t move to statement following point
cvsdist b39f9d
\\[py-goto-block-up]\t move up to start of current block
cvsdist b39f9d
\\[py-beginning-of-def-or-class]\t move to start of def
cvsdist b39f9d
\\[universal-argument] \\[py-beginning-of-def-or-class]\t move to start of class
cvsdist b39f9d
\\[py-end-of-def-or-class]\t move to end of def
cvsdist b39f9d
\\[universal-argument] \\[py-end-of-def-or-class]\t move to end of class
cvsdist b39f9d
cvsdist b39f9d
The first two move to one statement beyond the statement that contains
cvsdist b39f9d
point.  A numeric prefix argument tells them to move that many
cvsdist b39f9d
statements instead.  Blank lines, comment lines, and continuation lines
cvsdist b39f9d
do not count as `statements' for these commands.  So, e.g., you can go
cvsdist b39f9d
to the first code statement in a file by entering
cvsdist b39f9d
\t\\[beginning-of-buffer]\t to move to the top of the file
cvsdist b39f9d
\t\\[py-next-statement]\t to skip over initial comments and blank lines
cvsdist b39f9d
Or do `\\[py-previous-statement]' with a huge prefix argument.
cvsdist b39f9d
%c:py-previous-statement
cvsdist b39f9d
%c:py-next-statement
cvsdist b39f9d
%c:py-goto-block-up
cvsdist b39f9d
%c:py-beginning-of-def-or-class
cvsdist b39f9d
%c:py-end-of-def-or-class
cvsdist b39f9d
cvsdist b39f9d
@LITTLE-KNOWN EMACS COMMANDS PARTICULARLY USEFUL IN PYTHON MODE
cvsdist b39f9d
cvsdist b39f9d
`\\[indent-new-comment-line]' is handy for entering a multi-line comment.
cvsdist b39f9d
cvsdist b39f9d
`\\[set-selective-display]' with a `small' prefix arg is ideally suited for viewing the
cvsdist b39f9d
overall class and def structure of a module.
cvsdist b39f9d
cvsdist b39f9d
`\\[back-to-indentation]' moves point to a line's first non-blank character.
cvsdist b39f9d
cvsdist b39f9d
`\\[indent-relative]' is handy for creating odd indentation.
cvsdist b39f9d
cvsdist b39f9d
@OTHER EMACS HINTS
cvsdist b39f9d
cvsdist b39f9d
If you don't like the default value of a variable, change its value to
cvsdist b39f9d
whatever you do like by putting a `setq' line in your .emacs file.
cvsdist b39f9d
E.g., to set the indentation increment to 4, put this line in your
cvsdist b39f9d
.emacs:
cvsdist b39f9d
\t(setq  py-indent-offset  4)
cvsdist b39f9d
To see the value of a variable, do `\\[describe-variable]' and enter the variable
cvsdist b39f9d
name at the prompt.
cvsdist b39f9d
cvsdist b39f9d
When entering a key sequence like `C-c C-n', it is not necessary to
cvsdist b39f9d
release the CONTROL key after doing the `C-c' part -- it suffices to
cvsdist b39f9d
press the CONTROL key, press and release `c' (while still holding down
cvsdist b39f9d
CONTROL), press and release `n' (while still holding down CONTROL), &
cvsdist b39f9d
then release CONTROL.
cvsdist b39f9d
cvsdist b39f9d
Entering Python mode calls with no arguments the value of the variable
cvsdist b39f9d
`python-mode-hook', if that value exists and is not nil; for backward
cvsdist b39f9d
compatibility it also tries `py-mode-hook'; see the `Hooks' section of
cvsdist b39f9d
the Elisp manual for details.
cvsdist b39f9d
cvsdist b39f9d
Obscure:  When python-mode is first loaded, it looks for all bindings
cvsdist b39f9d
to newline-and-indent in the global keymap, and shadows them with
cvsdist b39f9d
local bindings to py-newline-and-indent."))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
;; Helper functions
cvsdist b39f9d
(defvar py-parse-state-re
cvsdist b39f9d
  (concat
cvsdist b39f9d
   "^[ \t]*\\(if\\|elif\\|else\\|while\\|def\\|class\\)\\>"
cvsdist b39f9d
   "\\|"
cvsdist b39f9d
   "^[^ #\t\n]"))
cvsdist b39f9d
cvsdist b39f9d
(defun py-parse-state ()
cvsdist b39f9d
  "Return the parse state at point (see `parse-partial-sexp' docs)."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (let ((here (point))
cvsdist c08065
	  in-listcomp pps done)
cvsdist b39f9d
      (while (not done)
cvsdist b39f9d
	;; back up to the first preceding line (if any; else start of
cvsdist b39f9d
	;; buffer) that begins with a popular Python keyword, or a
cvsdist b39f9d
	;; non- whitespace and non-comment character.  These are good
cvsdist b39f9d
	;; places to start parsing to see whether where we started is
cvsdist b39f9d
	;; at a non-zero nesting level.  It may be slow for people who
cvsdist b39f9d
	;; write huge code blocks or huge lists ... tough beans.
cvsdist b39f9d
	(re-search-backward py-parse-state-re nil 'move)
cvsdist c08065
	;; Watch out for landing inside a list comprehension
cvsdist c08065
	(save-excursion
cvsdist c08065
	  (if (and (looking-at "[ \t]*\\<\\(if\\|for\\)\\>")
cvsdist c08065
		   (py-safe (progn (up-list -1) t))
cvsdist c08065
		   (eq (char-after) ?\[))
cvsdist c08065
	      (setq in-listcomp (point))
cvsdist c08065
	    (setq in-listcomp nil)))
cvsdist b39f9d
	(beginning-of-line)
cvsdist b39f9d
	;; In XEmacs, we have a much better way to test for whether
cvsdist b39f9d
	;; we're in a triple-quoted string or not.  Emacs does not
cvsdist b39f9d
	;; have this built-in function, which is its loss because
cvsdist b39f9d
	;; without scanning from the beginning of the buffer, there's
cvsdist b39f9d
	;; no accurate way to determine this otherwise.
cvsdist b39f9d
	(if (not (fboundp 'buffer-syntactic-context))
cvsdist b39f9d
	    ;; Emacs
cvsdist b39f9d
	    (progn
cvsdist b39f9d
	      (save-excursion (setq pps (parse-partial-sexp (point) here)))
cvsdist b39f9d
	      ;; make sure we don't land inside a triple-quoted string
cvsdist b39f9d
	      (setq done (or (not (nth 3 pps))
cvsdist b39f9d
			     (bobp)))
cvsdist b39f9d
	      ;; Just go ahead and short circuit the test back to the
cvsdist b39f9d
	      ;; beginning of the buffer.  This will be slow, but not
cvsdist b39f9d
	      ;; nearly as slow as looping through many
cvsdist b39f9d
	      ;; re-search-backwards.
cvsdist b39f9d
	      (if (not done)
cvsdist b39f9d
		  (goto-char (point-min))))
cvsdist b39f9d
	  ;; XEmacs
cvsdist b39f9d
	  (setq done (or (not (buffer-syntactic-context))
cvsdist b39f9d
			 (bobp)))
cvsdist c08065
	  (when in-listcomp
cvsdist c08065
	    (goto-char in-listcomp)
cvsdist c08065
	    (setq done nil))
cvsdist b39f9d
	  (when done
cvsdist b39f9d
	    (setq pps (parse-partial-sexp (point) here)))
cvsdist b39f9d
	  ))
cvsdist b39f9d
      pps)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-nesting-level ()
cvsdist b39f9d
  "Return the buffer position of the last unclosed enclosing list.
cvsdist b39f9d
If nesting level is zero, return nil."
cvsdist b39f9d
  (let ((status (py-parse-state)))
cvsdist b39f9d
    (if (zerop (car status))
cvsdist b39f9d
	nil				; not in a nest
cvsdist b39f9d
      (car (cdr status)))))		; char# of open bracket
cvsdist b39f9d
cvsdist b39f9d
(defun py-backslash-continuation-line-p ()
cvsdist b39f9d
  "Return t iff preceding line ends with backslash that is not in a comment."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (beginning-of-line)
cvsdist b39f9d
    (and
cvsdist b39f9d
     ;; use a cheap test first to avoid the regexp if possible
cvsdist b39f9d
     ;; use 'eq' because char-after may return nil
cvsdist b39f9d
     (eq (char-after (- (point) 2)) ?\\ )
cvsdist b39f9d
     ;; make sure; since eq test passed, there is a preceding line
cvsdist b39f9d
     (forward-line -1)			; always true -- side effect
cvsdist b39f9d
     (looking-at py-continued-re))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-continuation-line-p ()
cvsdist b39f9d
  "Return t iff current line is a continuation line."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (beginning-of-line)
cvsdist b39f9d
    (or (py-backslash-continuation-line-p)
cvsdist b39f9d
	(py-nesting-level))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-goto-beginning-of-tqs (delim)
cvsdist b39f9d
  "Go to the beginning of the triple quoted string we find ourselves in.
cvsdist b39f9d
DELIM is the TQS string delimiter character we're searching backwards
cvsdist b39f9d
for."
cvsdist c08065
  (let ((skip (and delim (make-string 1 delim)))
cvsdist c08065
	(continue t))
cvsdist b39f9d
    (when skip
cvsdist b39f9d
      (save-excursion
cvsdist c08065
	(while continue
cvsdist c08065
	  (py-safe (search-backward skip))
cvsdist c08065
	  (setq continue (and (not (bobp))
cvsdist c08065
			      (= (char-before) ?\\))))
cvsdist c08065
	(if (and (= (char-before) delim)
cvsdist c08065
		 (= (char-before (1- (point))) delim))
cvsdist b39f9d
	    (setq skip (make-string 3 delim))))
cvsdist b39f9d
      ;; we're looking at a triple-quoted string
cvsdist b39f9d
      (py-safe (search-backward skip)))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-goto-initial-line ()
cvsdist b39f9d
  "Go to the initial line of the current statement.
cvsdist b39f9d
Usually this is the line we're on, but if we're on the 2nd or
cvsdist b39f9d
following lines of a continuation block, we need to go up to the first
cvsdist b39f9d
line of the block."
cvsdist b39f9d
  ;; Tricky: We want to avoid quadratic-time behavior for long
cvsdist b39f9d
  ;; continued blocks, whether of the backslash or open-bracket
cvsdist b39f9d
  ;; varieties, or a mix of the two.  The following manages to do that
cvsdist b39f9d
  ;; in the usual cases.
cvsdist b39f9d
  ;;
cvsdist b39f9d
  ;; Also, if we're sitting inside a triple quoted string, this will
cvsdist b39f9d
  ;; drop us at the line that begins the string.
cvsdist b39f9d
  (let (open-bracket-pos)
cvsdist b39f9d
    (while (py-continuation-line-p)
cvsdist b39f9d
      (beginning-of-line)
cvsdist b39f9d
      (if (py-backslash-continuation-line-p)
cvsdist b39f9d
	  (while (py-backslash-continuation-line-p)
cvsdist b39f9d
	    (forward-line -1))
cvsdist b39f9d
	;; else zip out of nested brackets/braces/parens
cvsdist b39f9d
	(while (setq open-bracket-pos (py-nesting-level))
cvsdist b39f9d
	  (goto-char open-bracket-pos)))))
cvsdist b39f9d
  (beginning-of-line))
cvsdist b39f9d
cvsdist b39f9d
(defun py-goto-beyond-final-line ()
cvsdist b39f9d
  "Go to the point just beyond the fine line of the current statement.
cvsdist b39f9d
Usually this is the start of the next line, but if this is a
cvsdist b39f9d
multi-line statement we need to skip over the continuation lines."
cvsdist b39f9d
  ;; Tricky: Again we need to be clever to avoid quadratic time
cvsdist b39f9d
  ;; behavior.
cvsdist b39f9d
  ;;
cvsdist b39f9d
  ;; XXX: Not quite the right solution, but deals with multi-line doc
cvsdist b39f9d
  ;; strings
cvsdist b39f9d
  (if (looking-at (concat "[ \t]*\\(" py-stringlit-re "\\)"))
cvsdist b39f9d
      (goto-char (match-end 0)))
cvsdist b39f9d
  ;;
cvsdist b39f9d
  (forward-line 1)
cvsdist b39f9d
  (let (state)
cvsdist b39f9d
    (while (and (py-continuation-line-p)
cvsdist b39f9d
		(not (eobp)))
cvsdist b39f9d
      ;; skip over the backslash flavor
cvsdist b39f9d
      (while (and (py-backslash-continuation-line-p)
cvsdist b39f9d
		  (not (eobp)))
cvsdist b39f9d
	(forward-line 1))
cvsdist b39f9d
      ;; if in nest, zip to the end of the nest
cvsdist b39f9d
      (setq state (py-parse-state))
cvsdist b39f9d
      (if (and (not (zerop (car state)))
cvsdist b39f9d
	       (not (eobp)))
cvsdist b39f9d
	  (progn
cvsdist b39f9d
	    (parse-partial-sexp (point) (point-max) 0 nil state)
cvsdist b39f9d
	    (forward-line 1))))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-statement-opens-block-p ()
cvsdist b39f9d
  "Return t iff the current statement opens a block.
cvsdist b39f9d
I.e., iff it ends with a colon that is not in a comment.  Point should 
cvsdist b39f9d
be at the start of a statement."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (let ((start (point))
cvsdist b39f9d
	  (finish (progn (py-goto-beyond-final-line) (1- (point))))
cvsdist b39f9d
	  (searching t)
cvsdist b39f9d
	  (answer nil)
cvsdist b39f9d
	  state)
cvsdist b39f9d
      (goto-char start)
cvsdist b39f9d
      (while searching
cvsdist b39f9d
	;; look for a colon with nothing after it except whitespace, and
cvsdist b39f9d
	;; maybe a comment
cvsdist b39f9d
	(if (re-search-forward ":\\([ \t]\\|\\\\\n\\)*\\(#.*\\)?$"
cvsdist b39f9d
			       finish t)
cvsdist b39f9d
	    (if (eq (point) finish)	; note: no `else' clause; just
cvsdist b39f9d
					; keep searching if we're not at
cvsdist b39f9d
					; the end yet
cvsdist b39f9d
		;; sure looks like it opens a block -- but it might
cvsdist b39f9d
		;; be in a comment
cvsdist b39f9d
		(progn
cvsdist b39f9d
		  (setq searching nil)	; search is done either way
cvsdist b39f9d
		  (setq state (parse-partial-sexp start
cvsdist b39f9d
						  (match-beginning 0)))
cvsdist b39f9d
		  (setq answer (not (nth 4 state)))))
cvsdist b39f9d
	  ;; search failed: couldn't find another interesting colon
cvsdist b39f9d
	  (setq searching nil)))
cvsdist b39f9d
      answer)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-statement-closes-block-p ()
cvsdist b39f9d
  "Return t iff the current statement closes a block.
cvsdist b39f9d
I.e., if the line starts with `return', `raise', `break', `continue',
cvsdist b39f9d
and `pass'.  This doesn't catch embedded statements."
cvsdist b39f9d
  (let ((here (point)))
cvsdist b39f9d
    (py-goto-initial-line)
cvsdist b39f9d
    (back-to-indentation)
cvsdist b39f9d
    (prog1
cvsdist b39f9d
	(looking-at (concat py-block-closing-keywords-re "\\>"))
cvsdist b39f9d
      (goto-char here))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-goto-beyond-block ()
cvsdist b39f9d
  "Go to point just beyond the final line of block begun by the current line.
cvsdist b39f9d
This is the same as where `py-goto-beyond-final-line' goes unless
cvsdist b39f9d
we're on colon line, in which case we go to the end of the block.
cvsdist b39f9d
Assumes point is at the beginning of the line."
cvsdist b39f9d
  (if (py-statement-opens-block-p)
cvsdist b39f9d
      (py-mark-block nil 'just-move)
cvsdist b39f9d
    (py-goto-beyond-final-line)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-goto-statement-at-or-above ()
cvsdist b39f9d
  "Go to the start of the first statement at or preceding point.
cvsdist b39f9d
Return t if there is such a statement, otherwise nil.  `Statement'
cvsdist b39f9d
does not include blank lines, comments, or continuation lines."
cvsdist b39f9d
  (py-goto-initial-line)
cvsdist b39f9d
  (if (looking-at py-blank-or-comment-re)
cvsdist b39f9d
      ;; skip back over blank & comment lines
cvsdist b39f9d
      ;; note:  will skip a blank or comment line that happens to be
cvsdist b39f9d
      ;; a continuation line too
cvsdist b39f9d
      (if (re-search-backward "^[ \t]*[^ \t#\n]" nil t)
cvsdist b39f9d
	  (progn (py-goto-initial-line) t)
cvsdist b39f9d
	nil)
cvsdist b39f9d
    t))
cvsdist b39f9d
cvsdist b39f9d
(defun py-goto-statement-below ()
cvsdist b39f9d
  "Go to start of the first statement following the statement containing point.
cvsdist b39f9d
Return t if there is such a statement, otherwise nil.  `Statement'
cvsdist b39f9d
does not include blank lines, comments, or continuation lines."
cvsdist b39f9d
  (beginning-of-line)
cvsdist b39f9d
  (let ((start (point)))
cvsdist b39f9d
    (py-goto-beyond-final-line)
cvsdist b39f9d
    (while (and
cvsdist b39f9d
	    (looking-at py-blank-or-comment-re)
cvsdist b39f9d
	    (not (eobp)))
cvsdist b39f9d
      (forward-line 1))
cvsdist b39f9d
    (if (eobp)
cvsdist b39f9d
	(progn (goto-char start) nil)
cvsdist b39f9d
      t)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-go-up-tree-to-keyword (key)
cvsdist b39f9d
  "Go to begining of statement starting with KEY, at or preceding point.
cvsdist b39f9d
cvsdist b39f9d
KEY is a regular expression describing a Python keyword.  Skip blank
cvsdist b39f9d
lines and non-indenting comments.  If the statement found starts with
cvsdist b39f9d
KEY, then stop, otherwise go back to first enclosing block starting
cvsdist b39f9d
with KEY.  If successful, leave point at the start of the KEY line and 
cvsdist b39f9d
return t.  Otherwise, leav point at an undefined place and return nil."
cvsdist b39f9d
  ;; skip blanks and non-indenting #
cvsdist b39f9d
  (py-goto-initial-line)
cvsdist b39f9d
  (while (and
cvsdist b39f9d
	  (looking-at "[ \t]*\\($\\|#[^ \t\n]\\)")
cvsdist b39f9d
	  (zerop (forward-line -1)))	; go back
cvsdist b39f9d
    nil)
cvsdist b39f9d
  (py-goto-initial-line)
cvsdist b39f9d
  (let* ((re (concat "[ \t]*" key "\\b"))
cvsdist b39f9d
	 (case-fold-search nil)		; let* so looking-at sees this
cvsdist b39f9d
	 (found (looking-at re))
cvsdist b39f9d
	 (dead nil))
cvsdist b39f9d
    (while (not (or found dead))
cvsdist b39f9d
      (condition-case nil		; in case no enclosing block
cvsdist b39f9d
	  (py-goto-block-up 'no-mark)
cvsdist b39f9d
	(error (setq dead t)))
cvsdist b39f9d
      (or dead (setq found (looking-at re))))
cvsdist b39f9d
    (beginning-of-line)
cvsdist b39f9d
    found))
cvsdist b39f9d
cvsdist b39f9d
(defun py-suck-up-leading-text ()
cvsdist b39f9d
  "Return string in buffer from start of indentation to end of line.
cvsdist b39f9d
Prefix with \"...\" if leading whitespace was skipped."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (back-to-indentation)
cvsdist b39f9d
    (concat
cvsdist b39f9d
     (if (bolp) "" "...")
cvsdist b39f9d
     (buffer-substring (point) (progn (end-of-line) (point))))))
cvsdist b39f9d
cvsdist b39f9d
(defun py-suck-up-first-keyword ()
cvsdist b39f9d
  "Return first keyword on the line as a Lisp symbol.
cvsdist b39f9d
`Keyword' is defined (essentially) as the regular expression
cvsdist b39f9d
([a-z]+).  Returns nil if none was found."
cvsdist b39f9d
  (let ((case-fold-search nil))
cvsdist b39f9d
    (if (looking-at "[ \t]*\\([a-z]+\\)\\b")
cvsdist b39f9d
	(intern (buffer-substring (match-beginning 1) (match-end 1)))
cvsdist b39f9d
      nil)))
cvsdist b39f9d
cvsdist b39f9d
(defun py-current-defun ()
cvsdist b39f9d
  "Python value for `add-log-current-defun-function'.
cvsdist b39f9d
This tells add-log.el how to find the current function/method/variable."
cvsdist b39f9d
  (save-excursion
cvsdist b39f9d
    (if (re-search-backward py-defun-start-re nil t)
cvsdist b39f9d
	(or (match-string 3)
cvsdist b39f9d
	    (let ((method (match-string 2)))
cvsdist b39f9d
	      (if (and (not (zerop (length (match-string 1))))
cvsdist b39f9d
		       (re-search-backward py-class-start-re nil t))
cvsdist b39f9d
		  (concat (match-string 1) "." method)
cvsdist b39f9d
		method)))
cvsdist b39f9d
      nil)))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
(defconst py-help-address "python-mode@python.org"
cvsdist b39f9d
  "Address accepting submission of bug reports.")
cvsdist b39f9d
cvsdist b39f9d
(defun py-version ()
cvsdist b39f9d
  "Echo the current version of `python-mode' in the minibuffer."
cvsdist b39f9d
  (interactive)
cvsdist b39f9d
  (message "Using `python-mode' version %s" py-version)
cvsdist b39f9d
  (py-keep-region-active))
cvsdist b39f9d
cvsdist b39f9d
;; only works under Emacs 19
cvsdist b39f9d
;(eval-when-compile
cvsdist b39f9d
;  (require 'reporter))
cvsdist b39f9d
cvsdist b39f9d
(defun py-submit-bug-report (enhancement-p)
cvsdist b39f9d
  "Submit via mail a bug report on `python-mode'.
cvsdist b39f9d
With \\[universal-argument] (programmatically, argument ENHANCEMENT-P
cvsdist b39f9d
non-nil) just submit an enhancement request."
cvsdist b39f9d
  (interactive
cvsdist b39f9d
   (list (not (y-or-n-p
cvsdist b39f9d
	       "Is this a bug report (hit `n' to send other comments)? "))))
cvsdist b39f9d
  (let ((reporter-prompt-for-summary-p (if enhancement-p
cvsdist b39f9d
					   "(Very) brief summary: "
cvsdist b39f9d
					 t)))
cvsdist b39f9d
    (require 'reporter)
cvsdist b39f9d
    (reporter-submit-bug-report
cvsdist b39f9d
     py-help-address			;address
cvsdist b39f9d
     (concat "python-mode " py-version)	;pkgname
cvsdist b39f9d
     ;; varlist
cvsdist b39f9d
     (if enhancement-p nil
cvsdist b39f9d
       '(py-python-command
cvsdist b39f9d
	 py-indent-offset
cvsdist b39f9d
	 py-block-comment-prefix
cvsdist b39f9d
	 py-temp-directory
cvsdist b39f9d
	 py-beep-if-tab-change))
cvsdist b39f9d
     nil				;pre-hooks
cvsdist b39f9d
     nil				;post-hooks
cvsdist b39f9d
     "Dear Barry,")			;salutation
cvsdist b39f9d
    (if enhancement-p nil
cvsdist b39f9d
      (set-mark (point))
cvsdist b39f9d
      (insert 
cvsdist b39f9d
"Please replace this text with a sufficiently large code sample\n\
cvsdist b39f9d
and an exact recipe so that I can reproduce your problem.  Failure\n\
cvsdist b39f9d
to do so may mean a greater delay in fixing your bug.\n\n")
cvsdist b39f9d
      (exchange-point-and-mark)
cvsdist b39f9d
      (py-keep-region-active))))
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
(defun py-kill-emacs-hook ()
cvsdist b39f9d
  "Delete files in `py-file-queue'.
cvsdist b39f9d
These are Python temporary files awaiting execution."
cvsdist b39f9d
  (mapcar #'(lambda (filename)
cvsdist b39f9d
	      (py-safe (delete-file filename)))
cvsdist b39f9d
	  py-file-queue))
cvsdist b39f9d
cvsdist b39f9d
;; arrange to kill temp files when Emacs exists
cvsdist b39f9d
(add-hook 'kill-emacs-hook 'py-kill-emacs-hook)
cvsdist c08065
(add-hook 'comint-output-filter-functions 'py-pdbtrack-track-stack-file)
cvsdist c08065
cvsdist c08065
;; Add a designator to the minor mode strings
cvsdist c08065
(or (assq 'py-pdbtrack-minor-mode-string minor-mode-alist)
cvsdist c08065
    (push '(py-pdbtrack-is-tracking-p py-pdbtrack-minor-mode-string)
cvsdist c08065
	  minor-mode-alist))
cvsdist b39f9d
cvsdist b39f9d
cvsdist b39f9d

cvsdist b39f9d
(provide 'python-mode)
cvsdist b39f9d
;;; python-mode.el ends here