diff .elisp/doctest-mode.el @ 0:c30d68fbd368

Initial import from svn.
author Augie Fackler <durin42@gmail.com>
date Wed, 26 Nov 2008 10:56:09 -0600
parents
children b5d75594b356
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/.elisp/doctest-mode.el
@@ -0,0 +1,2061 @@
+;;; doctest-mode.el --- Major mode for editing Python doctest files
+
+;; Copyright (C) 2004-2007  Edward Loper
+
+;; Author:     Edward Loper
+;; Maintainer: edloper@alum.mit.edu
+;; Created:    Aug 2004
+;; Keywords:   python doctest unittest test docstring
+
+(defconst doctest-version "0.5 alpha"
+  "`doctest-mode' version number.")
+
+;; This software is provided as-is, without express or implied
+;; warranty.  Permission to use, copy, modify, distribute or sell this
+;; software, without fee, for any purpose and by any individual or
+;; organization, is hereby granted, provided that the above copyright
+;; notice and this paragraph appear in all copies.
+
+;; This is a major mode for editing text files that contain Python
+;; doctest examples.  Doctest is a testing framework for Python that
+;; emulates an interactive session, and checks the result of each
+;; command.  For more information, see the Python library reference:
+;; <http://docs.python.org/lib/module-doctest.html>
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Table of Contents
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;  1. Customization: use-editable variables to customize
+;;     doctest-mode.
+;;
+;;  2. Fonts: defines new font-lock faces.
+;;
+;;  3. Constants: various consts (mainly regexps) used by the rest
+;;     of the code.
+;;
+;;  4. Syntax Highlighting: defines variables and functions used by
+;;     font-lock-mode to perform syntax highlighting.
+;;
+;;  5. Source code editing & indentation: commands used to
+;;     automatically indent, dedent, & handle prompts.
+;;
+;;  6. Code Execution: commands used to start doctest processes,
+;;     and handle their output.
+;;
+;;  7. Markers: functions used to insert markers at the start of
+;;     doctest examples.  These are used to keep track of the
+;;     correspondence between examples in the source buffer and
+;;     results in the output buffer.
+;;
+;;  8. Navigation: commands used to navigate between failed examples.
+;;
+;;  9. Replace Output: command used to replace a doctest example's
+;;     expected output with its actual output.
+;;
+;; 10. Helper functions: various helper functions used by the rest
+;;     of the code.
+;;
+;; 11. Emacs compatibility functions: defines compatible versions of
+;;     functions that are defined for some versions of emacs but not
+;;     others.
+;;
+;; 12. Doctest Results Mode: defines doctest-results-mode, which is
+;;     used for the output generated by doctest.
+;;
+;; 13. Doctest Mode: defines doctest-mode itself.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Customizable Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defgroup doctest nil
+  "Support for the Python doctest framework"
+  :group 'languages
+  :prefix "doctest-")
+
+(defcustom doctest-default-margin 4
+  "The default pre-prompt margin for doctest examples."
+  :type 'integer
+  :group 'doctest)
+
+(defcustom doctest-avoid-trailing-whitespace t
+  "If true, then delete trailing whitespace when inserting a newline."
+  :type 'boolean
+  :group 'doctest)
+
+(defcustom doctest-temp-directory
+  (let ((ok '(lambda (x)
+	       (and x
+		    (setq x (expand-file-name x)) ; always true
+		    (file-directory-p x)
+		    (file-writable-p x)
+		    x))))
+    (or (funcall ok (getenv "TMPDIR"))
+	(funcall ok "/usr/tmp")
+	(funcall ok "/tmp")
+	(funcall ok "/var/tmp")
+	(funcall ok  ".")
+	(error (concat "Couldn't find a usable temp directory -- "
+		       "set `doctest-temp-directory'"))))	 
+  "Directory used for temporary files created when running doctest.
+By default, the first directory from this list that exists and that you
+can write into: the value (if any) of the environment variable TMPDIR,
+/usr/tmp, /tmp, /var/tmp, or the current directory."
+  :type 'string
+  :group 'doctest)
+
+(defcustom doctest-hide-example-source nil
+  "If true, then don't display the example source code for each 
+failure in the results buffer."
+  :type 'boolean
+  :group 'doctest)
+
+(defcustom doctest-python-command "python"
+  "Shell command used to start the python interpreter"
+  :type 'string
+  :group 'doctest)
+
+(defcustom doctest-results-buffer-name "*doctest-output (%N)*"
+  "The name of the buffer used to store the output of the doctest
+command.  This name can contain the following special sequences:
+  %n -- replaced by the doctest buffer's name.
+  %N -- replaced by the doctest buffer's name, with '.doctest'
+        stripped off.
+  %f -- replaced by the doctest buffer's filename."
+  :type 'string
+  :group 'doctest)
+
+(defcustom doctest-optionflags '()
+  "Option flags for doctest"
+  :group 'doctest
+  :type '(repeat (choice (const :tag "Select an option..." "")
+                         (const :tag "Normalize whitespace"
+                                "NORMALIZE_WHITESPACE")
+                         (const :tag "Ellipsis"
+                                "ELLIPSIS")
+                         (const :tag "Don't accept True for 1"
+                                "DONT_ACCEPT_TRUE_FOR_1")
+                         (const :tag "Don't accept <BLANKLINE>"
+                                "DONT_ACCEPT_BLANKLINE")
+                         (const :tag "Ignore Exception detail"
+                                "IGNORE_EXCEPTION_DETAIL")
+                         (const :tag "Report only first failure"
+                                "REPORT_ONLY_FIRST_FAILURE")
+                         )))
+
+(defcustom doctest-async t
+  "If true, then doctest will be run asynchronously."
+  :type 'boolean
+  :group 'doctest)
+
+(defcustom doctest-trim-exceptions t
+  "If true, then any exceptions inserted by doctest-replace-output
+will have the stack trace lines trimmed."
+  :type 'boolean
+  :group 'doctest)
+
+(defcustom doctest-highlight-strings t
+  "If true, then highlight strings.  If you find that doctest-mode
+is responding slowly when you type, turning this off might help."
+  :type 'boolean
+  :group 'doctest)
+
+(defcustom doctest-follow-output t
+  "If true, then when doctest is run asynchronously, the output buffer
+will scroll to display its output as it is generated.  If false, then
+the output buffer not scroll."
+  :type 'boolean
+  :group 'doctest)
+
+(defvar doctest-mode-hook nil
+  "Hook called by `doctest-mode'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Fonts
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defface doctest-prompt-face
+  '((((class color) (background dark))
+     (:foreground "#68f"))
+    (t (:foreground "#226")))
+  "Face for Python prompts in doctest examples."
+  :group 'doctest)
+
+(defface doctest-output-face
+  '((((class color) (background dark))
+     (:foreground "#afd"))
+    (t (:foreground "#262")))
+  "Face for the output of doctest examples."
+  :group 'doctest)
+
+(defface doctest-output-marker-face
+  '((((class color) (background dark))
+     (:foreground "#0f0"))
+    (t (:foreground "#080")))
+  "Face for markers in the output of doctest examples."
+  :group 'doctest)
+
+(defface doctest-output-traceback-face
+  '((((class color) (background dark))
+     (:foreground "#f88"))
+    (t (:foreground "#622")))
+  "Face for traceback headers in the output of doctest examples."
+  :group 'doctest)
+
+(defface doctest-results-divider-face
+  '((((class color) (background dark))
+     (:foreground "#08f"))
+    (t (:foreground "#00f")))
+  "Face for dividers in the doctest results window."
+  :group 'doctest)
+
+(defface doctest-results-loc-face
+  '((((class color) (background dark))
+     (:foreground "#0f8"))
+    (t (:foreground "#084")))
+  "Face for location headers in the doctest results window."
+  :group 'doctest)
+
+(defface doctest-results-header-face
+  '((((class color) (background dark))
+     (:foreground "#8ff"))
+    (t (:foreground "#088")))
+  "Face for sub-headers in the doctest results window."
+  :group 'doctest)
+
+(defface doctest-results-selection-face
+  '((((class color) (background dark))
+     (:foreground "#ff0" :background "#008"))
+    (t (:background "#088" :foreground "#fff")))
+  "Face for selected failure's location header in the results window."
+  :group 'doctest)
+
+(defface doctest-selection-face
+  '((((class color) (background dark))
+     (:foreground "#ff0" :background "#00f" :bold t))
+    (t (:foreground "#f00")))
+  "Face for selected example's prompt"
+  :group 'doctest)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst doctest-prompt-re
+  "^\\(?:\\([ \t]*\\)\\(>>> ?\\|[.][.][.] ?\\)\\([ \t]*\\)\\)"
+  "Regular expression for doctest prompts.  It defines three groups:
+the pre-prompt margin; the prompt; and the post-prompt indentation.")
+
+(defconst doctest-open-block-re
+  "[^\n]+:[ \t]*\\(#.*\\)?$"
+  "Regular expression for a line that opens a block")
+
+(defconst doctest-close-block-re
+  "\\(return\\|raise\\|break\\|continue\\|pass\\)\\b"
+  "Regular expression for a line that closes a block")
+
+(defconst doctest-example-source-re 
+  "^Failed example:\n\\(\n\\|    [^\n]*\n\\)+"
+  "Regular expression for example source in doctest's output.")
+
+(defconst doctest-results-divider-re
+  "^\\([*]\\{60,\\}\\)$"
+  "Regular expression for example dividers in doctest's output.")
+
+(defconst doctest-py24-results-loc-re
+  "^File \"[^\"]+\", line \\([0-9]+\\), in [^\n]+"
+  "Regular expression for example location markers in doctest's output.")
+
+(defconst doctest-py21-results-loc-re
+  "^from line #\\([0-9]+\\) of [^\n]*"
+  "Regular expression for example location markers in doctest's output,
+when the output was generated by an old version of doctest.")
+
+(defconst doctest-results-header-re
+  "^\\([^ \n\t].+:\\|Expected nothing\\|Got nothing\\)$"
+  "Regular expression for example headers in doctest's output.")
+
+(defconst doctest-traceback-header-re
+  "^[ \t]*Traceback (\\(most recent call last\\|innermost last\\)):"
+  "Regular expression for Python traceback headers.")
+
+(defconst doctest-py21-results-re
+  "^from line #"
+  "Regular expression used to test which version of doctest was used.")
+
+;; nb: There's a bug in Python's traceback.print_exception function
+;; which causes SyntaxError exceptions to be displayed incorrectly;
+;; which prevents this regexp from matching.  But there shouldn't be
+;; too many people testing for SyntaxErrors, so I won't worry about
+;; it.
+(defconst doctest-traceback-re
+  (let ((nonprompt
+         ;; This matches any non-blank line that doesn't start with
+         ;; a prompt (... or >>>).
+         (concat 
+          "\\(?:[.][.][^.\n]\\|[>][>][^>\n]\\|"
+          "[.][^.\n]\\|[>][^>\n]\\|[^.>\n \t]\\)[^\n]*")))
+    (concat
+     "^\\(\\([ \t]*\\)Traceback "
+     "(\\(?:most recent call last\\|innermost last\\)):\n\\)"
+     "\\(?:\\2[ \t]+[^ \t\n][^\n]*\n\\)*"
+     "\\(\\(?:\\2" nonprompt "\n\\)"
+        "\\(?:\\2[ \t]*" nonprompt "\n\\)*\\)"))
+  "Regular expression that matches a complete exception traceback.
+It contains three groups: group 1 is the header line; group 2 is
+the indentation; and group 3 is the exception message.")
+  
+(defconst doctest-blankline-re
+  "^[ \t]*<BLANKLINE>"
+  "Regular expression that matches blank line markers in doctest
+output.")
+
+(defconst doctest-outdent-re
+  (concat "\\(" (mapconcat 'identity
+			   '("else:"
+			     "except\\(\\s +.*\\)?:"
+			     "finally:"
+			     "elif\\s +.*:")
+			   "\\|")
+	  "\\)")
+  "Regular expression for a line that should be outdented.  Any line
+that matches `doctest-outdent-re', but does not follow a line matching
+`doctest-no-outdent-re', will be outdented.")
+
+;; It's not clear to me *why* the behavior given by this definition of
+;; doctest-no-outdent-re is desirable; but it's basically just copied
+;; from python-mode.
+(defconst doctest-no-outdent-re
+  (concat
+   "\\("
+   (mapconcat 'identity
+	      (list "try:"
+		    "except\\(\\s +.*\\)?:"
+		    "while\\s +.*:"
+		    "for\\s +.*:"
+		    "if\\s +.*:"
+		    "elif\\s +.*:"
+                    "\\(return\\|raise\\|break\\|continue\\|pass\\)[ \t\n]"
+		    )
+	      "\\|")
+	  "\\)")
+  "Regular expression matching lines not to outdent after.  Any line
+that matches `doctest-outdent-re', but does not follow a line matching
+`doctest-no-outdent-re', will be outdented.")
+
+(defconst doctest-script
+  "\
+from doctest import *
+import sys
+if '%m':
+    import imp
+    try:
+        m = imp.load_source('__imported__', '%m')
+        globs = m.__dict__
+    except Exception, e:
+        print ('doctest-mode encountered an error while importing '
+               'the current buffer:\\n\\n  %s' % e)
+        sys.exit(1)
+else:
+    globs = {}
+doc = open('%t').read()
+if sys.version_info[:2] >= (2,4):
+    test = DocTestParser().get_doctest(doc, globs, '%n', '%f', 0)
+    r = DocTestRunner(optionflags=%l)
+    r.run(test)
+else:
+    Tester(globs=globs).runstring(doc, '%f')"
+  ;; Docstring:
+  "Python script used to run doctest.
+The following special sequences are defined:
+  %n -- replaced by the doctest buffer's name.
+  %f -- replaced by the doctest buffer's filename.
+  %l -- replaced by the doctest flags string.
+  %t -- replaced by the name of the tempfile containing the doctests."
+  )
+
+(defconst doctest-keyword-re
+  (let* ((kw1 (mapconcat 'identity
+                         '("and"      "assert"   "break"   "class"
+                           "continue" "def"      "del"     "elif"
+                           "else"     "except"   "exec"    "for"
+                           "from"     "global"   "if"      "import"
+                           "in"       "is"       "lambda"  "not"
+                           "or"       "pass"     "print"   "raise"
+                           "return"   "while"    "yield"
+                           )
+                         "\\|"))
+         (kw2 (mapconcat 'identity
+                         '("else:" "except:" "finally:" "try:")
+                         "\\|"))
+         (kw3 (mapconcat 'identity
+                         '("ArithmeticError" "AssertionError"
+                           "AttributeError" "DeprecationWarning" "EOFError"
+                           "Ellipsis" "EnvironmentError" "Exception" "False"
+                           "FloatingPointError" "FutureWarning" "IOError"
+                           "ImportError" "IndentationError" "IndexError"
+                           "KeyError" "KeyboardInterrupt" "LookupError"
+                           "MemoryError" "NameError" "None" "NotImplemented"
+                           "NotImplementedError" "OSError" "OverflowError"
+                           "OverflowWarning" "PendingDeprecationWarning"
+                           "ReferenceError" "RuntimeError" "RuntimeWarning"
+                           "StandardError" "StopIteration" "SyntaxError"
+                           "SyntaxWarning" "SystemError" "SystemExit"
+                           "TabError" "True" "TypeError" "UnboundLocalError"
+                           "UnicodeDecodeError" "UnicodeEncodeError"
+                           "UnicodeError" "UnicodeTranslateError"
+                           "UserWarning" "ValueError" "Warning"
+                           "ZeroDivisionError" "__debug__"
+                           "__import__" "__name__" "abs" "apply" "basestring"
+                           "bool" "buffer" "callable" "chr" "classmethod"
+                           "cmp" "coerce" "compile" "complex" "copyright"
+                           "delattr" "dict" "dir" "divmod"
+                           "enumerate" "eval" "execfile" "exit" "file"
+                           "filter" "float" "getattr" "globals" "hasattr"
+                           "hash" "hex" "id" "input" "int" "intern"
+                           "isinstance" "issubclass" "iter" "len" "license"
+                           "list" "locals" "long" "map" "max" "min" "object"
+                           "oct" "open" "ord" "pow" "property" "range"
+                           "raw_input" "reduce" "reload" "repr" "round"
+                           "setattr" "slice" "staticmethod" "str" "sum"
+                           "super" "tuple" "type" "unichr" "unicode" "vars"
+                           "xrange" "zip")
+                         "\\|"))
+         (pseudokw (mapconcat 'identity
+                              '("self" "None" "True" "False" "Ellipsis")
+                              "\\|"))
+         (string (concat "'\\(?:\\\\[^\n]\\|[^\n']*\\)'" "\\|"
+                         "\"\\(?:\\\\[^\n]\\|[^\n\"]*\\)\""))
+         (brk "\\(?:[ \t(]\\|$\\)"))
+    (concat
+     ;; Comments (group 1)
+     "\\(#.*\\)"
+     ;; Function & Class Definitions (groups 2-3)
+     "\\|\\b\\(class\\|def\\)"
+     "[ \t]+\\([a-zA-Z_][a-zA-Z0-9_]*\\)"
+     ;; Builtins preceeded by '.'(group 4)
+     "\\|[.][\t ]*\\(" kw3 "\\)"
+     ;; Keywords & builtins (group 5)
+     "\\|\\b\\(" kw1 "\\|" kw2 "\\|"
+     kw3 "\\|" pseudokw "\\)" brk
+     ;; Decorators (group 6)
+     "\\|\\(@[a-zA-Z_][a-zA-Z0-9_]*\\)"
+     ))
+  "A regular expression used for syntax highlighting of Python
+source code.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Syntax Highlighting (font-lock mode)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Define the font-lock keyword table.
+(defconst doctest-font-lock-keywords
+  `(
+    ;; The following pattern colorizes source lines.  In particular,
+    ;; it first matches prompts, and then looks for any of the
+    ;; following matches *on the same line* as the prompt.  It uses
+    ;; the form:
+    ;;
+    ;;   (MATCHER MATCH-HIGHLIGHT
+    ;;            (ANCHOR-MATCHER nil nil MATCH-HIGHLIGHT))
+    ;;
+    ;; See the variable documentation for font-lock-keywords for a
+    ;; description of what each of those means.
+    ("^[ \t]*\\(>>>\\|\\.\\.\\.\\)"
+     (1 'doctest-prompt-face)
+     (doctest-source-matcher
+      nil nil
+      (1 'font-lock-comment-face t t)         ; comments
+      (2 'font-lock-keyword-face t t)         ; def/class
+      (3 'font-lock-type-face t t)            ; func/class name
+      ;; group 4 (builtins preceeded by '.') gets no colorization.
+      (5 'font-lock-keyword-face t t)         ; keywords & builtins
+      (6 'font-lock-preprocessor-face t t)    ; decorators
+      (7 'font-lock-string-face t t)          ; strings
+      ))
+      
+    ;; The following pattern colorizes output lines.  In particular,
+    ;; it uses doctest-output-line-matcher to check if this is an
+    ;; output line, and if so, it colorizes it, and any special
+    ;; markers it contains.
+    (doctest-output-line-matcher
+     (0 'doctest-output-face t)
+     ("\\.\\.\\." (beginning-of-line) (end-of-line)
+      (0 'doctest-output-marker-face t))
+     (,doctest-blankline-re (beginning-of-line) (end-of-line)
+                            (0 'doctest-output-marker-face t))
+     (doctest-traceback-line-matcher (beginning-of-line) (end-of-line)
+                                     (0 'doctest-output-traceback-face t))
+     (,doctest-traceback-header-re (beginning-of-line) (end-of-line)
+                                   (0 'doctest-output-traceback-face t))
+     )
+
+    ;; A PS1 prompt followed by a non-space is an error.
+    ("^[ \t]*\\(>>>[^ \t\n][^\n]*\\)" (1 'font-lock-warning-face t))
+    )
+  "Expressions to highlight in doctest-mode.")
+
+(defconst doctest-results-font-lock-keywords
+  `((,doctest-results-divider-re 
+     (0 'doctest-results-divider-face))
+    (,doctest-py24-results-loc-re 
+     (0 'doctest-results-loc-face))
+    (,doctest-results-header-re 
+     (0 'doctest-results-header-face))
+    (doctest-results-selection-matcher 
+     (0 'doctest-results-selection-face t)))
+  "Expressions to highlight in doctest-results-mode.")
+
+(defun doctest-output-line-matcher (limit)
+  "A `font-lock-keyword' MATCHER that returns t if the current 
+line is the expected output for a doctest example, and if so, 
+sets `match-data' so that group 0 spans the current line."
+  ;; The real work is done by doctest-find-output-line.
+  (when (doctest-find-output-line limit)
+    ;; If we found one, then mark the entire line.
+    (beginning-of-line)
+    (re-search-forward "[^\n]*" limit)))
+
+(defun doctest-traceback-line-matcher (limit)
+  "A `font-lock-keyword' MATCHER that returns t if the current line is
+the beginning of a traceback, and if so, sets `match-data' so that
+group 0 spans the entire traceback.  n.b.: limit is ignored."
+  (beginning-of-line)
+  (when (looking-at doctest-traceback-re)
+      (goto-char (match-end 0))
+      t))
+
+(defun doctest-source-matcher (limit)
+  "A `font-lock-keyword' MATCHER that returns t if the current line
+contains a Python source expression that should be highlighted
+after the point.  If so, it sets `match-data' to cover the string
+literal.  The groups in `match-data' should be interpreted as follows:
+
+  Group 1: comments
+  Group 2: def/class
+  Group 3: function/class name
+  Group 4: builtins preceeded by '.'
+  Group 5: keywords & builtins
+  Group 6: decorators
+  Group 7: strings
+"
+  (let ((matchdata nil))
+    ;; First, look for string literals.
+    (when doctest-highlight-strings
+      (save-excursion
+        (when (doctest-string-literal-matcher limit)
+          (setq matchdata
+                (list (match-beginning 0) (match-end 0)
+                      nil nil nil nil nil nil nil nil nil nil nil nil
+                      (match-beginning 0) (match-end 0))))))
+    ;; Then, look for other keywords.  If they occur before the
+    ;; string literal, then they take precedence.
+    (save-excursion
+      (when (and (re-search-forward doctest-keyword-re limit t)
+                 (or (null matchdata)
+                     (< (match-beginning 0) (car matchdata))))
+        (setq matchdata (match-data))))
+    (when matchdata
+      (set-match-data matchdata)
+      (goto-char (match-end 0))
+      t)))
+          
+(defun doctest-string-literal-matcher (limit &optional debug)
+  "A `font-lock-keyword' MATCHER that returns t if the current line
+contains a string literal starting at or after the point.  If so, it
+expands `match-data' to cover the string literal.  This matcher uses
+`doctest-statement-info' to collect information about strings that
+continue over multiple lines.  It therefore might be a little slow for
+very large statements."
+  (let* ((stmt-info (doctest-statement-info))
+         (quotes (reverse (nth 5 stmt-info)))
+         (result nil))
+    (if debug (doctest-debug "quotes %s" quotes))
+    (while (and quotes (null result))
+      (let* ((quote (pop quotes))
+             (start (car quote))
+             (end (min limit (or (cdr quote) limit))))
+        (if debug (doctest-debug "quote %s-%s pt=%s lim=%s"
+                                 start end (point) limit))
+        (when (or (and (<= (point) start) (< start limit))
+                  (and (< start (point)) (< (point) end)))
+          (setq start (max start (point)))
+          (set-match-data (list start end))
+          (if debug (doctest-debug "marking string %s" (match-data)))
+          (goto-char end)
+          (setq result t))))
+    result))
+   
+(defun doctest-results-selection-matcher (limit)
+  "Matches from `doctest-selected-failure' to the end of the
+line.  This is used to highlight the currently selected failure."
+  (when (and doctest-selected-failure
+	     (<= (point) doctest-selected-failure)
+	     (< doctest-selected-failure limit))
+    (goto-char doctest-selected-failure)
+    (re-search-forward "[^\n]+" limit)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Source code editing & indentation
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun doctest-indent-source-line (&optional dedent-only)
+  "Re-indent the current line, as doctest source code.  I.e., add a
+prompt to the current line if it doesn't have one, and re-indent the
+source code (to the right of the prompt).  If `dedent-only' is true,
+then don't increase the indentation level any."
+  (interactive "*")
+  (let ((indent-end nil))
+    (save-excursion
+      (beginning-of-line)
+      (let ((new-indent (doctest-current-source-line-indentation dedent-only))
+            (new-margin (doctest-current-source-line-margin))
+            (line-had-prompt (looking-at doctest-prompt-re)))
+        ;; Delete the old prompt (if any).
+        (when line-had-prompt
+          (goto-char (match-beginning 2))
+          (delete-char (- (match-end 2) (match-beginning 2))))
+        ;; Delete the old indentation.
+        (delete-backward-char (skip-chars-forward " \t"))
+        ;; If it's a continuation line, or a new PS1 prompt,
+        ;; then copy the margin.
+        (when (or new-indent (not line-had-prompt))
+          (beginning-of-line)
+          (delete-backward-char (skip-chars-forward " \t"))
+          (insert-char ?\  new-margin))
+        ;; Add the new prompt.
+        (insert-string (if new-indent "... " ">>> "))
+        ;; Add the new indentation
+        (if new-indent (insert-char ?\  new-indent))
+        (setq indent-end (point))))
+    ;; If we're left of the indentation end, then move up to the
+    ;; indentation end.
+    (if (< (point) indent-end) (goto-char indent-end))))
+
+(defun doctest-current-source-line-indentation (&optional dedent-only)
+  "Return the post-prompt indent to use for this line.  This is an
+integer for a continuation lines, and nil for non-continuation lines."
+  (save-excursion
+    ;; Examine the previous doctest line (if present).
+    (let* ((prev-stmt-info (doctest-prev-statement-info))
+           (prev-stmt-indent (nth 0 prev-stmt-info)) 
+           (continuation-indent (nth 1 prev-stmt-info))
+           (prev-stmt-opens-block (nth 2 prev-stmt-info))
+           (prev-stmt-closes-block (nth 3 prev-stmt-info))
+           (prev-stmt-blocks-outdent (nth 4 prev-stmt-info))
+           )
+      ;; Examine this doctest line.
+      (let* ((curr-line-indent 0)
+             (curr-line-outdented nil))
+        (beginning-of-line)
+        (when (looking-at doctest-prompt-re)
+          (setq curr-line-indent (- (match-end 3) (match-beginning 3)))
+          (goto-char (match-end 3)))
+        (setq curr-line-outdented (and (looking-at doctest-outdent-re)
+                                       (not prev-stmt-blocks-outdent)))
+        ;; Compute the overall indent.
+        (let ((indent (or continuation-indent 
+                          (+ prev-stmt-indent
+                             (if curr-line-outdented -4 0)
+                             (if prev-stmt-opens-block 4 0)
+                             (if prev-stmt-closes-block -4 0)))))
+          ;; If dedent-only is true, then make sure we don't indent.
+          (when dedent-only 
+            (setq indent (min indent curr-line-indent)))
+          ;; If indent=0 and we're not outdented, then set indent to
+          ;; nil (to signify the start of a new source example).
+          (when (and (= indent 0)
+                     (not (or curr-line-outdented continuation-indent)))
+            (setq indent nil))
+          ;; Return the indentation.
+          indent)))))
+
+(defun doctest-prev-statement-info (&optional debug)
+  (save-excursion
+    (forward-line -1)
+    (doctest-statement-info debug)))
+
+(defun doctest-statement-info (&optional debug)
+  "Collect info about the previous statement, and return it as a list:
+
+  (INDENT, CONTINUATION, OPENS-BLOCK, CLOSES-BLOCK, BLOCKS-OUTDENT,
+   QUOTES)
+
+INDENT -- The indentation of the previous statement (after the prompt)
+
+CONTINUATION -- If the previous statement is incomplete (e.g., has an
+open paren or quote), then this is the appropriate indentation
+level; otherwise, it's nil.
+
+OPENS-BLOCK -- True if the previous statement opens a Python control
+block.
+
+CLOSES-BLOCK -- True if the previous statement closes a Python control
+block.
+
+BLOCKS-OUTDENT -- True if the previous statement should 'block the
+next statement from being considered an outdent.
+
+QUOTES -- A list of (START . END) pairs for all quotation strings.
+"
+  (save-excursion
+    (end-of-line)
+    (let ((end (point)))
+      (while (and (doctest-on-source-line-p "...") (= (forward-line -1) 0)))
+      (cond
+       ;; If there's no previous >>> line, then give up.
+       ((not (doctest-on-source-line-p ">>>"))
+        '(0 nil nil nil nil))
+       
+       ;; If there is a previous statement, walk through the source
+       ;; code, checking for operators that may be of interest.
+       (t 
+        (beginning-of-line)
+        (let* ((quote-mark nil) (nesting 0) (indent-stack '())
+               (stmt-indent 0)
+               (stmt-opens-block nil)
+               (stmt-closes-block nil)
+               (stmt-blocks-outdent nil)
+               (quotes '())
+               (elt-re (concat "\\\\[^\n]\\|"
+                               "(\\|)\\|\\[\\|\\]\\|{\\|}\\|"
+                               "\"\"\"\\|\'\'\'\\|\"\\|\'\\|"
+                               "#[^\n]*\\|" doctest-prompt-re)))
+          (while (re-search-forward elt-re end t)
+            (let* ((elt (match-string 0))
+                   (elt-first-char (substring elt 0 1)))
+              (if debug (doctest-debug "Debug: %s" elt))
+              (cond
+               ;; Close quote -- set quote-mark back to nil.  The
+               ;; second case is for cases like: '  '''
+               (quote-mark
+                (cond
+                 ((equal quote-mark elt)
+                  (setq quote-mark nil)
+                  (setcdr (car quotes) (point)))
+                 ((equal quote-mark elt-first-char)
+                  (setq quote-mark nil)
+                  (setcdr (car quotes) (point))
+                  (backward-char 2))))
+               ;; Prompt -- check if we're starting a new stmt.  If so,
+               ;; then collect various info about it.
+               ((string-match doctest-prompt-re elt)
+                (when (and (null quote-mark) (= nesting 0))
+                  (let ((indent (- (match-end 3) (match-end 2))))
+                    (unless (looking-at "[ \t]*\n")
+                      (setq stmt-indent indent)
+                      (setq stmt-opens-block
+                            (looking-at doctest-open-block-re))
+                      (setq stmt-closes-block
+                            (looking-at doctest-close-block-re))
+                      (setq stmt-blocks-outdent
+                            (looking-at doctest-no-outdent-re))))))
+               ;; Open paren -- increment nesting, and update indent-stack.
+               ((string-match "(\\|\\[\\|{" elt-first-char)
+                (let ((elt-pos (point))
+                      (at-eol (looking-at "[ \t]*\n"))
+                      (indent 0))
+                  (save-excursion 
+                    (re-search-backward doctest-prompt-re)
+                    (if at-eol
+                        (setq indent (+ 4 (- (match-end 3) (match-end 2))))
+                      (setq indent (- elt-pos (match-end 2))))
+                    (push indent indent-stack)))
+                (setq nesting (+ nesting 1)))
+               ;; Close paren -- decrement nesting, and pop indent-stack.
+               ((string-match ")\\|\\]\\|}" elt-first-char)
+                (setq indent-stack (cdr indent-stack))
+                (setq nesting (max 0 (- nesting 1))))
+               ;; Open quote -- set quote-mark.
+               ((string-match "\"\\|\'" elt-first-char)
+                (push (cons (- (point) (length elt)) nil) quotes)
+                (setq quote-mark elt)))))
+        
+          (let* ((continuation-indent
+                 (cond
+                  (quote-mark 0)
+                  ((> nesting 0) (if (null indent-stack) 0 (car indent-stack)))
+                  (t nil)))
+                 (result 
+                  (list stmt-indent continuation-indent
+                        stmt-opens-block stmt-closes-block
+                        stmt-blocks-outdent quotes)))
+            (if debug (doctest-debug "Debug: %s" result))
+            result)))))))
+
+(defun doctest-current-source-line-margin ()
+  "Return the pre-prompt margin to use for this source line.  This is
+copied from the most recent source line, or set to
+`doctest-default-margin' if there are no preceeding source lines."
+  (save-excursion
+    (save-restriction
+      (when (doctest-in-mmm-docstring-overlay)
+        (doctest-narrow-to-mmm-overlay))
+      (beginning-of-line)
+      (forward-line -1)
+      (while (and (not (doctest-on-source-line-p))
+                  (re-search-backward doctest-prompt-re nil t))))
+    (cond ((looking-at doctest-prompt-re)
+           (- (match-end 1) (match-beginning 1)))
+          ((doctest-in-mmm-docstring-overlay)
+           (doctest-default-margin-in-mmm-docstring-overlay))
+          (t
+           doctest-default-margin))))
+
+(defun doctest-electric-backspace ()
+  "Delete the preceeding character, level of indentation, or
+prompt.  
+
+If point is at the leftmost column, delete the preceding newline.
+
+Otherwise, if point is at the first non-whitespace character
+following an indented source line's prompt, then reduce the
+indentation to the next multiple of 4; and update the source line's
+prompt, when necessary.
+
+Otherwise, if point is at the first non-whitespace character
+following an unindented source line's prompt, then remove the
+prompt (converting the line to an output line or text line).
+
+Otherwise, if point is at the first non-whitespace character of a
+line, the delete the line's indentation.
+
+Otherwise, delete the preceeding character.
+"
+  (interactive "*")
+  (cond 
+   ;; Beginning of line: delete preceeding newline.
+   ((bolp) (backward-delete-char 1))
+      
+   ;; First non-ws char following prompt: dedent or remove prompt.
+   ((and (looking-at "[^ \t\n]\\|$") (doctest-looking-back doctest-prompt-re))
+    (let* ((prompt-beg (match-beginning 2))
+	   (indent-beg (match-beginning 3)) (indent-end (match-end 3))
+	   (old-indent (- indent-end indent-beg))
+	   (new-indent (* (/ (- old-indent 1) 4) 4)))
+      (cond
+       ;; Indented source line: dedent it.
+       ((> old-indent 0)
+	(goto-char indent-beg)
+	(delete-region indent-beg indent-end)
+	(insert-char ?\  new-indent)
+	;; Change prompt to PS1, when appropriate.
+	(when (and (= new-indent 0) (not (looking-at doctest-outdent-re)))
+	  (delete-backward-char 4)
+	  (insert-string ">>> ")))
+       ;; Non-indented source line: remove prompt.
+       (t
+	(goto-char indent-end)
+	(delete-region prompt-beg indent-end)))))
+
+   ;; First non-ws char of a line: delete all indentation.
+   ((and (looking-at "[^ \n\t]\\|$") (doctest-looking-back "^[ \t]+"))
+    (delete-region (match-beginning 0) (match-end 0)))
+
+   ;; Otherwise: delete a character.
+   (t
+    (backward-delete-char 1))))
+
+(defun doctest-newline-and-indent ()
+  "Insert a newline, and indent the new line appropriately.
+
+If the current line is a source line containing a bare prompt,
+then clear the current line, and insert a newline.
+
+Otherwise, if the current line is a source line, then insert a
+newline, and add an appropriately indented prompt to the new
+line.
+
+Otherwise, if the current line is an output line, then insert a
+newline and indent the new line to match the example's margin.
+
+Otherwise, insert a newline.
+
+If `doctest-avoid-trailing-whitespace' is true, then clear any
+whitespace to the left of the point before inserting a newline.
+"
+  (interactive "*")
+  ;; If we're avoiding trailing spaces, then delete WS before point.
+  (if doctest-avoid-trailing-whitespace
+      (delete-char (- (skip-chars-backward " \t"))))     
+  (cond 
+   ;; If we're on an empty prompt, delete it.
+   ((doctest-on-empty-source-line-p)
+    (delete-region (match-beginning 0) (match-end 0))
+    (insert-char ?\n 1))
+   ;; If we're on a doctest line, add a new prompt.
+   ((doctest-on-source-line-p)
+    (insert-char ?\n 1)
+    (doctest-indent-source-line))
+   ;; If we're in doctest output, indent to the margin.
+   ((doctest-on-output-line-p)
+    (insert-char ?\n 1)
+    (insert-char ?\  (doctest-current-source-line-margin)))
+   ;; Otherwise, just add a newline.
+   (t (insert-char ?\n 1))))
+
+(defun doctest-electric-colon ()
+  "Insert a colon, and dedent the line when appropriate."
+  (interactive "*")
+  (insert-char ?: 1)
+  (when (doctest-on-source-line-p)
+    (doctest-indent-source-line t)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Code Execution
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun doctest-execute ()
+  "Run doctest on the current buffer, or on the current docstring
+if the point is inside an `mmm-mode' `doctest-docstring' region.
+Display the results in the *doctest-output* buffer."
+  (interactive)
+  (doctest-execute-region (point-min) (point-max) nil t))
+  
+(defun doctest-execute-with-diff ()
+  "Run doctest on the current buffer, or on the current docstring
+if the point is inside an `mmm-mode' `doctest-docstring' region.
+Display the results in the *doctest-output* buffer, using diff format."
+  (interactive)
+  (doctest-execute-region (point-min) (point-max) t t))
+  
+(defun doctest-execute-buffer-with-diff ()
+  "Run doctest on the current buffer, and display the results in the 
+*doctest-output* buffer, using the diff format."
+  (interactive)
+  (doctest-execute-region (point-min) (point-max) t nil))
+
+(defun doctest-execute-buffer ()
+  "Run doctest on the current buffer, and display the results in the 
+*doctest-output* buffer."
+  (interactive)
+  (doctest-execute-region (point-min) (point-max) nil nil))
+
+(defun doctest-execute-region (start end &optional diff
+                                     check-for-mmm-docstring-overlay)
+  "Run doctest on the current buffer, and display the results in the 
+*doctest-output* buffer."
+  (interactive "r")
+  ;; If it's already running, give the user a chance to restart it.
+  (when (doctest-process-live-p doctest-async-process)
+    (when (y-or-n-p "Doctest is already running.  Restart it? ")
+      (doctest-cancel-async-process)
+      (message "Killing doctest...")))
+  (cond
+   ((and doctest-async (doctest-process-live-p doctest-async-process))
+    (message "Can't run two doctest processes at once!"))
+   (t
+    (let* ((results-buf-name (doctest-results-buffer-name))
+           (in-docstring (and check-for-mmm-docstring-overlay
+                              (doctest-in-mmm-docstring-overlay)))
+           (temp (doctest-temp-name)) (dir doctest-temp-directory)
+           (input-file (expand-file-name (concat temp ".py") dir))
+           (globs-file (when in-docstring
+                         (expand-file-name (concat temp "-globs.py") dir)))
+           (cur-buf (current-buffer))
+           (in-buf (get-buffer-create "*doctest-input*"))
+           (script (doctest-script input-file globs-file diff)))
+      ;; If we're in a docstring, narrow start & end.
+      (when in-docstring
+        (let ((bounds (doctest-mmm-overlay-bounds)))
+          (setq start (max start (car bounds))
+                end (min end (cdr bounds)))))
+      ;; Write the doctests to a file.
+      (save-excursion
+        (goto-char (min start end))
+        (let ((lineno (doctest-line-number)))
+          (set-buffer in-buf)
+          ;; Add blank lines, to keep line numbers the same:
+          (dotimes (n (- lineno 1)) (insert-string "\n"))
+          ;; Add the selected region
+          (insert-buffer-substring cur-buf start end)
+          ;; Write it to a file
+          (write-file input-file)))
+      ;; If requested, write the buffer to a file for use as globs.
+      (when globs-file
+        (let ((cur-buf-start (point-min)) (cur-buf-end (point-max)))
+          (save-excursion
+            (set-buffer in-buf)
+            (delete-region (point-min) (point-max))
+            (insert-buffer-substring cur-buf cur-buf-start cur-buf-end)
+            (write-file globs-file))))
+      ;; Dispose of in-buf (we're done with it now.
+      (kill-buffer in-buf)
+      ;; Prepare the results buffer.  Clear it, if it contains
+      ;; anything, and set its mode.
+      (setq doctest-results-buffer (get-buffer-create results-buf-name))
+      (save-excursion
+        (set-buffer doctest-results-buffer)
+        (toggle-read-only 0)
+        (delete-region (point-min) (point-max))
+        (doctest-results-mode)
+        (setq doctest-source-buffer cur-buf)
+        )
+      ;; Add markers to examples, and record what line number each one
+      ;; starts at.  That way, if the input buffer is edited, we can
+      ;; still find corresponding examples in the output.
+      (doctest-mark-examples)
+
+      ;; Run doctest
+      (cond (doctest-async
+             ;; Asynchronous mode:
+             (let ((process (start-process "*doctest*" doctest-results-buffer
+                                           doctest-python-command
+                                           "-c" script)))
+               ;; Store some information about the process.
+               (setq doctest-async-process-buffer cur-buf)
+               (setq doctest-async-process process)
+               (push input-file doctest-async-process-tempfiles)
+               (when globs-file
+                 (push globs-file doctest-async-process-tempfiles))
+               ;; Set up a sentinel to respond when it's done running.
+               (set-process-sentinel process 'doctest-async-process-sentinel)
+
+               ;; Show the output window.
+               (let ((w (display-buffer doctest-results-buffer)))
+                 (when doctest-follow-output
+                   ;; Insert a newline, which will move the buffer's
+                   ;; point past the process's mark -- this causes the
+                   ;; window to scroll as new output is generated.
+                   (save-current-buffer
+                     (set-buffer doctest-results-buffer)
+                     (insert-string "\n")
+                     (set-window-point w (point)))))
+
+               ;; Let the user know the process is running.
+               (doctest-update-mode-line ":running")
+               (message "Running doctest...")))
+            (t
+             ;; Synchronous mode:
+             (call-process doctest-python-command nil
+                           doctest-results-buffer t "-c" script)
+             (doctest-handle-output)
+             (delete-file input-file)
+             (when globs-file
+               (delete-file globs-file))))))))
+
+(defun doctest-handle-output ()
+  "This function, which is called after the 'doctest' process spawned
+by doctest-execute-buffer has finished, checks the doctest results
+buffer.  If that buffer is empty, it reports no errors and hides it;
+if that buffer is not empty, it reports that errors occured, displays
+the buffer, and runs doctest-postprocess-results."
+  ;; If any tests failed, display them.
+  (cond ((not (buffer-live-p doctest-results-buffer))
+         (doctest-warn "Results buffer not found!"))
+        ((> (buffer-size doctest-results-buffer) 1)
+         (display-buffer doctest-results-buffer)
+         (doctest-postprocess-results)
+         (let ((num (length doctest-example-markers)))
+           (message "%d doctest example%s failed!" num
+                    (if (= num 1) "" "s"))))
+        (t
+         (display-buffer doctest-results-buffer)
+         (delete-windows-on doctest-results-buffer)
+         (message "All doctest examples passed!"))))
+         
+(defun doctest-async-process-sentinel (process state)
+  "A process sentinel, called when the asynchronous doctest process
+completes, which calls doctest-handle-output."
+  ;; Check to make sure we got the process we're expecting.  On
+  ;; some operating systems, this will end up getting called twice
+  ;; when we use doctest-cancel-async-process; this check keeps us
+  ;; from trying to clean up after the same process twice (since we
+  ;; set doctest-async-process to nil when we're done).
+  (when (and (equal process doctest-async-process)
+             (buffer-live-p doctest-async-process-buffer))
+    (save-current-buffer
+      (set-buffer doctest-async-process-buffer)
+      (cond ((not (buffer-live-p doctest-results-buffer))
+             (doctest-warn "Results buffer not found!"))
+            ((equal state "finished\n")
+             (doctest-handle-output)
+             (let ((window (get-buffer-window
+                            doctest-async-process-buffer t)))
+               (when window (set-window-point window (point)))))
+            ((equal state "killed\n")
+             (message "Doctest killed."))
+            (t
+             (message "Doctest failed -- %s" state)
+             (display-buffer doctest-results-buffer)))
+      (doctest-update-mode-line "")
+      (while doctest-async-process-tempfiles
+        (delete-file (pop doctest-async-process-tempfiles)))
+      (setq doctest-async-process nil))))
+
+(defun doctest-cancel-async-process ()
+  "If a doctest process is running, then kill it."
+  (interactive "")
+  (when (doctest-process-live-p doctest-async-process)
+    ;; Update the modeline
+    (doctest-update-mode-line ":killing")
+    ;; Kill the process.
+    (kill-process doctest-async-process)
+    ;; Run the sentinel.  (Depending on what OS we're on, the sentinel
+    ;; may end up getting called once or twice.)
+    (doctest-async-process-sentinel doctest-async-process "killed\n")
+    ))
+
+(defun doctest-postprocess-results ()
+  "Post-process the doctest results buffer: check what version of
+doctest was used, and set doctest-results-py-version accordingly;
+turn on read-only mode; filter the example markers; hide the example
+source (if `doctest-hide-example-source' is non-nil); and select the
+first failure."
+  (save-excursion
+    (set-buffer doctest-results-buffer)
+    ;; Check if we're using an old doctest version.
+    (goto-char (point-min))
+    (if (re-search-forward doctest-py21-results-re nil t)
+        (setq doctest-results-py-version 'py21)
+      (setq doctest-results-py-version 'py24))
+    ;; Turn on read-only mode.
+    (toggle-read-only t))
+  
+  (doctest-filter-example-markers)
+  (if doctest-hide-example-source
+      (doctest-hide-example-source))
+  (doctest-next-failure 1))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Markers
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun doctest-mark-examples ()
+  "Add a marker at the beginning of every (likely) example in the
+input buffer; and create a list, `doctest-example-markers',
+which maps from markers to the line numbers they originally occured
+on.  This will allow us to find the corresponding example in the
+doctest output, even if the input buffer is edited."
+  (dolist (marker-info doctest-example-markers)
+    (set-marker (car marker-info) nil))
+  (setq doctest-example-markers '())
+  (save-excursion
+    (goto-char (point-min))
+    (while (re-search-forward "^ *>>> " nil t)
+      (backward-char 4)
+      (push (cons (point-marker) (doctest-line-number))
+            doctest-example-markers)
+      (forward-char 4))))
+
+(defun doctest-filter-example-markers ()
+  "Remove any entries from `doctest-example-markers' that do not
+correspond to a failed example."
+  (let ((filtered nil) (markers doctest-example-markers))
+    (save-excursion
+      (set-buffer doctest-results-buffer)
+      (goto-char (point-max))
+      (while (re-search-backward (doctest-results-loc-re) nil t)
+        (let ((lineno (string-to-int (match-string 1))))
+          (when (equal doctest-results-py-version 'py21)
+            (setq lineno (+ lineno 1)))
+          (while (and markers (< lineno (cdar markers)))
+            (set-marker (caar markers) nil)
+            (setq markers (cdr markers)))
+          (if (and markers (= lineno (cdar markers)))
+              (push (pop markers) filtered)
+            (doctest-warn "Example expected on line %d but not found %s"
+                          lineno markers)))))
+    (dolist (marker-info markers)
+      (set-marker (car marker-info) nil))
+    (setq doctest-example-markers filtered)))
+                       
+(defun doctest-prev-example-marker ()
+  "Helper for doctest-replace-output: move to the preceeding example
+marker, and return the corresponding 'original' lineno.  If none is
+found, return nil."
+  (let ((lineno nil)
+        (pos nil))
+    (save-excursion
+      (end-of-line)
+      (when (re-search-backward "^\\( *\\)>>> " nil t)
+        (goto-char (match-end 1))
+        (dolist (marker-info doctest-example-markers)
+          (when (= (marker-position (car marker-info)) (point))
+            (setq lineno (cdr marker-info))
+            (setq pos (point))))))
+    (unless (null lineno)
+      (goto-char pos)
+      lineno)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Navigation
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun doctest-next-failure (count)
+  "Move to the top of the next failing example, and highlight the
+example's failure description in *doctest-output*."
+  (interactive "p")
+  (cond
+   ((and doctest-async (doctest-process-live-p doctest-async-process))
+    (message "Wait for doctest to finish running!"))
+   ((not (doctest-results-buffer-valid-p))
+    (message "Run doctest first! (C-c C-c)"))
+   ((equal count 0)
+    t)
+   (t
+    (let ((marker nil) (example-markers doctest-example-markers)
+          (results-window (display-buffer doctest-results-buffer)))
+      (save-excursion
+        (set-buffer doctest-results-buffer)
+        ;; Pick up where we left off.
+        ;; (nb: doctest-selected-failure is buffer-local)
+        (goto-char (or doctest-selected-failure (point-min)))
+        ;; Skip past anything on *this* line.
+        (if (>= count 0) (end-of-line) (beginning-of-line))
+        ;; Look for the next failure
+        (when 
+            (if (>= count 0)
+                (re-search-forward (doctest-results-loc-re) nil t count)
+              (re-search-backward (doctest-results-loc-re) nil t (- count)))
+          ;; We found a failure:
+          (let ((old-selected-failure doctest-selected-failure))
+            (beginning-of-line)
+            ;; Extract the line number for the doctest file.
+            (let ((orig-lineno (string-to-int (match-string 1))))
+              (when (equal doctest-results-py-version 'py21)
+                (setq orig-lineno (+ orig-lineno 1)))
+              (dolist (marker-info example-markers)
+                (when (= orig-lineno (cdr marker-info))
+                  (setq marker (car marker-info)))))
+              
+            ;; Update the window cursor.
+            (beginning-of-line)
+            (set-window-point results-window (point))
+            ;; Store our position for next time.
+            (setq doctest-selected-failure (point))
+            ;; Update selection.
+            (doctest-fontify-line old-selected-failure)
+            (doctest-fontify-line doctest-selected-failure))))
+      
+      (cond
+       ;; We found a failure -- move point to the selected failure.
+       (marker
+        (goto-char (marker-position marker))
+        (beginning-of-line))
+       ;; We didn't find a failure, but there is one -- wrap.
+       ((> (length doctest-example-markers) 0)
+        (if (>= count 0) (doctest-first-failure) (doctest-last-failure)))
+       ;; We didn't find a failure -- alert the user.
+       (t (message "No failures found!")))))))
+
+(defun doctest-prev-failure (count)
+  "Move to the top of the previous failing example, and highlight
+the example's failure description in *doctest-output*."
+  (interactive "p")
+  (doctest-next-failure (- count)))
+
+(defun doctest-first-failure ()
+  "Move to the top of the first failing example, and highlight
+the example's failure description in *doctest-output*."
+  (interactive)
+  (if (buffer-live-p doctest-results-buffer)
+      (save-excursion
+        (set-buffer doctest-results-buffer)
+        (let ((old-selected-failure doctest-selected-failure))
+          (setq doctest-selected-failure (point-min))
+          (doctest-fontify-line old-selected-failure))))
+  (doctest-next-failure 1))
+
+(defun doctest-last-failure ()
+  "Move to the top of the last failing example, and highlight
+the example's failure description in *doctest-output*."
+  (interactive)
+  (if (buffer-live-p doctest-results-buffer)
+      (save-excursion
+        (set-buffer doctest-results-buffer)
+        (let ((old-selected-failure doctest-selected-failure))
+          (setq doctest-selected-failure (point-max))
+          (doctest-fontify-line old-selected-failure))))
+  (doctest-next-failure -1))
+
+(defun doctest-select-failure ()
+  "Move to the top of the currently selected example, and select that
+example in the source buffer.  Intended for use in the results
+buffer."
+  (interactive)
+  (re-search-backward doctest-results-divider-re)
+  (let ((old-selected-failure doctest-selected-failure))
+    (setq doctest-selected-failure (point))
+    (doctest-fontify-line doctest-selected-failure)
+    (doctest-fontify-line old-selected-failure))
+  (pop-to-buffer doctest-source-buffer)
+  (doctest-next-failure 1))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Replace Output
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun doctest-replace-output ()
+  "Move to the top of the closest example, and replace its output
+with the 'got' output from the *doctest-output* buffer.  An error is
+displayed if the chosen example is not listed in *doctest-output*, or
+if the 'expected' output for the example does not exactly match the
+output listed in the source buffer.  The user is asked to confirm the
+replacement."
+  (interactive)
+  ;; Move to the beginning of the example.
+  (cond
+   ((and doctest-async (doctest-process-live-p doctest-async-process))
+    (message "Wait for doctest to finish running!"))
+   ((not (doctest-results-buffer-valid-p))
+    (message "Run doctest first! (C-c C-c)"))
+   ((save-excursion (set-buffer doctest-results-buffer)
+                    (equal doctest-results-py-version 'py21))
+    (error "doctest-replace-output requires python 2.4+"))
+   (t
+    (save-excursion
+      (save-restriction
+        (when (doctest-in-mmm-docstring-overlay)
+          (doctest-narrow-to-mmm-overlay))
+            
+        (let* ((orig-buffer (current-buffer))
+               ;; Find an example, and look up its original lineno.
+               (lineno (doctest-prev-example-marker))
+               ;; Find the example's indentation.
+               (prompt-indent (doctest-line-indentation)))
+        
+          ;; Switch to the output buffer, and look for the example.
+          ;; If we don't find one, complain.
+          (cond
+           ((null lineno) (message "Doctest example not found"))
+           (t
+            (set-buffer doctest-results-buffer)
+            (goto-char (point-min))
+            (let ((output-re (format "^File .*, line %s," lineno)))
+              (when (not (re-search-forward output-re nil t))
+                (message "This doctest example did not fail")
+                (setq lineno nil)))))
+
+          ;; If we didn't find an example, give up.
+          (when (not (null lineno))
+            ;; Get the output's 'expected' & 'got' texts.
+            (let ((doctest-got nil) (doctest-expected nil) (header nil))
+              (while (setq header (doctest-results-next-header))
+                (cond
+                 ((equal header "Failed example:")
+                  t)
+                 ((equal header "Expected nothing")
+                  (setq doctest-expected ""))
+                 ((equal header "Expected:")
+                  (unless (re-search-forward "^\\(\\(    \\).*\n\\)*" nil t)
+                    (error "Error parsing doctest output"))
+                  (setq doctest-expected (doctest-replace-regexp-in-string
+                                          "^    " prompt-indent
+                                          (match-string 0))))
+                 ((equal header "Got nothing")
+                  (setq doctest-got ""))
+                 ((or (equal header "Got:") (equal header "Exception raised:"))
+                  (unless (re-search-forward "^\\(\\(    \\).*\n\\)*" nil t)
+                    (error "Error parsing doctest output"))
+                  (setq doctest-got (doctest-replace-regexp-in-string
+                                     "^    " prompt-indent (match-string 0))))
+                 ((string-match "^Differences" header)
+                  (error (concat "doctest-replace-output can not be used "
+                                 "with diff style output")))
+                 (t (error "Unexpected header %s" header))))
+
+              ;; Go back to the source buffer.
+              (set-buffer orig-buffer)
+          
+              ;; Skip ahead to the output.
+              (beginning-of-line)
+              (unless (re-search-forward "^ *>>>.*")
+                (error "Error parsing doctest output"))
+              (re-search-forward "\\(\n *\\.\\.\\..*\\)*\n?")
+              (when (looking-at "\\'") (insert-char ?\n))
+
+              ;; Check that the output matches.
+              (let ((start (point)) end)
+                (cond ((re-search-forward "^ *\\(>>>.*\\|$\\)" nil t)
+                       (setq end (match-beginning 0)))
+                      (t
+                       (goto-char (point-max))
+                       (insert-string "\n")
+                       (setq end (point-max))))
+                (when (and doctest-expected
+                           (not (equal (buffer-substring start end)
+                                       doctest-expected)))
+                  (warn "{%s} {%s}" (buffer-substring start end)
+                        doctest-expected)
+                  (error (concat "This example's output has been modified "
+                                 "since doctest was last run")))
+                (setq doctest-expected (buffer-substring start end))
+                (goto-char end))
+
+              ;; Trim exceptions
+              (when (and doctest-trim-exceptions
+                         (string-match doctest-traceback-re
+                                       doctest-got))
+                (let ((s1 0) (e1 (match-end 1))
+                      (s2 (match-beginning 2)) (e2 (match-end 2))
+                      (s3 (match-beginning 3)) (e3 (length doctest-got)))
+                  (setq doctest-got
+                        (concat (substring doctest-got s1 e1)
+                                (substring doctest-got s2 e2) "  . . .\n"
+                                (substring doctest-got s3 e3)))))
+              
+              ;; Confirm it with the user.
+              (let ((confirm-buffer (get-buffer-create "*doctest-confirm*")))
+                (set-buffer confirm-buffer)
+                ;; Erase anything left over in the buffer.
+                (delete-region (point-min) (point-max))
+                ;; Write a confirmation message
+                (if (equal doctest-expected "")
+                    (insert-string "Replace nothing\n")
+                  (insert-string (concat "Replace:\n" doctest-expected)))
+                (if (equal doctest-got "")
+                    (insert-string "With nothing\n")
+                  (insert-string (concat "With:\n" doctest-got)))
+                (let ((confirm-window (display-buffer confirm-buffer)))
+                  ;; Shrink the confirm window.
+                  (shrink-window-if-larger-than-buffer confirm-window)
+                  ;; Return to the original buffer.
+                  (set-buffer orig-buffer)
+                  ;; Match the old expected region.
+                  (when doctest-expected
+                    (search-backward doctest-expected))
+                  (when (equal doctest-expected "") (backward-char 1))
+                  ;; Get confirmation & do the replacement
+                  (widen)
+                  (cond ((y-or-n-p "Ok to replace? ")
+                         (when (equal doctest-expected "") (forward-char 1))
+                         (replace-match doctest-got t t)
+                         (message "Replaced."))
+                        (t
+                         (message "Replace cancelled.")))
+                  ;; Clean up our confirm window
+                  (kill-buffer confirm-buffer)
+                  (delete-window confirm-window)))))))))))
+
+(defun doctest-results-next-header ()
+  "Move to the next header in the doctest results buffer, and return
+the string contents of that header.  If no header is found, return
+nil."
+  (if (re-search-forward (concat doctest-results-header-re "\\|"
+                                 doctest-results-divider-re) nil t)
+      (let ((result (match-string 0)))
+        (if (string-match doctest-results-header-re result)
+            result
+          nil))
+    nil))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; mmm-mode support
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; MMM Mode is a minor mode for Emacs which allows Multiple Major
+;; Modes to coexist in a single buffer.  
+
+;;;###autoload
+(defun doctest-register-mmm-classes (&optional add-mode-ext-classes
+                                               fix-mmm-fontify-region-bug)
+  "Register doctest's mmm classes, allowing doctest to be used as a
+submode region in other major modes, such as python-mode and rst-mode.
+Two classes are registered:
+
+`doctest-docstring'
+
+    Used to edit docstrings containing doctest examples in python-
+    mode.  Docstring submode regions start and end with triple-quoted
+    strings (\"\"\").  In order to avoid confusing start-string
+    markers and end-string markers, all triple-quote strings in the
+    buffer are treated as submode regions (even if they're not
+    actually docstrings).  Use (C-c % C-d) to insert a new doctest-
+    docstring region.  When `doctest-execute' (C-c C-c) is called
+    inside a doctest-docstring region, it executes just the current
+    docstring.  The globals for this execution are constructed by
+    importing the current buffer's contents in Python.
+
+`doctest-example'
+
+    Used to edit doctest examples in text-editing modes, such as
+    `rst-mode' or `text-mode'.  Docstring submode regions start with
+    optionally indented prompts (>>>) and end with blank lines.  Use
+    (C-c % C-e) to insert a new doctest-example region.  When
+    `doctest-execute' (C-c C-c) is called inside a doctest-example
+    region, it executes all examples in the buffer.
+
+If ADD-MODE-EXT-CLASSES is true, then register the new classes in
+`mmm-mode-ext-classes-alist', which will cause them to be used by
+default in the following modes:
+
+    doctest-docstring:  python-mode
+    doctest-example:    rst-mode
+
+If FIX-MMM-FONTIFY-REGION-BUG is true, then register a hook that will
+fix a bug in `mmm-fontify-region' that affects some (but not all)
+versions of emacs.  (See `doctest-fixed-mmm-fontify-region' for more
+info.)"
+  (interactive)
+  (require 'mmm-auto)
+  (mmm-add-classes
+   '(
+     ;; === doctest-docstring ===
+     (doctest-docstring :submode doctest-mode
+      
+      ;; The front is any triple-quote.  Include it in the submode region,
+      ;; to prevent clashes between the two syntax tables over quotes.
+      :front "\\(\"\"\"\\|'''\\)" :include-front t
+      
+      ;; The back matches the front.  Include just the first character
+      ;; of the quote.  If we didn't include at least one quote, then
+      ;; the outer modes quote-counting would be thrown off.  But if
+      ;; we include all three, we run into a bug in mmm-mode.  See
+      ;; <http://tinyurl.com/2fa83w> for more info about the bug.
+      :save-matches t :back "~1" :back-offset 1 :end-not-begin t
+      
+      ;; Define a skeleton for entering new docstrings.
+      :insert ((?d docstring nil @ "\"\"" @ "\"" \n
+                   _ \n "\"" @ "\"\"" @)))
+     
+     ;; === doctest-example ===
+     (doctest-example
+      :submode doctest-mode
+      ;; The front is an optionally indented prompt.
+      :front "^[ \t]*>>>" :include-front t
+      ;; The back is a blank line.
+      :back "^[ \t]*$"
+      ;; Define a skeleton for entering new docstrings.
+      :insert ((?e doctest-example nil
+                   @ @ "    >>> " _ "\n\n" @ @)))))
+  
+  ;; Register some local variables that need to be saved.
+  (add-to-list 'mmm-save-local-variables
+               '(doctest-results-buffer buffer))
+  (add-to-list 'mmm-save-local-variables
+               '(doctest-example-markers buffer))
+
+  ;; Register association with modes, if requested.
+  (when add-mode-ext-classes
+    (mmm-add-mode-ext-class 'python-mode nil 'doctest-docstring)
+    (mmm-add-mode-ext-class 'rst-mode nil 'doctest-example))
+
+  ;; Fix the buggy mmm-fontify-region, if requested.
+  (when fix-mmm-fontify-region-bug
+    (add-hook 'mmm-mode-hook 'doctest-fix-mmm-fontify-region-bug)))
+
+(defvar doctest-old-mmm-fontify-region 'nil
+  "Used to hold the original definition of `mmm-fontify-region' when it
+is rebound by `doctest-fix-mmm-fontify-region-bug'.")
+
+(defun doctest-fix-mmm-fontify-region-bug ()
+  "A function for `mmm-mode-hook' which fixes a potential bug in
+`mmm-fontify-region' by using `doctest-fixed-mmm-fontify-region'
+instead.  (See `doctest-fixed-mmm-fontify-region' for more info.)"
+  (setq font-lock-fontify-region-function
+        'doctest-fixed-mmm-fontify-region))
+
+(defun doctest-fixed-mmm-fontify-region (start stop &optional loudly)
+  "A replacement for `mmm-fontify-region', which fixes a bug caused by
+versions of emacs where post-command-hooks are run *before*
+fontification.  `mmm-mode' assumes that its post-command-hook will be
+run after fontification; and if it's not, then mmm-mode can end up
+with the wrong local variables, keymap, etc. after fontification.  We
+fix that here by redefining `mmm-fontify-region' to remember what
+submode overlay it started in; and to return to that overlay after
+fontification is complete.  The original definition of
+`mmm-fontify-region' is stored in `doctest-old-mmm-fontify-region'."
+  (let ((overlay mmm-current-overlay))
+    (mmm-fontify-region start stop loudly)
+    (if (and overlay (or (< (point) (overlay-start overlay))
+                         (> (point) (overlay-end overlay))))
+        (goto-char (overlay-start overlay)))
+    (mmm-update-submode-region)))
+
+(defun doctest-in-mmm-docstring-overlay ()
+  (and (featurep 'mmm-auto)
+       (mmm-overlay-at (point))
+       (save-excursion
+         (goto-char (overlay-start (mmm-overlay-at (point))))
+         (looking-at "\"\"\"\\|\'\'\'"))))
+
+(defun doctest-narrow-to-mmm-overlay ()
+  "If we're in an mmm-mode overlay, then narrow to that overlay.
+This is useful, e.g., to keep from interpreting the close-quote of a
+docstring as part of the example's output."
+  (let ((bounds (doctest-mmm-overlay-bounds)))
+    (when bounds (narrow-to-region (car bounds) (cdr bounds)))))
+
+(defun doctest-default-margin-in-mmm-docstring-overlay ()
+  (save-excursion
+    (let ((pos (car (doctest-mmm-overlay-bounds))))
+      (goto-char pos)
+      (when (doctest-looking-back "\"\"\"\\|\'\'\'")
+        (setq pos (- pos 3)))
+      (beginning-of-line)
+      (- pos (point)))))
+
+(defun doctest-mmm-overlay-bounds ()
+  (when (featurep 'mmm-auto)
+    (let ((overlay (mmm-overlay-at (point))))
+      (when overlay
+        (let ((start (overlay-start overlay))
+              (end (overlay-end overlay)))
+          (when (doctest-in-mmm-docstring-overlay)
+            (save-excursion
+              (goto-char start)
+              (re-search-forward "[\"\']*")
+              (setq start (point))
+              (goto-char end)
+              (while (doctest-looking-back "[\"\']")
+                (backward-char 1))
+              (setq end (point))))
+          (cons start end))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Helper functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun doctest-on-source-line-p (&optional prompt)
+  "Return true if the current line is a source line.  The optional
+argument prompt can be used to specify which type of source
+line (... or >>>)."
+  (save-excursion
+    (beginning-of-line)
+    ;; Check if we're looking at a prompt (of the right type).
+    (when (and (looking-at doctest-prompt-re)
+               (or (null prompt)
+                   (equal prompt (substring (match-string 2) 0 3))))
+      ;; Scan backwards to make sure there's a >>> somewhere.  Otherwise,
+      ;; this might be a '...' in the text or in an example's output.
+      (while (looking-at "^[ \t]*[.][.][.]")
+        (forward-line -1))
+      (looking-at "^[ \t]*>>>"))))
+
+(defun doctest-on-empty-source-line-p ()
+  "Return true if the current line contains a bare prompt."
+  (save-excursion
+    (beginning-of-line)
+    (and (doctest-on-source-line-p)
+         (looking-at (concat doctest-prompt-re "$")))))
+
+(defun doctest-on-output-line-p ()
+  "Return true if the current line is an output line."
+  (save-excursion
+    (beginning-of-line)
+    ;; The line must not be blank or a source line.
+    (when (not (or (doctest-on-source-line-p) (looking-at "[ \t]*$")))
+      ;; The line must follow a source line, with no intervening blank
+      ;; lines.
+      (while (not (or (doctest-on-source-line-p) (looking-at "[ \t]*$")
+                      (= (point) (point-min))))
+        (forward-line -1))
+      (doctest-on-source-line-p))))
+
+(defun doctest-find-output-line (&optional limit)
+  "Move forward to the next doctest output line (staying within
+the given bounds).  Return the character position of the doctest
+output line if one was found, and false otherwise."
+  (let ((found-it nil) ; point where we found an output line
+	(limit (or limit (point-max)))) ; default value for limit
+    (save-excursion
+      ;; Keep moving forward, one line at a time, until we find a
+      ;; doctest output line.
+      (while (and (not found-it) (< (point) limit) (not (eobp)))
+	(if (and (not (eolp)) (doctest-on-output-line-p))
+	    (setq found-it (point))
+	  (forward-line))))
+    ;; If we found a doctest output line, then go to it.
+    (if found-it (goto-char found-it))))
+
+(defun doctest-line-indentation ()
+  "Helper for doctest-replace-output: return the whitespace indentation
+at the beginning of this line."
+  (save-excursion
+    (end-of-line)
+    (re-search-backward "^\\( *\\)" nil t)
+    (match-string 1)))
+
+(defun doctest-optionflags (&optional diff)
+  "Return a string describing the optionflags that should be used
+by doctest.  If DIFF is non-nil, then add the REPORT_UDIFF flag."
+  (let ((flags "0"))
+    (dolist (flag doctest-optionflags)
+      (setq flags (concat flags "|" flag)))
+    (if diff (concat flags "|" "REPORT_UDIFF") flags)))
+  
+(defun doctest-results-loc-re ()
+  "Return the regexp that should be used to look for doctest example
+location markers in doctest's output (based on which version of
+doctest was used"
+  (cond
+   ((equal doctest-results-py-version 'py21)
+    doctest-py21-results-loc-re)
+   ((equal doctest-results-py-version 'py24)
+    doctest-py24-results-loc-re)
+   (t (error "bad value for doctest-results-py-version"))))
+
+(defun doctest-results-buffer-name ()
+  "Return the buffer name that should be used for the doctest results
+buffer.  This is computed from the variable
+`doctest-results-buffer-name'."
+  (doctest-replace-regexp-in-string
+   "%[nfN]"
+   (lambda (sym)
+     (cond ((equal sym "%n") (buffer-name))
+           ((equal sym "%N") (doctest-replace-regexp-in-string
+                              "[.]doctest$" "" (buffer-name) t))
+           ((equal sym "%f") (buffer-file-name))))
+   doctest-results-buffer-name t))
+
+(defun doctest-script (input-file globs-file diff)
+  "..."
+  (doctest-replace-regexp-in-string
+   "%[tnflm]"
+   (lambda (sym)
+     (cond ((equal sym "%n") (buffer-name))
+           ((equal sym "%f") (buffer-file-name))
+           ((equal sym "%l") (doctest-optionflags diff))
+           ((equal sym "%t") input-file)
+           ((equal sym "%m") (or globs-file ""))))
+   doctest-script t))
+
+(defun doctest-hide-example-source ()
+  "Delete the source code listings from the results buffer (since it's
+easy enough to see them in the original buffer)"
+  (save-excursion
+    (set-buffer doctest-results-buffer)
+    (toggle-read-only 0)
+    (goto-char (point-min))
+    (while (re-search-forward doctest-example-source-re nil t)
+      (replace-match "" nil nil))
+    (toggle-read-only t)))
+
+(defun doctest-results-buffer-valid-p ()
+  "Return true if this buffer has a live results buffer; and that
+results buffer reports this buffer as its source buffer.  (Two
+buffers in doctest-mode might point to the same results buffer;
+but only one of them will be equal to that results buffer's
+source buffer."
+  (let ((cur-buf (current-buffer)))
+    (and (buffer-live-p doctest-results-buffer)
+         (save-excursion
+           (set-buffer doctest-results-buffer)
+           (equal cur-buf doctest-source-buffer)))))
+
+(defun doctest-update-mode-line (value)
+  "Update the doctest mode line with the given string value.  This
+is used to display information about asynchronous processes that
+are run by doctest-mode."
+  (setq doctest-mode-line-process
+        value)
+  (force-mode-line-update t))
+
+(defun doctest-version ()
+  "Echo the current version of `doctest-mode' in the minibuffer."
+  (interactive)
+  (message "Using `doctest-mode' version %s" doctest-version))
+
+(defun doctest-warn (msg &rest args)
+  "Display a doctest warning message."
+  (if (fboundp 'display-warning)
+      (display-warning 'doctest (apply 'format msg args))
+    (apply 'message msg args)))
+
+(defun doctest-debug (msg &rest args)
+  "Display a doctest debug message."
+  (if (fboundp 'display-warning)
+      (display-warning 'doctest (apply 'format msg args) 'debug)
+    (apply 'message msg args)))
+
+(defvar doctest-serial-number 0) ;used if broken-temp-names.
+(defun doctest-temp-name ()
+  "Return a new temporary filename, for use in calling doctest."
+  (if (memq 'broken-temp-names features)
+      (let
+	  ((sn doctest-serial-number)
+	   (pid (and (fboundp 'emacs-pid) (emacs-pid))))
+	(setq doctest-serial-number (1+ doctest-serial-number))
+	(if pid
+	    (format "doctest-%d-%d" sn pid)
+	  (format "doctest-%d" sn)))
+    (make-temp-name "doctest-")))
+
+(defun doctest-fontify-line (charpos)
+  "Run font-lock-fontify-region on the line containing the given
+position."
+  (if (and charpos (functionp 'font-lock-fontify-region))
+      (save-excursion
+        (goto-char charpos)
+        (let ((beg (progn (beginning-of-line) (point)))
+              (end (progn (end-of-line) (point))))
+          (font-lock-fontify-region beg end)))))
+
+(defun doctest-do-auto-fill ()
+  "If the current line is a soucre line or an output line, do nothing.
+Otherwise, call (do-auto-fill)."
+  (cond
+   ;; Don't wrap source lines.
+   ((doctest-on-source-line-p) nil)
+   ;; Don't wrap output lines
+   ((doctest-on-output-line-p) nil)
+   ;; Wrap all other lines
+   (t (do-auto-fill))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Emacs Compatibility Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Define compatible versions of functions that are defined
+;; for some versions of emacs but not others.
+
+;; Backwards compatibility: looking-back
+(cond ((fboundp 'looking-back) ;; Emacs 22.x
+       (defalias 'doctest-looking-back 'looking-back))
+      (t
+       (defun doctest-looking-back (regexp)
+         "Return true if text before point matches REGEXP."
+         (save-excursion
+           (let ((orig-pos (point)))
+             ;; Search backwards for the regexp.
+             (if (re-search-backward regexp nil t)
+                 ;; Check if it ends at the original point.
+                 (= orig-pos (match-end 0))))))))
+
+;; Backwards compatibility: replace-regexp-in-string
+(cond ((fboundp 'replace-regexp-in-string)
+       (defalias 'doctest-replace-regexp-in-string 'replace-regexp-in-string))
+      (t ;; XEmacs 21.x or Emacs 20.x
+       (defun doctest-replace-regexp-in-string
+         (regexp rep string &optional fixedcase literal)
+         "Replace all matches for REGEXP with REP in STRING."
+         (let ((start 0))
+           (while (and (< start (length string))
+                       (string-match regexp string start))
+             (setq start (+ (match-end 0) 1))
+             (let ((newtext (if (functionp rep)
+                                (save-match-data
+                                  (funcall rep (match-string 0 string)))
+                              rep)))
+               (setq string (replace-match newtext fixedcase
+                                           literal string)))))
+         string)))
+
+;; Backwards compatibility: line-number
+(cond ((fboundp 'line-number) ;; XEmacs
+       (defalias 'doctest-line-number 'line-number))
+      ((fboundp 'line-number-at-pos) ;; Emacs 22.x
+       (defalias 'doctest-line-number 'line-number-at-pos))
+      (t ;; Emacs 21.x
+       (defun doctest-line-number (&optional pos)
+         "Return the line number of POS (default=point)."
+         (1+ (count-lines 1
+               (save-excursion (progn (beginning-of-line)
+                                      (or pos (point)))))))))
+
+;; Backwards compatibility: process-live-p
+(cond ((fboundp 'process-live-p) ;; XEmacs
+       (defalias 'doctest-process-live-p 'process-live-p))
+      (t ;; Emacs
+       (defun doctest-process-live-p (process)
+         (and (processp process)
+              (equal (process-status process) 'run)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Doctest Results Mode (output of doctest-execute-buffer)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Register the font-lock keywords (xemacs)
+(put 'doctest-results-mode 'font-lock-defaults 
+     '(doctest-results-font-lock-keywords))
+
+;; Register the font-lock keywords (older versions of gnu emacs)
+(when (boundp 'font-lock-defaults-alist)
+  (add-to-list 'font-lock-defaults-alist
+               '(doctest-results-mode doctest-results-font-lock-keywords 
+                                      nil nil nil nil)))
+
+(defvar doctest-selected-failure nil
+  "The location of the currently selected failure.
+This variable is uffer-local to doctest-results-mode buffers.")
+
+(defvar doctest-source-buffer nil
+  "The buffer that spawned this one.
+This variable is uffer-local to doctest-results-mode buffers.")
+
+(defvar doctest-results-py-version nil
+  "A symbol indicating which version of Python was used to generate
+the results in a doctest-results-mode buffer.  Can be either the
+symbol `py21' or the symbol `py24'.
+This variable is uffer-local to doctest-results-mode buffers.")
+
+;; Keymap for doctest-results-mode.
+(defconst doctest-results-mode-map 
+  (let ((map (make-keymap)))
+    (define-key map [return] 'doctest-select-failure)
+    map) 
+  "Keymap for doctest-results-mode.")
+
+;; Syntax table for doctest-results-mode.
+(defvar doctest-results-mode-syntax-table nil
+  "Syntax table used in `doctest-results-mode' buffers.")
+(when (not doctest-results-mode-syntax-table)
+  (setq doctest-results-mode-syntax-table (make-syntax-table))
+  (dolist (entry '(("(" . "()") ("[" . "(]") ("{" . "(}")
+                   (")" . ")(") ("]" . ")[") ("}" . "){")
+                   ("$%&*+-/<=>|'\"`" . ".") ("_" . "w")))
+    (dolist (char (string-to-list (car entry)))
+      (modify-syntax-entry char (cdr entry)
+                           doctest-results-mode-syntax-table))))
+
+;; Define the mode
+(defun doctest-results-mode ()
+  "A major mode used to display the results of running doctest.
+See `doctest-mode'.
+
+\\{doctest-results-mode-map}"
+  (interactive)
+  
+  ;; Declare local variables.
+  (kill-all-local-variables)
+  (make-local-variable 'font-lock-defaults)
+  (make-local-variable 'doctest-selected-failure)
+  (make-local-variable 'doctest-source-buffer)
+  (make-local-variable 'doctest-results-py-version)
+  
+  ;; Define local variables.
+  (setq major-mode              'doctest-results-mode
+	mode-name               "Doctest-Results"
+        mode-line-process       'doctest-mode-line-process
+        font-lock-defaults      '(doctest-results-font-lock-keywords
+                                  nil nil nil nil))
+  ;; Define keymap.
+  (use-local-map doctest-results-mode-map)
+  
+  ;; Define the syntax table.
+  (set-syntax-table doctest-results-mode-syntax-table)
+  
+  ;; Enable font-lock mode.
+  (if (featurep 'font-lock) (font-lock-mode 1)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Doctest Mode
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Register the font-lock keywords (xemacs)
+(put 'doctest-mode 'font-lock-defaults '(doctest-font-lock-keywords
+                                         nil nil nil nil))
+
+;; Register the font-lock keywords (older versions of gnu emacs)
+(when (boundp 'font-lock-defaults-alist)
+  (add-to-list 'font-lock-defaults-alist
+               '(doctest-mode doctest-font-lock-keywords
+                              nil nil nil nil)))
+  
+(defvar doctest-results-buffer nil
+  "The output buffer for doctest-mode.
+This variable is buffer-local to doctest-mode buffers.")
+
+(defvar doctest-example-markers nil
+  "A list mapping markers to the line numbers at which they appeared
+in the buffer at the time doctest was last run.  This is used to find
+'original' line numbers, which can be used to search the doctest
+output buffer.  It's encoded as a list of (MARKER . POS) tuples, in
+reverse POS order.
+This variable is buffer-local to doctest-mode buffers.")
+
+;; These are global, since we only one run process at a time:
+(defvar doctest-async-process nil
+  "The process object created by the asynchronous doctest process")
+(defvar doctest-async-process-tempfiles nil
+  "A list of tempfile names created by the asynchronous doctest process")
+(defvar doctest-async-process-buffer nil
+  "The source buffer for the asynchronous doctest process")
+(defvar doctest-mode-line-process ""
+  "A string displayed on the modeline, to indicate when doctest is
+running asynchronously.")
+
+;; Keymap for doctest-mode.  n.b.: we intentionally define [tab]
+;; rather than overriding indent-line-function, since we don't want
+;; doctest-indent-source-line to be called by do-auto-fill.
+(defconst doctest-mode-map 
+  (let ((map (make-keymap)))
+    (define-key map [backspace] 'doctest-electric-backspace)
+    (define-key map [return] 'doctest-newline-and-indent)
+    (define-key map [tab] 'doctest-indent-source-line)
+    (define-key map ":" 'doctest-electric-colon)
+    (define-key map "\C-c\C-v" 'doctest-version)
+    (define-key map "\C-c\C-c" 'doctest-execute)
+    (define-key map "\C-c\C-d" 'doctest-execute-with-diff)
+    (define-key map "\C-c\C-n" 'doctest-next-failure)
+    (define-key map "\C-c\C-p" 'doctest-prev-failure)
+    (define-key map "\C-c\C-a" 'doctest-first-failure)
+    (define-key map "\C-c\C-e" 'doctest-last-failure)
+    (define-key map "\C-c\C-z" 'doctest-last-failure)
+    (define-key map "\C-c\C-r" 'doctest-replace-output)
+    (define-key map "\C-c|" 'doctest-execute-region)
+    map) 
+  "Keymap for doctest-mode.")
+
+;; Syntax table for doctest-mode.
+(defvar doctest-mode-syntax-table nil
+  "Syntax table used in `doctest-mode' buffers.")
+(when (not doctest-mode-syntax-table)
+  (setq doctest-mode-syntax-table (make-syntax-table))
+  (dolist (entry '(("(" . "()") ("[" . "(]") ("{" . "(}")
+                   (")" . ")(") ("]" . ")[") ("}" . "){")
+                   ("$%&*+-/<=>|'\"`" . ".") ("_" . "w")))
+    (dolist (char (string-to-list (car entry)))
+      (modify-syntax-entry char (cdr entry) doctest-mode-syntax-table))))
+
+;; Use doctest mode for files ending in .doctest
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.doctest$" . doctest-mode))
+
+;;;###autoload
+(defun doctest-mode ()
+  "A major mode for editing text files that contain Python
+doctest examples.  Doctest is a testing framework for Python that
+emulates an interactive session, and checks the result of each
+command.  For more information, see the Python library reference:
+<http://docs.python.org/lib/module-doctest.html>
+
+`doctest-mode' defines three kinds of line, each of which is
+treated differently:
+
+  - 'Source lines' are lines consisting of a Python prompt
+    ('>>>' or '...'), followed by source code.  Source lines are
+    colored (similarly to `python-mode') and auto-indented.
+
+  - 'Output lines' are non-blank lines immediately following
+    source lines.  They are colored using several doctest-
+    specific output faces.
+
+  - 'Text lines' are any other lines.  They are not processed in
+    any special way.
+
+\\{doctest-mode-map}"
+  (interactive)
+  
+  ;; Declare local variables.
+  (kill-all-local-variables)
+  (make-local-variable 'font-lock-defaults)
+  (make-local-variable 'doctest-results-buffer)
+  (make-local-variable 'doctest-example-markers)
+  
+  ;; Define local variables.
+  (setq major-mode              'doctest-mode
+        mode-name               "Doctest"
+        mode-line-process       'doctest-mode-line-process
+        font-lock-defaults      '(doctest-font-lock-keywords
+                                  nil nil nil nil))
+  
+  ;; Define keymap.
+  (use-local-map doctest-mode-map)
+
+  ;; Define the syntax table.
+  (set-syntax-table doctest-mode-syntax-table)
+  
+  ;; Enable auto-fill mode.
+  (auto-fill-mode 1)
+  (setq auto-fill-function 'doctest-do-auto-fill)
+
+  ;; Enable font-lock mode.
+  (if (featurep 'font-lock) (font-lock-mode 1))
+
+  ;; Run the mode hook.
+  (run-hooks 'doctest-mode-hook))
+
+(provide 'doctest-mode)
+;;; doctest-mode.el ends here