comparison .elisp/doctest-mode.el @ 19:b5d75594b356

Add support for the ipython-mode stuff and remove vestigial pymacs code.
author Augie Fackler <durin42@gmail.com>
date Mon, 08 Dec 2008 10:58:06 -0600
parents c30d68fbd368
children 014e745b2d04
comparison
equal deleted inserted replaced
18:30467b2328cb 19:b5d75594b356
1 ;;; doctest-mode.el --- Major mode for editing Python doctest files 1 ;;; doctest-mode.el --- Major mode for editing Python doctest files
2 2
3 ;; Copyright (C) 2004-2007 Edward Loper 3 ;; Copyright (C) 2004 Edward Loper
4 4
5 ;; Author: Edward Loper 5 ;; Author: Edward Loper
6 ;; Maintainer: edloper@alum.mit.edu 6 ;; Maintainer: edloper@alum.mit.edu
7 ;; Created: Aug 2004 7 ;; Created: Aug 2004
8 ;; Keywords: python doctest unittest test docstring 8 ;; Keywords: python doctest unittest test docstring
9 9
10 (defconst doctest-version "0.5 alpha" 10 (defconst doctest-version "0.2"
11 "`doctest-mode' version number.") 11 "`doctest-mode' version number.")
12 12
13 ;; This software is provided as-is, without express or implied 13 ;; This software is provided as-is, without express or implied
14 ;; warranty. Permission to use, copy, modify, distribute or sell this 14 ;; warranty. Permission to use, copy, modify, distribute or sell this
15 ;; software, without fee, for any purpose and by any individual or 15 ;; software, without fee, for any purpose and by any individual or
20 ;; doctest examples. Doctest is a testing framework for Python that 20 ;; doctest examples. Doctest is a testing framework for Python that
21 ;; emulates an interactive session, and checks the result of each 21 ;; emulates an interactive session, and checks the result of each
22 ;; command. For more information, see the Python library reference: 22 ;; command. For more information, see the Python library reference:
23 ;; <http://docs.python.org/lib/module-doctest.html> 23 ;; <http://docs.python.org/lib/module-doctest.html>
24 24
25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 25 ;; Known bugs:
26 ;;; Table of Contents 26 ;; - Some places assume prompts are 4 chars (but they can be 3
27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 27 ;; if they're bare).
28 ;; 1. Customization: use-editable variables to customize 28 ;; - String literals are not colored correctly. (We need to color
29 ;; doctest-mode. 29 ;; string literals on source lines, but *not* output lines or
30 ;; 30 ;; text lines; this is hard to do.)
31 ;; 2. Fonts: defines new font-lock faces. 31 ;; - Output lines starting with "..." are mistakenly interpreted
32 ;; 32 ;; as (continuation) source lines.
33 ;; 3. Constants: various consts (mainly regexps) used by the rest 33
34 ;; of the code. 34 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
35 ;; 35 ;; Customizable Constants
36 ;; 4. Syntax Highlighting: defines variables and functions used by
37 ;; font-lock-mode to perform syntax highlighting.
38 ;;
39 ;; 5. Source code editing & indentation: commands used to
40 ;; automatically indent, dedent, & handle prompts.
41 ;;
42 ;; 6. Code Execution: commands used to start doctest processes,
43 ;; and handle their output.
44 ;;
45 ;; 7. Markers: functions used to insert markers at the start of
46 ;; doctest examples. These are used to keep track of the
47 ;; correspondence between examples in the source buffer and
48 ;; results in the output buffer.
49 ;;
50 ;; 8. Navigation: commands used to navigate between failed examples.
51 ;;
52 ;; 9. Replace Output: command used to replace a doctest example's
53 ;; expected output with its actual output.
54 ;;
55 ;; 10. Helper functions: various helper functions used by the rest
56 ;; of the code.
57 ;;
58 ;; 11. Emacs compatibility functions: defines compatible versions of
59 ;; functions that are defined for some versions of emacs but not
60 ;; others.
61 ;;
62 ;; 12. Doctest Results Mode: defines doctest-results-mode, which is
63 ;; used for the output generated by doctest.
64 ;;
65 ;; 13. Doctest Mode: defines doctest-mode itself.
66 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
67
68
69 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
70 ;;; Customizable Constants
71 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 36 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
72 37
73 (defgroup doctest nil 38 (defgroup doctest nil
74 "Support for the Python doctest framework" 39 "Support for the Python doctest framework"
75 :group 'languages 40 :group 'languages
96 (funcall ok "/usr/tmp") 61 (funcall ok "/usr/tmp")
97 (funcall ok "/tmp") 62 (funcall ok "/tmp")
98 (funcall ok "/var/tmp") 63 (funcall ok "/var/tmp")
99 (funcall ok ".") 64 (funcall ok ".")
100 (error (concat "Couldn't find a usable temp directory -- " 65 (error (concat "Couldn't find a usable temp directory -- "
101 "set `doctest-temp-directory'")))) 66 "set `doctest-temp-directory'"))))
102 "Directory used for temporary files created when running doctest. 67
68 "*Directory used for temporary files created when running doctest.
103 By default, the first directory from this list that exists and that you 69 By default, the first directory from this list that exists and that you
104 can write into: the value (if any) of the environment variable TMPDIR, 70 can write into: the value (if any) of the environment variable TMPDIR,
105 /usr/tmp, /tmp, /var/tmp, or the current directory." 71 /usr/tmp, /tmp, /var/tmp, or the current directory."
106 :type 'string 72 :type 'string
107 :group 'doctest) 73 :group 'doctest)
108 74
109 (defcustom doctest-hide-example-source nil 75 (defcustom hide-example-source t
110 "If true, then don't display the example source code for each 76 "If true, then don't display the example source code for each
111 failure in the results buffer." 77 failure in the results buffer."
112 :type 'boolean 78 :type 'boolean
113 :group 'doctest) 79 :group 'doctest)
114 80
115 (defcustom doctest-python-command "python" 81 (defcustom doctest-python-command "python"
116 "Shell command used to start the python interpreter" 82 "Shell command used to start the python interpreter")
117 :type 'string 83
118 :group 'doctest) 84 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
119 85 ;; Fonts
120 (defcustom doctest-results-buffer-name "*doctest-output (%N)*"
121 "The name of the buffer used to store the output of the doctest
122 command. This name can contain the following special sequences:
123 %n -- replaced by the doctest buffer's name.
124 %N -- replaced by the doctest buffer's name, with '.doctest'
125 stripped off.
126 %f -- replaced by the doctest buffer's filename."
127 :type 'string
128 :group 'doctest)
129
130 (defcustom doctest-optionflags '()
131 "Option flags for doctest"
132 :group 'doctest
133 :type '(repeat (choice (const :tag "Select an option..." "")
134 (const :tag "Normalize whitespace"
135 "NORMALIZE_WHITESPACE")
136 (const :tag "Ellipsis"
137 "ELLIPSIS")
138 (const :tag "Don't accept True for 1"
139 "DONT_ACCEPT_TRUE_FOR_1")
140 (const :tag "Don't accept <BLANKLINE>"
141 "DONT_ACCEPT_BLANKLINE")
142 (const :tag "Ignore Exception detail"
143 "IGNORE_EXCEPTION_DETAIL")
144 (const :tag "Report only first failure"
145 "REPORT_ONLY_FIRST_FAILURE")
146 )))
147
148 (defcustom doctest-async t
149 "If true, then doctest will be run asynchronously."
150 :type 'boolean
151 :group 'doctest)
152
153 (defcustom doctest-trim-exceptions t
154 "If true, then any exceptions inserted by doctest-replace-output
155 will have the stack trace lines trimmed."
156 :type 'boolean
157 :group 'doctest)
158
159 (defcustom doctest-highlight-strings t
160 "If true, then highlight strings. If you find that doctest-mode
161 is responding slowly when you type, turning this off might help."
162 :type 'boolean
163 :group 'doctest)
164
165 (defcustom doctest-follow-output t
166 "If true, then when doctest is run asynchronously, the output buffer
167 will scroll to display its output as it is generated. If false, then
168 the output buffer not scroll."
169 :type 'boolean
170 :group 'doctest)
171
172 (defvar doctest-mode-hook nil
173 "Hook called by `doctest-mode'.")
174
175 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
176 ;;; Fonts
177 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 86 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
178 87
179 (defface doctest-prompt-face 88 (defface doctest-prompt-face
180 '((((class color) (background dark)) 89 '((((class color) (background dark))
181 (:foreground "#68f")) 90 (:foreground "#68f"))
238 (t (:foreground "#f00"))) 147 (t (:foreground "#f00")))
239 "Face for selected example's prompt" 148 "Face for selected example's prompt"
240 :group 'doctest) 149 :group 'doctest)
241 150
242 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 151 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
243 ;;; Constants 152 ;; Constants
244 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 153 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
245 154
246 (defconst doctest-prompt-re 155 (defconst doctest-prompt-re
247 "^\\(?:\\([ \t]*\\)\\(>>> ?\\|[.][.][.] ?\\)\\([ \t]*\\)\\)" 156 "^\\([ \t]*\\)\\(>>> ?\\|[.][.][.] ?\\)\\([ \t]*\\)"
248 "Regular expression for doctest prompts. It defines three groups: 157 "Regular expression for doctest prompts. It defines three groups:
249 the pre-prompt margin; the prompt; and the post-prompt indentation.") 158 the pre-prompt margin; the prompt; and the post-prompt indentation.")
250 159
251 (defconst doctest-open-block-re 160 (defconst doctest-open-block-re
252 "[^\n]+:[ \t]*\\(#.*\\)?$" 161 "[^\n]+:[ \t]*\\(#.*\\)?$"
253 "Regular expression for a line that opens a block") 162 "Regular expression for a line that opens a block")
254 163
255 (defconst doctest-close-block-re 164 (defconst doctest-close-block-re
256 "\\(return\\|raise\\|break\\|continue\\|pass\\)\\b" 165 "\\(return\\|raise\\|break\\|continue\\|pass\\)\\b"
257 "Regular expression for a line that closes a block") 166 "Regular expression for a line that closes a block")
258
259 (defconst doctest-example-source-re
260 "^Failed example:\n\\(\n\\| [^\n]*\n\\)+"
261 "Regular expression for example source in doctest's output.")
262
263 (defconst doctest-results-divider-re
264 "^\\([*]\\{60,\\}\\)$"
265 "Regular expression for example dividers in doctest's output.")
266
267 (defconst doctest-py24-results-loc-re
268 "^File \"[^\"]+\", line \\([0-9]+\\), in [^\n]+"
269 "Regular expression for example location markers in doctest's output.")
270
271 (defconst doctest-py21-results-loc-re
272 "^from line #\\([0-9]+\\) of [^\n]*"
273 "Regular expression for example location markers in doctest's output,
274 when the output was generated by an old version of doctest.")
275
276 (defconst doctest-results-header-re
277 "^\\([^ \n\t].+:\\|Expected nothing\\|Got nothing\\)$"
278 "Regular expression for example headers in doctest's output.")
279
280 (defconst doctest-traceback-header-re
281 "^[ \t]*Traceback (\\(most recent call last\\|innermost last\\)):"
282 "Regular expression for Python traceback headers.")
283
284 (defconst doctest-py21-results-re
285 "^from line #"
286 "Regular expression used to test which version of doctest was used.")
287
288 ;; nb: There's a bug in Python's traceback.print_exception function
289 ;; which causes SyntaxError exceptions to be displayed incorrectly;
290 ;; which prevents this regexp from matching. But there shouldn't be
291 ;; too many people testing for SyntaxErrors, so I won't worry about
292 ;; it.
293 (defconst doctest-traceback-re
294 (let ((nonprompt
295 ;; This matches any non-blank line that doesn't start with
296 ;; a prompt (... or >>>).
297 (concat
298 "\\(?:[.][.][^.\n]\\|[>][>][^>\n]\\|"
299 "[.][^.\n]\\|[>][^>\n]\\|[^.>\n \t]\\)[^\n]*")))
300 (concat
301 "^\\(\\([ \t]*\\)Traceback "
302 "(\\(?:most recent call last\\|innermost last\\)):\n\\)"
303 "\\(?:\\2[ \t]+[^ \t\n][^\n]*\n\\)*"
304 "\\(\\(?:\\2" nonprompt "\n\\)"
305 "\\(?:\\2[ \t]*" nonprompt "\n\\)*\\)"))
306 "Regular expression that matches a complete exception traceback.
307 It contains three groups: group 1 is the header line; group 2 is
308 the indentation; and group 3 is the exception message.")
309
310 (defconst doctest-blankline-re
311 "^[ \t]*<BLANKLINE>"
312 "Regular expression that matches blank line markers in doctest
313 output.")
314 167
315 (defconst doctest-outdent-re 168 (defconst doctest-outdent-re
316 (concat "\\(" (mapconcat 'identity 169 (concat "\\(" (mapconcat 'identity
317 '("else:" 170 '("else:"
318 "except\\(\\s +.*\\)?:" 171 "except\\(\\s +.*\\)?:"
322 "\\)") 175 "\\)")
323 "Regular expression for a line that should be outdented. Any line 176 "Regular expression for a line that should be outdented. Any line
324 that matches `doctest-outdent-re', but does not follow a line matching 177 that matches `doctest-outdent-re', but does not follow a line matching
325 `doctest-no-outdent-re', will be outdented.") 178 `doctest-no-outdent-re', will be outdented.")
326 179
327 ;; It's not clear to me *why* the behavior given by this definition of
328 ;; doctest-no-outdent-re is desirable; but it's basically just copied
329 ;; from python-mode.
330 (defconst doctest-no-outdent-re 180 (defconst doctest-no-outdent-re
331 (concat 181 (concat
332 "\\(" 182 "\\("
333 (mapconcat 'identity 183 (mapconcat 'identity
334 (list "try:" 184 (list "try:"
343 "\\)") 193 "\\)")
344 "Regular expression matching lines not to outdent after. Any line 194 "Regular expression matching lines not to outdent after. Any line
345 that matches `doctest-outdent-re', but does not follow a line matching 195 that matches `doctest-outdent-re', but does not follow a line matching
346 `doctest-no-outdent-re', will be outdented.") 196 `doctest-no-outdent-re', will be outdented.")
347 197
348 (defconst doctest-script 198 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
349 "\ 199 ;; Colorization support (font-lock mode)
350 from doctest import *
351 import sys
352 if '%m':
353 import imp
354 try:
355 m = imp.load_source('__imported__', '%m')
356 globs = m.__dict__
357 except Exception, e:
358 print ('doctest-mode encountered an error while importing '
359 'the current buffer:\\n\\n %s' % e)
360 sys.exit(1)
361 else:
362 globs = {}
363 doc = open('%t').read()
364 if sys.version_info[:2] >= (2,4):
365 test = DocTestParser().get_doctest(doc, globs, '%n', '%f', 0)
366 r = DocTestRunner(optionflags=%l)
367 r.run(test)
368 else:
369 Tester(globs=globs).runstring(doc, '%f')"
370 ;; Docstring:
371 "Python script used to run doctest.
372 The following special sequences are defined:
373 %n -- replaced by the doctest buffer's name.
374 %f -- replaced by the doctest buffer's filename.
375 %l -- replaced by the doctest flags string.
376 %t -- replaced by the name of the tempfile containing the doctests."
377 )
378
379 (defconst doctest-keyword-re
380 (let* ((kw1 (mapconcat 'identity
381 '("and" "assert" "break" "class"
382 "continue" "def" "del" "elif"
383 "else" "except" "exec" "for"
384 "from" "global" "if" "import"
385 "in" "is" "lambda" "not"
386 "or" "pass" "print" "raise"
387 "return" "while" "yield"
388 )
389 "\\|"))
390 (kw2 (mapconcat 'identity
391 '("else:" "except:" "finally:" "try:")
392 "\\|"))
393 (kw3 (mapconcat 'identity
394 '("ArithmeticError" "AssertionError"
395 "AttributeError" "DeprecationWarning" "EOFError"
396 "Ellipsis" "EnvironmentError" "Exception" "False"
397 "FloatingPointError" "FutureWarning" "IOError"
398 "ImportError" "IndentationError" "IndexError"
399 "KeyError" "KeyboardInterrupt" "LookupError"
400 "MemoryError" "NameError" "None" "NotImplemented"
401 "NotImplementedError" "OSError" "OverflowError"
402 "OverflowWarning" "PendingDeprecationWarning"
403 "ReferenceError" "RuntimeError" "RuntimeWarning"
404 "StandardError" "StopIteration" "SyntaxError"
405 "SyntaxWarning" "SystemError" "SystemExit"
406 "TabError" "True" "TypeError" "UnboundLocalError"
407 "UnicodeDecodeError" "UnicodeEncodeError"
408 "UnicodeError" "UnicodeTranslateError"
409 "UserWarning" "ValueError" "Warning"
410 "ZeroDivisionError" "__debug__"
411 "__import__" "__name__" "abs" "apply" "basestring"
412 "bool" "buffer" "callable" "chr" "classmethod"
413 "cmp" "coerce" "compile" "complex" "copyright"
414 "delattr" "dict" "dir" "divmod"
415 "enumerate" "eval" "execfile" "exit" "file"
416 "filter" "float" "getattr" "globals" "hasattr"
417 "hash" "hex" "id" "input" "int" "intern"
418 "isinstance" "issubclass" "iter" "len" "license"
419 "list" "locals" "long" "map" "max" "min" "object"
420 "oct" "open" "ord" "pow" "property" "range"
421 "raw_input" "reduce" "reload" "repr" "round"
422 "setattr" "slice" "staticmethod" "str" "sum"
423 "super" "tuple" "type" "unichr" "unicode" "vars"
424 "xrange" "zip")
425 "\\|"))
426 (pseudokw (mapconcat 'identity
427 '("self" "None" "True" "False" "Ellipsis")
428 "\\|"))
429 (string (concat "'\\(?:\\\\[^\n]\\|[^\n']*\\)'" "\\|"
430 "\"\\(?:\\\\[^\n]\\|[^\n\"]*\\)\""))
431 (brk "\\(?:[ \t(]\\|$\\)"))
432 (concat
433 ;; Comments (group 1)
434 "\\(#.*\\)"
435 ;; Function & Class Definitions (groups 2-3)
436 "\\|\\b\\(class\\|def\\)"
437 "[ \t]+\\([a-zA-Z_][a-zA-Z0-9_]*\\)"
438 ;; Builtins preceeded by '.'(group 4)
439 "\\|[.][\t ]*\\(" kw3 "\\)"
440 ;; Keywords & builtins (group 5)
441 "\\|\\b\\(" kw1 "\\|" kw2 "\\|"
442 kw3 "\\|" pseudokw "\\)" brk
443 ;; Decorators (group 6)
444 "\\|\\(@[a-zA-Z_][a-zA-Z0-9_]*\\)"
445 ))
446 "A regular expression used for syntax highlighting of Python
447 source code.")
448
449 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
450 ;;; Syntax Highlighting (font-lock mode)
451 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 200 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
452 201
453 ;; Define the font-lock keyword table. 202 ;; Define the font-lock keyword table.
454 (defconst doctest-font-lock-keywords 203 (defconst doctest-font-lock-keywords
455 `( 204 (let ((prompt "^[ \t]*\\(>>>\\|\\.\\.\\.\\)")
456 ;; The following pattern colorizes source lines. In particular, 205 (kw1 (mapconcat 'identity
457 ;; it first matches prompts, and then looks for any of the 206 '("and" "assert" "break" "class"
458 ;; following matches *on the same line* as the prompt. It uses 207 "continue" "def" "del" "elif"
459 ;; the form: 208 "else" "except" "exec" "for"
460 ;; 209 "from" "global" "if" "import"
461 ;; (MATCHER MATCH-HIGHLIGHT 210 "in" "is" "lambda" "not"
462 ;; (ANCHOR-MATCHER nil nil MATCH-HIGHLIGHT)) 211 "or" "pass" "print" "raise"
463 ;; 212 "return" "while" "yield"
464 ;; See the variable documentation for font-lock-keywords for a 213 )
465 ;; description of what each of those means. 214 "\\|"))
466 ("^[ \t]*\\(>>>\\|\\.\\.\\.\\)" 215 (kw2 (mapconcat 'identity
467 (1 'doctest-prompt-face) 216 '("else:" "except:" "finally:" "try:")
468 (doctest-source-matcher 217 "\\|"))
469 nil nil 218 (kw3 (mapconcat 'identity
470 (1 'font-lock-comment-face t t) ; comments 219 '("ArithmeticError" "AssertionError"
471 (2 'font-lock-keyword-face t t) ; def/class 220 "AttributeError" "DeprecationWarning" "EOFError"
472 (3 'font-lock-type-face t t) ; func/class name 221 "Ellipsis" "EnvironmentError" "Exception" "False"
473 ;; group 4 (builtins preceeded by '.') gets no colorization. 222 "FloatingPointError" "FutureWarning" "IOError"
474 (5 'font-lock-keyword-face t t) ; keywords & builtins 223 "ImportError" "IndentationError" "IndexError"
475 (6 'font-lock-preprocessor-face t t) ; decorators 224 "KeyError" "KeyboardInterrupt" "LookupError"
476 (7 'font-lock-string-face t t) ; strings 225 "MemoryError" "NameError" "None" "NotImplemented"
226 "NotImplementedError" "OSError" "OverflowError"
227 "OverflowWarning" "PendingDeprecationWarning"
228 "ReferenceError" "RuntimeError" "RuntimeWarning"
229 "StandardError" "StopIteration" "SyntaxError"
230 "SyntaxWarning" "SystemError" "SystemExit"
231 "TabError" "True" "TypeError" "UnboundLocalError"
232 "UnicodeDecodeError" "UnicodeEncodeError"
233 "UnicodeError" "UnicodeTranslateError"
234 "UserWarning" "ValueError" "Warning"
235 "ZeroDivisionError" "__debug__"
236 "__import__" "__name__" "abs" "apply" "basestring"
237 "bool" "buffer" "callable" "chr" "classmethod"
238 "cmp" "coerce" "compile" "complex" "copyright"
239 "delattr" "dict" "dir" "divmod"
240 "enumerate" "eval" "execfile" "exit" "file"
241 "filter" "float" "getattr" "globals" "hasattr"
242 "hash" "hex" "id" "input" "int" "intern"
243 "isinstance" "issubclass" "iter" "len" "license"
244 "list" "locals" "long" "map" "max" "min" "object"
245 "oct" "open" "ord" "pow" "property" "range"
246 "raw_input" "reduce" "reload" "repr" "round"
247 "setattr" "slice" "staticmethod" "str" "sum"
248 "super" "tuple" "type" "unichr" "unicode" "vars"
249 "xrange" "zip")
250 "\\|"))
251 (pseudokw (mapconcat 'identity
252 '("self" "None" "True" "False" "Ellipsis")
253 "\\|"))
254 (brk "\\([ \t(]\\|$\\)")
255 )
256 `(
257 ;; The following pattern colorizes source lines. In particular,
258 ;; it first matches prompts, and then looks for any of the
259 ;; following matches *on the same line* as the prompt. It uses
260 ;; the form:
261 ;;
262 ;; (MATCHER MATCH-HIGHLIGHT
263 ;; (ANCHOR-MATCHER nil nil MATCH-HIGHLIGHT)
264 ;; ...
265 ;; (ANCHOR-MATCHER nil nil MATCH-HIGHLIGHT))
266 ;;
267 ;; See the variable documentation for font-lock-keywords for a
268 ;; description of what each of those means.
269 (,prompt (1 'doctest-prompt-face)
270 ;; classes
271 ("\\b\\(class\\)[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)"
272 nil nil (1 'font-lock-keyword-face)
273 (2 'font-lock-type-face))
274 ;; functions
275 ("\\b\\(def\\)[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)"
276 nil nil (1 'font-lock-keyword-face) (2 'font-lock-type-face))
277 ;; keywords
278 (,(concat "\\b\\(" kw1 "\\)" brk)
279 nil nil (1 'font-lock-keyword-face))
280 ;; builtins when they don't appear as object attributes
281 (,(concat "\\(\\b\\|[.]\\)\\(" kw3 "\\)" brk)
282 nil nil (2 'font-lock-keyword-face))
283 ;; block introducing keywords with immediately
284 ;; following colons. Yes "except" is in both lists.
285 (,(concat "\\b\\(" kw2 "\\)" brk)
286 nil nil (1 'font-lock-keyword-face))
287 ;; `as' but only in "import foo as bar"
288 ("[ \t]*\\(\\bfrom\\b.*\\)?\\bimport\\b.*\\b\\(as\\)\\b"
289 nil nil (2 'font-lock-keyword-face))
290 ;; pseudo-keywords
291 (,(concat "\\b\\(" pseudokw "\\)" brk)
292 nil nil (1 'font-lock-keyword-face))
293 ;; comments
294 ("\\(#.*\\)"
295 nil nil (1 'font-lock-comment-face)))
296
297 ;; The following pattern colorizes output lines. In particular,
298 ;; it uses doctest-output-line-matcher to check if this is an
299 ;; output line, and if so, it colorizes it, and any special
300 ;; markers it contains.
301 (doctest-output-line-matcher
302 (0 'doctest-output-face t)
303 ("\\.\\.\\." (beginning-of-line) (end-of-line)
304 (0 'doctest-output-marker-face t))
305 ("<BLANKLINE>" (beginning-of-line) (end-of-line)
306 (0 'doctest-output-marker-face t))
307 ("^Traceback (most recent call last):" (beginning-of-line) (end-of-line)
308 (0 'doctest-output-traceback-face t))
309 ("^Traceback (innermost last):" (beginning-of-line) (end-of-line)
310 (0 'doctest-output-traceback-face t))
311 )
312
313 ;; A PS1 prompt followed by a non-space is an error.
314 ("^[ \t]*\\(>>>[^ \t\n][^\n]*\\)" (1 'font-lock-warning-face t))
315
316 ;; Selected example (to highlight selected failure)
317 (doctest-selection-matcher (0 'doctest-selection-face t))
477 )) 318 ))
478 319 "Expressions to highlight in Doctest mode.")
479 ;; The following pattern colorizes output lines. In particular,
480 ;; it uses doctest-output-line-matcher to check if this is an
481 ;; output line, and if so, it colorizes it, and any special
482 ;; markers it contains.
483 (doctest-output-line-matcher
484 (0 'doctest-output-face t)
485 ("\\.\\.\\." (beginning-of-line) (end-of-line)
486 (0 'doctest-output-marker-face t))
487 (,doctest-blankline-re (beginning-of-line) (end-of-line)
488 (0 'doctest-output-marker-face t))
489 (doctest-traceback-line-matcher (beginning-of-line) (end-of-line)
490 (0 'doctest-output-traceback-face t))
491 (,doctest-traceback-header-re (beginning-of-line) (end-of-line)
492 (0 'doctest-output-traceback-face t))
493 )
494
495 ;; A PS1 prompt followed by a non-space is an error.
496 ("^[ \t]*\\(>>>[^ \t\n][^\n]*\\)" (1 'font-lock-warning-face t))
497 )
498 "Expressions to highlight in doctest-mode.")
499
500 (defconst doctest-results-font-lock-keywords
501 `((,doctest-results-divider-re
502 (0 'doctest-results-divider-face))
503 (,doctest-py24-results-loc-re
504 (0 'doctest-results-loc-face))
505 (,doctest-results-header-re
506 (0 'doctest-results-header-face))
507 (doctest-results-selection-matcher
508 (0 'doctest-results-selection-face t)))
509 "Expressions to highlight in doctest-results-mode.")
510 320
511 (defun doctest-output-line-matcher (limit) 321 (defun doctest-output-line-matcher (limit)
512 "A `font-lock-keyword' MATCHER that returns t if the current 322 "A `font-lock-keyword' MATCHER that returns t if the current
513 line is the expected output for a doctest example, and if so, 323 line is the expected output for a doctest example, and if so,
514 sets `match-data' so that group 0 spans the current line." 324 sets `match-data' so that group 0 spans the current line."
515 ;; The real work is done by doctest-find-output-line. 325 ;; The real work is done by find-doctest-output-line.
516 (when (doctest-find-output-line limit) 326 (when (find-doctest-output-line limit)
517 ;; If we found one, then mark the entire line. 327 ;; If we found one, then mark the entire line.
518 (beginning-of-line) 328 (beginning-of-line)
519 (re-search-forward "[^\n]*" limit))) 329 (search-forward-regexp "[^\n]*" limit)))
520 330
521 (defun doctest-traceback-line-matcher (limit) 331 ;; [XX] Under construction.
522 "A `font-lock-keyword' MATCHER that returns t if the current line is 332 (defun doctest-selection-matcher (limit)
523 the beginning of a traceback, and if so, sets `match-data' so that 333 (let (found-it)
524 group 0 spans the entire traceback. n.b.: limit is ignored." 334 (while (and (not found-it)
525 (beginning-of-line) 335 (search-forward-regexp "^[ \t]*\\(>>>\\|[.][.][.]\\)"
526 (when (looking-at doctest-traceback-re) 336 limit t))
527 (goto-char (match-end 0)) 337 (if (get-text-property (point) 'doctest-selected)
528 t)) 338 (setq found-it t)))
529 339 found-it))
530 (defun doctest-source-matcher (limit) 340
531 "A `font-lock-keyword' MATCHER that returns t if the current line 341 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
532 contains a Python source expression that should be highlighted 342 ;; Source line indentation
533 after the point. If so, it sets `match-data' to cover the string
534 literal. The groups in `match-data' should be interpreted as follows:
535
536 Group 1: comments
537 Group 2: def/class
538 Group 3: function/class name
539 Group 4: builtins preceeded by '.'
540 Group 5: keywords & builtins
541 Group 6: decorators
542 Group 7: strings
543 "
544 (let ((matchdata nil))
545 ;; First, look for string literals.
546 (when doctest-highlight-strings
547 (save-excursion
548 (when (doctest-string-literal-matcher limit)
549 (setq matchdata
550 (list (match-beginning 0) (match-end 0)
551 nil nil nil nil nil nil nil nil nil nil nil nil
552 (match-beginning 0) (match-end 0))))))
553 ;; Then, look for other keywords. If they occur before the
554 ;; string literal, then they take precedence.
555 (save-excursion
556 (when (and (re-search-forward doctest-keyword-re limit t)
557 (or (null matchdata)
558 (< (match-beginning 0) (car matchdata))))
559 (setq matchdata (match-data))))
560 (when matchdata
561 (set-match-data matchdata)
562 (goto-char (match-end 0))
563 t)))
564
565 (defun doctest-string-literal-matcher (limit &optional debug)
566 "A `font-lock-keyword' MATCHER that returns t if the current line
567 contains a string literal starting at or after the point. If so, it
568 expands `match-data' to cover the string literal. This matcher uses
569 `doctest-statement-info' to collect information about strings that
570 continue over multiple lines. It therefore might be a little slow for
571 very large statements."
572 (let* ((stmt-info (doctest-statement-info))
573 (quotes (reverse (nth 5 stmt-info)))
574 (result nil))
575 (if debug (doctest-debug "quotes %s" quotes))
576 (while (and quotes (null result))
577 (let* ((quote (pop quotes))
578 (start (car quote))
579 (end (min limit (or (cdr quote) limit))))
580 (if debug (doctest-debug "quote %s-%s pt=%s lim=%s"
581 start end (point) limit))
582 (when (or (and (<= (point) start) (< start limit))
583 (and (< start (point)) (< (point) end)))
584 (setq start (max start (point)))
585 (set-match-data (list start end))
586 (if debug (doctest-debug "marking string %s" (match-data)))
587 (goto-char end)
588 (setq result t))))
589 result))
590
591 (defun doctest-results-selection-matcher (limit)
592 "Matches from `doctest-selected-failure' to the end of the
593 line. This is used to highlight the currently selected failure."
594 (when (and doctest-selected-failure
595 (<= (point) doctest-selected-failure)
596 (< doctest-selected-failure limit))
597 (goto-char doctest-selected-failure)
598 (re-search-forward "[^\n]+" limit)))
599
600 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
601 ;;; Source code editing & indentation
602 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 343 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
603 344
604 (defun doctest-indent-source-line (&optional dedent-only) 345 (defun doctest-indent-source-line (&optional dedent-only)
605 "Re-indent the current line, as doctest source code. I.e., add a 346 "Re-indent the current line, as doctest source code. I.e., add a
606 prompt to the current line if it doesn't have one, and re-indent the 347 prompt to the current line if it doesn't have one, and re-indent the
613 (let ((new-indent (doctest-current-source-line-indentation dedent-only)) 354 (let ((new-indent (doctest-current-source-line-indentation dedent-only))
614 (new-margin (doctest-current-source-line-margin)) 355 (new-margin (doctest-current-source-line-margin))
615 (line-had-prompt (looking-at doctest-prompt-re))) 356 (line-had-prompt (looking-at doctest-prompt-re)))
616 ;; Delete the old prompt (if any). 357 ;; Delete the old prompt (if any).
617 (when line-had-prompt 358 (when line-had-prompt
618 (goto-char (match-beginning 2)) 359 (goto-char (match-end 1))
619 (delete-char (- (match-end 2) (match-beginning 2)))) 360 (delete-char 4))
620 ;; Delete the old indentation. 361 ;; Delete the old indentation.
621 (delete-backward-char (skip-chars-forward " \t")) 362 (delete-backward-char (skip-chars-forward " \t"))
622 ;; If it's a continuation line, or a new PS1 prompt, 363 ;; If it's a continuation line, or a new PS1 prompt,
623 ;; then copy the margin. 364 ;; then copy the margin.
624 (when (or new-indent (not line-had-prompt)) 365 (when (or new-indent (not line-had-prompt))
636 377
637 (defun doctest-current-source-line-indentation (&optional dedent-only) 378 (defun doctest-current-source-line-indentation (&optional dedent-only)
638 "Return the post-prompt indent to use for this line. This is an 379 "Return the post-prompt indent to use for this line. This is an
639 integer for a continuation lines, and nil for non-continuation lines." 380 integer for a continuation lines, and nil for non-continuation lines."
640 (save-excursion 381 (save-excursion
641 ;; Examine the previous doctest line (if present). 382 (let ((prev-line-indent 0)
642 (let* ((prev-stmt-info (doctest-prev-statement-info)) 383 (curr-line-indent 0)
643 (prev-stmt-indent (nth 0 prev-stmt-info)) 384 (prev-line-opens-block nil)
644 (continuation-indent (nth 1 prev-stmt-info)) 385 (prev-line-closes-block nil)
645 (prev-stmt-opens-block (nth 2 prev-stmt-info)) 386 (curr-line-outdented nil))
646 (prev-stmt-closes-block (nth 3 prev-stmt-info))
647 (prev-stmt-blocks-outdent (nth 4 prev-stmt-info))
648 )
649 ;; Examine this doctest line. 387 ;; Examine this doctest line.
650 (let* ((curr-line-indent 0) 388 (beginning-of-line)
651 (curr-line-outdented nil)) 389 (when (looking-at doctest-prompt-re)
652 (beginning-of-line)
653 (when (looking-at doctest-prompt-re)
654 (setq curr-line-indent (- (match-end 3) (match-beginning 3))) 390 (setq curr-line-indent (- (match-end 3) (match-beginning 3)))
655 (goto-char (match-end 3))) 391 (goto-char (match-end 3)))
656 (setq curr-line-outdented (and (looking-at doctest-outdent-re) 392 (setq curr-line-outdented (looking-at doctest-outdent-re))
657 (not prev-stmt-blocks-outdent))) 393 ;; Examine the previous line.
658 ;; Compute the overall indent. 394 (when (= (forward-line -1) 0) ; move up a line
659 (let ((indent (or continuation-indent 395 (when (looking-at doctest-prompt-re) ; is it a source line?
660 (+ prev-stmt-indent 396 (let ((indent-beg (column-at-char (match-beginning 3)))
661 (if curr-line-outdented -4 0) 397 (indent-end (column-at-char (match-end 3))))
662 (if prev-stmt-opens-block 4 0) 398 (setq prev-line-indent (- indent-end indent-beg))
663 (if prev-stmt-closes-block -4 0))))) 399 (goto-char (match-end 3))
664 ;; If dedent-only is true, then make sure we don't indent. 400 (if (looking-at doctest-open-block-re)
665 (when dedent-only 401 (setq prev-line-opens-block t))
666 (setq indent (min indent curr-line-indent))) 402 (if (looking-at doctest-close-block-re)
667 ;; If indent=0 and we're not outdented, then set indent to 403 (setq prev-line-closes-block t))
668 ;; nil (to signify the start of a new source example). 404 (if (looking-at doctest-no-outdent-re)
669 (when (and (= indent 0) 405 (setq curr-line-outdented nil))
670 (not (or curr-line-outdented continuation-indent))) 406 )))
671 (setq indent nil)) 407 (let ((indent (+ prev-line-indent
672 ;; Return the indentation. 408 (if curr-line-outdented -4 0)
673 indent))))) 409 (if prev-line-opens-block 4 0)
674 410 (if prev-line-closes-block -4 0))))
675 (defun doctest-prev-statement-info (&optional debug) 411 ;; If dedent-only is true, then make sure we don't indent.
676 (save-excursion 412 (when dedent-only
677 (forward-line -1) 413 (setq indent (min indent curr-line-indent)))
678 (doctest-statement-info debug))) 414 ;; If indent=0 and we're not outdented, then set indent to
679 415 ;; nil (to signify the start of a new source example).
680 (defun doctest-statement-info (&optional debug) 416 (when (and (= indent 0) (not curr-line-outdented))
681 "Collect info about the previous statement, and return it as a list: 417 (setq indent nil))
682 418 ;; Return the indentation.
683 (INDENT, CONTINUATION, OPENS-BLOCK, CLOSES-BLOCK, BLOCKS-OUTDENT, 419 indent))))
684 QUOTES)
685
686 INDENT -- The indentation of the previous statement (after the prompt)
687
688 CONTINUATION -- If the previous statement is incomplete (e.g., has an
689 open paren or quote), then this is the appropriate indentation
690 level; otherwise, it's nil.
691
692 OPENS-BLOCK -- True if the previous statement opens a Python control
693 block.
694
695 CLOSES-BLOCK -- True if the previous statement closes a Python control
696 block.
697
698 BLOCKS-OUTDENT -- True if the previous statement should 'block the
699 next statement from being considered an outdent.
700
701 QUOTES -- A list of (START . END) pairs for all quotation strings.
702 "
703 (save-excursion
704 (end-of-line)
705 (let ((end (point)))
706 (while (and (doctest-on-source-line-p "...") (= (forward-line -1) 0)))
707 (cond
708 ;; If there's no previous >>> line, then give up.
709 ((not (doctest-on-source-line-p ">>>"))
710 '(0 nil nil nil nil))
711
712 ;; If there is a previous statement, walk through the source
713 ;; code, checking for operators that may be of interest.
714 (t
715 (beginning-of-line)
716 (let* ((quote-mark nil) (nesting 0) (indent-stack '())
717 (stmt-indent 0)
718 (stmt-opens-block nil)
719 (stmt-closes-block nil)
720 (stmt-blocks-outdent nil)
721 (quotes '())
722 (elt-re (concat "\\\\[^\n]\\|"
723 "(\\|)\\|\\[\\|\\]\\|{\\|}\\|"
724 "\"\"\"\\|\'\'\'\\|\"\\|\'\\|"
725 "#[^\n]*\\|" doctest-prompt-re)))
726 (while (re-search-forward elt-re end t)
727 (let* ((elt (match-string 0))
728 (elt-first-char (substring elt 0 1)))
729 (if debug (doctest-debug "Debug: %s" elt))
730 (cond
731 ;; Close quote -- set quote-mark back to nil. The
732 ;; second case is for cases like: ' '''
733 (quote-mark
734 (cond
735 ((equal quote-mark elt)
736 (setq quote-mark nil)
737 (setcdr (car quotes) (point)))
738 ((equal quote-mark elt-first-char)
739 (setq quote-mark nil)
740 (setcdr (car quotes) (point))
741 (backward-char 2))))
742 ;; Prompt -- check if we're starting a new stmt. If so,
743 ;; then collect various info about it.
744 ((string-match doctest-prompt-re elt)
745 (when (and (null quote-mark) (= nesting 0))
746 (let ((indent (- (match-end 3) (match-end 2))))
747 (unless (looking-at "[ \t]*\n")
748 (setq stmt-indent indent)
749 (setq stmt-opens-block
750 (looking-at doctest-open-block-re))
751 (setq stmt-closes-block
752 (looking-at doctest-close-block-re))
753 (setq stmt-blocks-outdent
754 (looking-at doctest-no-outdent-re))))))
755 ;; Open paren -- increment nesting, and update indent-stack.
756 ((string-match "(\\|\\[\\|{" elt-first-char)
757 (let ((elt-pos (point))
758 (at-eol (looking-at "[ \t]*\n"))
759 (indent 0))
760 (save-excursion
761 (re-search-backward doctest-prompt-re)
762 (if at-eol
763 (setq indent (+ 4 (- (match-end 3) (match-end 2))))
764 (setq indent (- elt-pos (match-end 2))))
765 (push indent indent-stack)))
766 (setq nesting (+ nesting 1)))
767 ;; Close paren -- decrement nesting, and pop indent-stack.
768 ((string-match ")\\|\\]\\|}" elt-first-char)
769 (setq indent-stack (cdr indent-stack))
770 (setq nesting (max 0 (- nesting 1))))
771 ;; Open quote -- set quote-mark.
772 ((string-match "\"\\|\'" elt-first-char)
773 (push (cons (- (point) (length elt)) nil) quotes)
774 (setq quote-mark elt)))))
775
776 (let* ((continuation-indent
777 (cond
778 (quote-mark 0)
779 ((> nesting 0) (if (null indent-stack) 0 (car indent-stack)))
780 (t nil)))
781 (result
782 (list stmt-indent continuation-indent
783 stmt-opens-block stmt-closes-block
784 stmt-blocks-outdent quotes)))
785 (if debug (doctest-debug "Debug: %s" result))
786 result)))))))
787 420
788 (defun doctest-current-source-line-margin () 421 (defun doctest-current-source-line-margin ()
789 "Return the pre-prompt margin to use for this source line. This is 422 "Return the pre-prompt margin to use for this source line. This is
790 copied from the most recent source line, or set to 423 copied from the most recent source line, or set to
791 `doctest-default-margin' if there are no preceeding source lines." 424 `doctest-default-margin' if there are no preceeding source lines."
792 (save-excursion 425 (save-excursion
793 (save-restriction 426 (beginning-of-line)
794 (when (doctest-in-mmm-docstring-overlay) 427 (if (search-backward-regexp doctest-prompt-re nil t)
795 (doctest-narrow-to-mmm-overlay)) 428 (let ((margin-beg (column-at-char (match-beginning 1)))
796 (beginning-of-line) 429 (margin-end (column-at-char (match-end 1))))
797 (forward-line -1) 430 (- margin-end margin-beg))
798 (while (and (not (doctest-on-source-line-p)) 431 doctest-default-margin)))
799 (re-search-backward doctest-prompt-re nil t))))
800 (cond ((looking-at doctest-prompt-re)
801 (- (match-end 1) (match-beginning 1)))
802 ((doctest-in-mmm-docstring-overlay)
803 (doctest-default-margin-in-mmm-docstring-overlay))
804 (t
805 doctest-default-margin))))
806 432
807 (defun doctest-electric-backspace () 433 (defun doctest-electric-backspace ()
808 "Delete the preceeding character, level of indentation, or 434 "Delete the preceeding character, level of indentation, or
809 prompt. 435 prompt.
810 436
880 ;; If we're avoiding trailing spaces, then delete WS before point. 506 ;; If we're avoiding trailing spaces, then delete WS before point.
881 (if doctest-avoid-trailing-whitespace 507 (if doctest-avoid-trailing-whitespace
882 (delete-char (- (skip-chars-backward " \t")))) 508 (delete-char (- (skip-chars-backward " \t"))))
883 (cond 509 (cond
884 ;; If we're on an empty prompt, delete it. 510 ;; If we're on an empty prompt, delete it.
885 ((doctest-on-empty-source-line-p) 511 ((on-empty-doctest-source-line)
886 (delete-region (match-beginning 0) (match-end 0)) 512 (delete-region (match-beginning 0) (match-end 0))
887 (insert-char ?\n 1)) 513 (insert-char ?\n 1))
888 ;; If we're on a doctest line, add a new prompt. 514 ;; If we're on a doctest line, add a new prompt.
889 ((doctest-on-source-line-p) 515 ((on-doctest-source-line)
890 (insert-char ?\n 1) 516 (insert-char ?\n 1)
891 (doctest-indent-source-line)) 517 (doctest-indent-source-line))
892 ;; If we're in doctest output, indent to the margin. 518 ;; If we're in doctest output, indent to the margin.
893 ((doctest-on-output-line-p) 519 ((on-doctest-output-line)
894 (insert-char ?\n 1) 520 (insert-char ?\n 1)
895 (insert-char ?\ (doctest-current-source-line-margin))) 521 (insert-char ?\ (doctest-current-source-line-margin)))
896 ;; Otherwise, just add a newline. 522 ;; Otherwise, just add a newline.
897 (t (insert-char ?\n 1)))) 523 (t (insert-char ?\n 1))))
898 524
899 (defun doctest-electric-colon () 525 (defun doctest-electric-colon ()
900 "Insert a colon, and dedent the line when appropriate." 526 "Insert a colon, and dedent the line when appropriate."
901 (interactive "*") 527 (interactive "*")
902 (insert-char ?: 1) 528 (insert-char ?: 1)
903 (when (doctest-on-source-line-p) 529 (when (on-doctest-source-line)
904 (doctest-indent-source-line t))) 530 (doctest-indent-source-line t)))
905 531
906 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 532 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
907 ;;; Code Execution 533 ;; Code Execution
908 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 534 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
909 535
910 (defun doctest-execute () 536 ;; Add support for options (eg diff!)
911 "Run doctest on the current buffer, or on the current docstring
912 if the point is inside an `mmm-mode' `doctest-docstring' region.
913 Display the results in the *doctest-output* buffer."
914 (interactive)
915 (doctest-execute-region (point-min) (point-max) nil t))
916
917 (defun doctest-execute-with-diff ()
918 "Run doctest on the current buffer, or on the current docstring
919 if the point is inside an `mmm-mode' `doctest-docstring' region.
920 Display the results in the *doctest-output* buffer, using diff format."
921 (interactive)
922 (doctest-execute-region (point-min) (point-max) t t))
923
924 (defun doctest-execute-buffer-with-diff ()
925 "Run doctest on the current buffer, and display the results in the
926 *doctest-output* buffer, using the diff format."
927 (interactive)
928 (doctest-execute-region (point-min) (point-max) t nil))
929
930 (defun doctest-execute-buffer () 537 (defun doctest-execute-buffer ()
931 "Run doctest on the current buffer, and display the results in the 538 "Run doctest on the current buffer, and display the results in the
932 *doctest-output* buffer." 539 *doctest-output* buffer."
933 (interactive) 540 (interactive "*")
934 (doctest-execute-region (point-min) (point-max) nil nil)) 541 (setq doctest-results-buffer (get-buffer-create "*doctest-output*"))
935 542 (let* ((temp (concat (doctest-temp-name) ".py"))
936 (defun doctest-execute-region (start end &optional diff 543 (tempfile (expand-file-name temp doctest-temp-directory))
937 check-for-mmm-docstring-overlay) 544 (cur-buf (current-buffer))
938 "Run doctest on the current buffer, and display the results in the 545 (in-buf (get-buffer-create "*doctest-input*"))
939 *doctest-output* buffer." 546 (beg (point-min)) (end (point-max))
940 (interactive "r") 547 (script (concat "from doctest import *\n"
941 ;; If it's already running, give the user a chance to restart it. 548 "doc = open('" tempfile "').read()\n"
942 (when (doctest-process-live-p doctest-async-process) 549 "test = DocTestParser().get_doctest("
943 (when (y-or-n-p "Doctest is already running. Restart it? ") 550 "doc, {}, '" (buffer-name) "', '"
944 (doctest-cancel-async-process) 551 (buffer-file-name) "', 0)\n"
945 (message "Killing doctest..."))) 552 "r = DocTestRunner()\n"
946 (cond 553 "r.run(test)\n"))
947 ((and doctest-async (doctest-process-live-p doctest-async-process)) 554 (cmd (concat doctest-python-command " -c \"" script "\"")))
948 (message "Can't run two doctest processes at once!")) 555 ;; Write buffer to a file.
949 (t 556 (save-excursion
950 (let* ((results-buf-name (doctest-results-buffer-name)) 557 (set-buffer in-buf)
951 (in-docstring (and check-for-mmm-docstring-overlay 558 (insert-buffer-substring cur-buf beg end)
952 (doctest-in-mmm-docstring-overlay))) 559 (write-file tempfile))
953 (temp (doctest-temp-name)) (dir doctest-temp-directory) 560 ;; Run doctest
954 (input-file (expand-file-name (concat temp ".py") dir)) 561 (shell-command cmd doctest-results-buffer)
955 (globs-file (when in-docstring 562 ;; Delete the temp file
956 (expand-file-name (concat temp "-globs.py") dir))) 563 (delete-file tempfile)
957 (cur-buf (current-buffer)) 564 ;; Set mode on output buffer.
958 (in-buf (get-buffer-create "*doctest-input*"))
959 (script (doctest-script input-file globs-file diff)))
960 ;; If we're in a docstring, narrow start & end.
961 (when in-docstring
962 (let ((bounds (doctest-mmm-overlay-bounds)))
963 (setq start (max start (car bounds))
964 end (min end (cdr bounds)))))
965 ;; Write the doctests to a file.
966 (save-excursion
967 (goto-char (min start end))
968 (let ((lineno (doctest-line-number)))
969 (set-buffer in-buf)
970 ;; Add blank lines, to keep line numbers the same:
971 (dotimes (n (- lineno 1)) (insert-string "\n"))
972 ;; Add the selected region
973 (insert-buffer-substring cur-buf start end)
974 ;; Write it to a file
975 (write-file input-file)))
976 ;; If requested, write the buffer to a file for use as globs.
977 (when globs-file
978 (let ((cur-buf-start (point-min)) (cur-buf-end (point-max)))
979 (save-excursion
980 (set-buffer in-buf)
981 (delete-region (point-min) (point-max))
982 (insert-buffer-substring cur-buf cur-buf-start cur-buf-end)
983 (write-file globs-file))))
984 ;; Dispose of in-buf (we're done with it now.
985 (kill-buffer in-buf)
986 ;; Prepare the results buffer. Clear it, if it contains
987 ;; anything, and set its mode.
988 (setq doctest-results-buffer (get-buffer-create results-buf-name))
989 (save-excursion
990 (set-buffer doctest-results-buffer)
991 (toggle-read-only 0)
992 (delete-region (point-min) (point-max))
993 (doctest-results-mode)
994 (setq doctest-source-buffer cur-buf)
995 )
996 ;; Add markers to examples, and record what line number each one
997 ;; starts at. That way, if the input buffer is edited, we can
998 ;; still find corresponding examples in the output.
999 (doctest-mark-examples)
1000
1001 ;; Run doctest
1002 (cond (doctest-async
1003 ;; Asynchronous mode:
1004 (let ((process (start-process "*doctest*" doctest-results-buffer
1005 doctest-python-command
1006 "-c" script)))
1007 ;; Store some information about the process.
1008 (setq doctest-async-process-buffer cur-buf)
1009 (setq doctest-async-process process)
1010 (push input-file doctest-async-process-tempfiles)
1011 (when globs-file
1012 (push globs-file doctest-async-process-tempfiles))
1013 ;; Set up a sentinel to respond when it's done running.
1014 (set-process-sentinel process 'doctest-async-process-sentinel)
1015
1016 ;; Show the output window.
1017 (let ((w (display-buffer doctest-results-buffer)))
1018 (when doctest-follow-output
1019 ;; Insert a newline, which will move the buffer's
1020 ;; point past the process's mark -- this causes the
1021 ;; window to scroll as new output is generated.
1022 (save-current-buffer
1023 (set-buffer doctest-results-buffer)
1024 (insert-string "\n")
1025 (set-window-point w (point)))))
1026
1027 ;; Let the user know the process is running.
1028 (doctest-update-mode-line ":running")
1029 (message "Running doctest...")))
1030 (t
1031 ;; Synchronous mode:
1032 (call-process doctest-python-command nil
1033 doctest-results-buffer t "-c" script)
1034 (doctest-handle-output)
1035 (delete-file input-file)
1036 (when globs-file
1037 (delete-file globs-file))))))))
1038
1039 (defun doctest-handle-output ()
1040 "This function, which is called after the 'doctest' process spawned
1041 by doctest-execute-buffer has finished, checks the doctest results
1042 buffer. If that buffer is empty, it reports no errors and hides it;
1043 if that buffer is not empty, it reports that errors occured, displays
1044 the buffer, and runs doctest-postprocess-results."
1045 ;; If any tests failed, display them.
1046 (cond ((not (buffer-live-p doctest-results-buffer))
1047 (doctest-warn "Results buffer not found!"))
1048 ((> (buffer-size doctest-results-buffer) 1)
1049 (display-buffer doctest-results-buffer)
1050 (doctest-postprocess-results)
1051 (let ((num (length doctest-example-markers)))
1052 (message "%d doctest example%s failed!" num
1053 (if (= num 1) "" "s"))))
1054 (t
1055 (display-buffer doctest-results-buffer)
1056 (delete-windows-on doctest-results-buffer)
1057 (message "All doctest examples passed!"))))
1058
1059 (defun doctest-async-process-sentinel (process state)
1060 "A process sentinel, called when the asynchronous doctest process
1061 completes, which calls doctest-handle-output."
1062 ;; Check to make sure we got the process we're expecting. On
1063 ;; some operating systems, this will end up getting called twice
1064 ;; when we use doctest-cancel-async-process; this check keeps us
1065 ;; from trying to clean up after the same process twice (since we
1066 ;; set doctest-async-process to nil when we're done).
1067 (when (and (equal process doctest-async-process)
1068 (buffer-live-p doctest-async-process-buffer))
1069 (save-current-buffer
1070 (set-buffer doctest-async-process-buffer)
1071 (cond ((not (buffer-live-p doctest-results-buffer))
1072 (doctest-warn "Results buffer not found!"))
1073 ((equal state "finished\n")
1074 (doctest-handle-output)
1075 (let ((window (get-buffer-window
1076 doctest-async-process-buffer t)))
1077 (when window (set-window-point window (point)))))
1078 ((equal state "killed\n")
1079 (message "Doctest killed."))
1080 (t
1081 (message "Doctest failed -- %s" state)
1082 (display-buffer doctest-results-buffer)))
1083 (doctest-update-mode-line "")
1084 (while doctest-async-process-tempfiles
1085 (delete-file (pop doctest-async-process-tempfiles)))
1086 (setq doctest-async-process nil))))
1087
1088 (defun doctest-cancel-async-process ()
1089 "If a doctest process is running, then kill it."
1090 (interactive "")
1091 (when (doctest-process-live-p doctest-async-process)
1092 ;; Update the modeline
1093 (doctest-update-mode-line ":killing")
1094 ;; Kill the process.
1095 (kill-process doctest-async-process)
1096 ;; Run the sentinel. (Depending on what OS we're on, the sentinel
1097 ;; may end up getting called once or twice.)
1098 (doctest-async-process-sentinel doctest-async-process "killed\n")
1099 ))
1100
1101 (defun doctest-postprocess-results ()
1102 "Post-process the doctest results buffer: check what version of
1103 doctest was used, and set doctest-results-py-version accordingly;
1104 turn on read-only mode; filter the example markers; hide the example
1105 source (if `doctest-hide-example-source' is non-nil); and select the
1106 first failure."
1107 (save-excursion
1108 (set-buffer doctest-results-buffer)
1109 ;; Check if we're using an old doctest version.
1110 (goto-char (point-min))
1111 (if (re-search-forward doctest-py21-results-re nil t)
1112 (setq doctest-results-py-version 'py21)
1113 (setq doctest-results-py-version 'py24))
1114 ;; Turn on read-only mode.
1115 (toggle-read-only t))
1116
1117 (doctest-filter-example-markers)
1118 (if doctest-hide-example-source
1119 (doctest-hide-example-source))
1120 (doctest-next-failure 1))
1121
1122 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1123 ;;; Markers
1124 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1125
1126 (defun doctest-mark-examples ()
1127 "Add a marker at the beginning of every (likely) example in the
1128 input buffer; and create a list, `doctest-example-markers',
1129 which maps from markers to the line numbers they originally occured
1130 on. This will allow us to find the corresponding example in the
1131 doctest output, even if the input buffer is edited."
1132 (dolist (marker-info doctest-example-markers)
1133 (set-marker (car marker-info) nil))
1134 (setq doctest-example-markers '())
1135 (save-excursion
1136 (goto-char (point-min))
1137 (while (re-search-forward "^ *>>> " nil t)
1138 (backward-char 4)
1139 (push (cons (point-marker) (doctest-line-number))
1140 doctest-example-markers)
1141 (forward-char 4))))
1142
1143 (defun doctest-filter-example-markers ()
1144 "Remove any entries from `doctest-example-markers' that do not
1145 correspond to a failed example."
1146 (let ((filtered nil) (markers doctest-example-markers))
1147 (save-excursion 565 (save-excursion
1148 (set-buffer doctest-results-buffer) 566 (set-buffer doctest-results-buffer)
1149 (goto-char (point-max)) 567 (doctest-results-mode))
1150 (while (re-search-backward (doctest-results-loc-re) nil t) 568 ;; If any tests failed, display them.
1151 (let ((lineno (string-to-int (match-string 1)))) 569 (cond ((> (buffer-size doctest-results-buffer) 0)
1152 (when (equal doctest-results-py-version 'py21) 570 (message "Test failed!")
1153 (setq lineno (+ lineno 1))) 571 (display-buffer doctest-results-buffer)
1154 (while (and markers (< lineno (cdar markers))) 572 (doctest-postprocess-results))
1155 (set-marker (caar markers) nil) 573 (t
1156 (setq markers (cdr markers))) 574 (message "Test passed!")
1157 (if (and markers (= lineno (cdar markers))) 575 (if (get-buffer-window doctest-results-buffer)
1158 (push (pop markers) filtered) 576 (delete-window (get-buffer-window doctest-results-buffer)))))))
1159 (doctest-warn "Example expected on line %d but not found %s" 577
1160 lineno markers))))) 578 (defun doctest-postprocess-results ()
1161 (dolist (marker-info markers) 579 (doctest-next-failure 1)
1162 (set-marker (car marker-info) nil)) 580 (if hide-example-source
1163 (setq doctest-example-markers filtered))) 581 (hide-example-source)))
1164
1165 (defun doctest-prev-example-marker ()
1166 "Helper for doctest-replace-output: move to the preceeding example
1167 marker, and return the corresponding 'original' lineno. If none is
1168 found, return nil."
1169 (let ((lineno nil)
1170 (pos nil))
1171 (save-excursion
1172 (end-of-line)
1173 (when (re-search-backward "^\\( *\\)>>> " nil t)
1174 (goto-char (match-end 1))
1175 (dolist (marker-info doctest-example-markers)
1176 (when (= (marker-position (car marker-info)) (point))
1177 (setq lineno (cdr marker-info))
1178 (setq pos (point))))))
1179 (unless (null lineno)
1180 (goto-char pos)
1181 lineno)))
1182
1183 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1184 ;;; Navigation
1185 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1186 582
1187 (defun doctest-next-failure (count) 583 (defun doctest-next-failure (count)
1188 "Move to the top of the next failing example, and highlight the 584 "Move to the top of the next failing example, and highlight the
1189 example's failure description in *doctest-output*." 585 example's failure description in *doctest-output*."
1190 (interactive "p") 586 (interactive "p")
1191 (cond 587 (let (lineno)
1192 ((and doctest-async (doctest-process-live-p doctest-async-process)) 588 (cond
1193 (message "Wait for doctest to finish running!")) 589 ((not (buffer-live-p doctest-results-buffer))
1194 ((not (doctest-results-buffer-valid-p)) 590 (message "Run doctest first! (C-c C-c)"))
1195 (message "Run doctest first! (C-c C-c)")) 591 (t
1196 ((equal count 0)
1197 t)
1198 (t
1199 (let ((marker nil) (example-markers doctest-example-markers)
1200 (results-window (display-buffer doctest-results-buffer)))
1201 (save-excursion 592 (save-excursion
1202 (set-buffer doctest-results-buffer) 593 (let ((orig-window (selected-window))
1203 ;; Pick up where we left off. 594 (results-window (display-buffer doctest-results-buffer)))
1204 ;; (nb: doctest-selected-failure is buffer-local) 595 ;; Switch to the results window (so its point gets updated)
1205 (goto-char (or doctest-selected-failure (point-min))) 596 (if results-window (select-window results-window))
1206 ;; Skip past anything on *this* line. 597 ;; Pick up where we left off.
1207 (if (>= count 0) (end-of-line) (beginning-of-line)) 598 ;; (nb: doctest-selected-failure is buffer-local)
1208 ;; Look for the next failure 599 (goto-char (or doctest-selected-failure (point-min)))
1209 (when 600 ;; Skip past anything on *this* line.
1210 (if (>= count 0) 601 (if (>= count 0) (end-of-line) (beginning-of-line))
1211 (re-search-forward (doctest-results-loc-re) nil t count) 602 ;; Look for the next failure
1212 (re-search-backward (doctest-results-loc-re) nil t (- count))) 603 (if (>= count 0)
1213 ;; We found a failure: 604 (re-search-forward doctest-results-loc-re nil t count)
1214 (let ((old-selected-failure doctest-selected-failure)) 605 (re-search-backward doctest-results-loc-re nil t (- count)))
1215 (beginning-of-line) 606 (cond
1216 ;; Extract the line number for the doctest file. 607 ;; We found a failure:
1217 (let ((orig-lineno (string-to-int (match-string 1)))) 608 ((match-string 2)
1218 (when (equal doctest-results-py-version 'py21) 609 (let ((old-selected-failure doctest-selected-failure))
1219 (setq orig-lineno (+ orig-lineno 1))) 610 ;; Extract the line number for the doctest file.
1220 (dolist (marker-info example-markers) 611 (setq lineno (string-to-int (match-string 2)))
1221 (when (= orig-lineno (cdr marker-info)) 612 ;; Store our position for next time.
1222 (setq marker (car marker-info))))) 613 (beginning-of-line)
1223 614 (setq doctest-selected-failure (point))
1224 ;; Update the window cursor. 615 ;; Update selection.
1225 (beginning-of-line) 616 (doctest-fontify-line old-selected-failure)
1226 (set-window-point results-window (point)) 617 (doctest-fontify-line doctest-selected-failure)))
1227 ;; Store our position for next time. 618 ;; We didn't find a failure:
1228 (setq doctest-selected-failure (point)) 619 (t
1229 ;; Update selection. 620 (message "No failures found!")))
1230 (doctest-fontify-line old-selected-failure) 621 ;; Return to the original window
1231 (doctest-fontify-line doctest-selected-failure)))) 622 (select-window orig-window)))))
1232 623
1233 (cond 624 (when lineno
1234 ;; We found a failure -- move point to the selected failure. 625 ;; Move point to the selected failure.
1235 (marker 626 (goto-line lineno)
1236 (goto-char (marker-position marker)) 627 ; ;; Highlight it. [XX] Under construction.
1237 (beginning-of-line)) 628 ; (let ((beg (save-excursion (beginning-of-line) (point)))
1238 ;; We didn't find a failure, but there is one -- wrap. 629 ; (end (save-excursion (end-of-line) (point))))
1239 ((> (length doctest-example-markers) 0) 630 ; (add-text-properties (point-min) (point-max) '(doctest-selected nil))
1240 (if (>= count 0) (doctest-first-failure) (doctest-last-failure))) 631 ; (add-text-properties beg end '(doctest-selected t))
1241 ;; We didn't find a failure -- alert the user. 632 ; (doctest-fontify-line (point)))
1242 (t (message "No failures found!"))))))) 633 )))
1243 634
1244 (defun doctest-prev-failure (count) 635 (defun doctest-prev-failure (count)
1245 "Move to the top of the previous failing example, and highlight 636 "Move to the top of the previous failing example, and highlight
1246 the example's failure description in *doctest-output*." 637 the example's failure description in *doctest-output*."
1247 (interactive "p") 638 (interactive "p")
1248 (doctest-next-failure (- count))) 639 (doctest-next-failure (- count)))
1249 640
1250 (defun doctest-first-failure () 641 (defun doctest-first-failure ()
1251 "Move to the top of the first failing example, and highlight 642 (interactive "")
1252 the example's failure description in *doctest-output*."
1253 (interactive)
1254 (if (buffer-live-p doctest-results-buffer) 643 (if (buffer-live-p doctest-results-buffer)
1255 (save-excursion 644 (save-excursion
1256 (set-buffer doctest-results-buffer) 645 (set-buffer doctest-results-buffer)
1257 (let ((old-selected-failure doctest-selected-failure)) 646 (let ((old-selected-failure doctest-selected-failure))
1258 (setq doctest-selected-failure (point-min)) 647 (setq doctest-selected-failure (point-min))
1259 (doctest-fontify-line old-selected-failure)))) 648 (doctest-fontify-line old-selected-failure))))
1260 (doctest-next-failure 1)) 649 (doctest-next-failure 1))
1261 650
1262 (defun doctest-last-failure () 651 (defun doctest-last-failure ()
1263 "Move to the top of the last failing example, and highlight 652 (interactive "")
1264 the example's failure description in *doctest-output*."
1265 (interactive)
1266 (if (buffer-live-p doctest-results-buffer) 653 (if (buffer-live-p doctest-results-buffer)
1267 (save-excursion 654 (save-excursion
1268 (set-buffer doctest-results-buffer) 655 (set-buffer doctest-results-buffer)
1269 (let ((old-selected-failure doctest-selected-failure)) 656 (let ((old-selected-failure doctest-selected-failure))
1270 (setq doctest-selected-failure (point-max)) 657 (setq doctest-selected-failure (point-max))
1271 (doctest-fontify-line old-selected-failure)))) 658 (doctest-fontify-line old-selected-failure))))
1272 (doctest-next-failure -1)) 659 (doctest-next-failure -1))
1273 660
1274 (defun doctest-select-failure () 661 (defconst doctest-example-source-re
1275 "Move to the top of the currently selected example, and select that 662 "^Failed example:\n\\(\n\\| [^\n]*\n\\)+")
1276 example in the source buffer. Intended for use in the results 663 (defun hide-example-source ()
1277 buffer." 664 "Delete the source code listings from the results buffer (since it's
1278 (interactive) 665 easy enough to see them in the original buffer)"
1279 (re-search-backward doctest-results-divider-re)
1280 (let ((old-selected-failure doctest-selected-failure))
1281 (setq doctest-selected-failure (point))
1282 (doctest-fontify-line doctest-selected-failure)
1283 (doctest-fontify-line old-selected-failure))
1284 (pop-to-buffer doctest-source-buffer)
1285 (doctest-next-failure 1))
1286
1287 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1288 ;;; Replace Output
1289 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1290
1291 (defun doctest-replace-output ()
1292 "Move to the top of the closest example, and replace its output
1293 with the 'got' output from the *doctest-output* buffer. An error is
1294 displayed if the chosen example is not listed in *doctest-output*, or
1295 if the 'expected' output for the example does not exactly match the
1296 output listed in the source buffer. The user is asked to confirm the
1297 replacement."
1298 (interactive)
1299 ;; Move to the beginning of the example.
1300 (cond
1301 ((and doctest-async (doctest-process-live-p doctest-async-process))
1302 (message "Wait for doctest to finish running!"))
1303 ((not (doctest-results-buffer-valid-p))
1304 (message "Run doctest first! (C-c C-c)"))
1305 ((save-excursion (set-buffer doctest-results-buffer)
1306 (equal doctest-results-py-version 'py21))
1307 (error "doctest-replace-output requires python 2.4+"))
1308 (t
1309 (save-excursion
1310 (save-restriction
1311 (when (doctest-in-mmm-docstring-overlay)
1312 (doctest-narrow-to-mmm-overlay))
1313
1314 (let* ((orig-buffer (current-buffer))
1315 ;; Find an example, and look up its original lineno.
1316 (lineno (doctest-prev-example-marker))
1317 ;; Find the example's indentation.
1318 (prompt-indent (doctest-line-indentation)))
1319
1320 ;; Switch to the output buffer, and look for the example.
1321 ;; If we don't find one, complain.
1322 (cond
1323 ((null lineno) (message "Doctest example not found"))
1324 (t
1325 (set-buffer doctest-results-buffer)
1326 (goto-char (point-min))
1327 (let ((output-re (format "^File .*, line %s," lineno)))
1328 (when (not (re-search-forward output-re nil t))
1329 (message "This doctest example did not fail")
1330 (setq lineno nil)))))
1331
1332 ;; If we didn't find an example, give up.
1333 (when (not (null lineno))
1334 ;; Get the output's 'expected' & 'got' texts.
1335 (let ((doctest-got nil) (doctest-expected nil) (header nil))
1336 (while (setq header (doctest-results-next-header))
1337 (cond
1338 ((equal header "Failed example:")
1339 t)
1340 ((equal header "Expected nothing")
1341 (setq doctest-expected ""))
1342 ((equal header "Expected:")
1343 (unless (re-search-forward "^\\(\\( \\).*\n\\)*" nil t)
1344 (error "Error parsing doctest output"))
1345 (setq doctest-expected (doctest-replace-regexp-in-string
1346 "^ " prompt-indent
1347 (match-string 0))))
1348 ((equal header "Got nothing")
1349 (setq doctest-got ""))
1350 ((or (equal header "Got:") (equal header "Exception raised:"))
1351 (unless (re-search-forward "^\\(\\( \\).*\n\\)*" nil t)
1352 (error "Error parsing doctest output"))
1353 (setq doctest-got (doctest-replace-regexp-in-string
1354 "^ " prompt-indent (match-string 0))))
1355 ((string-match "^Differences" header)
1356 (error (concat "doctest-replace-output can not be used "
1357 "with diff style output")))
1358 (t (error "Unexpected header %s" header))))
1359
1360 ;; Go back to the source buffer.
1361 (set-buffer orig-buffer)
1362
1363 ;; Skip ahead to the output.
1364 (beginning-of-line)
1365 (unless (re-search-forward "^ *>>>.*")
1366 (error "Error parsing doctest output"))
1367 (re-search-forward "\\(\n *\\.\\.\\..*\\)*\n?")
1368 (when (looking-at "\\'") (insert-char ?\n))
1369
1370 ;; Check that the output matches.
1371 (let ((start (point)) end)
1372 (cond ((re-search-forward "^ *\\(>>>.*\\|$\\)" nil t)
1373 (setq end (match-beginning 0)))
1374 (t
1375 (goto-char (point-max))
1376 (insert-string "\n")
1377 (setq end (point-max))))
1378 (when (and doctest-expected
1379 (not (equal (buffer-substring start end)
1380 doctest-expected)))
1381 (warn "{%s} {%s}" (buffer-substring start end)
1382 doctest-expected)
1383 (error (concat "This example's output has been modified "
1384 "since doctest was last run")))
1385 (setq doctest-expected (buffer-substring start end))
1386 (goto-char end))
1387
1388 ;; Trim exceptions
1389 (when (and doctest-trim-exceptions
1390 (string-match doctest-traceback-re
1391 doctest-got))
1392 (let ((s1 0) (e1 (match-end 1))
1393 (s2 (match-beginning 2)) (e2 (match-end 2))
1394 (s3 (match-beginning 3)) (e3 (length doctest-got)))
1395 (setq doctest-got
1396 (concat (substring doctest-got s1 e1)
1397 (substring doctest-got s2 e2) " . . .\n"
1398 (substring doctest-got s3 e3)))))
1399
1400 ;; Confirm it with the user.
1401 (let ((confirm-buffer (get-buffer-create "*doctest-confirm*")))
1402 (set-buffer confirm-buffer)
1403 ;; Erase anything left over in the buffer.
1404 (delete-region (point-min) (point-max))
1405 ;; Write a confirmation message
1406 (if (equal doctest-expected "")
1407 (insert-string "Replace nothing\n")
1408 (insert-string (concat "Replace:\n" doctest-expected)))
1409 (if (equal doctest-got "")
1410 (insert-string "With nothing\n")
1411 (insert-string (concat "With:\n" doctest-got)))
1412 (let ((confirm-window (display-buffer confirm-buffer)))
1413 ;; Shrink the confirm window.
1414 (shrink-window-if-larger-than-buffer confirm-window)
1415 ;; Return to the original buffer.
1416 (set-buffer orig-buffer)
1417 ;; Match the old expected region.
1418 (when doctest-expected
1419 (search-backward doctest-expected))
1420 (when (equal doctest-expected "") (backward-char 1))
1421 ;; Get confirmation & do the replacement
1422 (widen)
1423 (cond ((y-or-n-p "Ok to replace? ")
1424 (when (equal doctest-expected "") (forward-char 1))
1425 (replace-match doctest-got t t)
1426 (message "Replaced."))
1427 (t
1428 (message "Replace cancelled.")))
1429 ;; Clean up our confirm window
1430 (kill-buffer confirm-buffer)
1431 (delete-window confirm-window)))))))))))
1432
1433 (defun doctest-results-next-header ()
1434 "Move to the next header in the doctest results buffer, and return
1435 the string contents of that header. If no header is found, return
1436 nil."
1437 (if (re-search-forward (concat doctest-results-header-re "\\|"
1438 doctest-results-divider-re) nil t)
1439 (let ((result (match-string 0)))
1440 (if (string-match doctest-results-header-re result)
1441 result
1442 nil))
1443 nil))
1444
1445 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1446 ;;; mmm-mode support
1447 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1448 ;; MMM Mode is a minor mode for Emacs which allows Multiple Major
1449 ;; Modes to coexist in a single buffer.
1450
1451 ;;;###autoload
1452 (defun doctest-register-mmm-classes (&optional add-mode-ext-classes
1453 fix-mmm-fontify-region-bug)
1454 "Register doctest's mmm classes, allowing doctest to be used as a
1455 submode region in other major modes, such as python-mode and rst-mode.
1456 Two classes are registered:
1457
1458 `doctest-docstring'
1459
1460 Used to edit docstrings containing doctest examples in python-
1461 mode. Docstring submode regions start and end with triple-quoted
1462 strings (\"\"\"). In order to avoid confusing start-string
1463 markers and end-string markers, all triple-quote strings in the
1464 buffer are treated as submode regions (even if they're not
1465 actually docstrings). Use (C-c % C-d) to insert a new doctest-
1466 docstring region. When `doctest-execute' (C-c C-c) is called
1467 inside a doctest-docstring region, it executes just the current
1468 docstring. The globals for this execution are constructed by
1469 importing the current buffer's contents in Python.
1470
1471 `doctest-example'
1472
1473 Used to edit doctest examples in text-editing modes, such as
1474 `rst-mode' or `text-mode'. Docstring submode regions start with
1475 optionally indented prompts (>>>) and end with blank lines. Use
1476 (C-c % C-e) to insert a new doctest-example region. When
1477 `doctest-execute' (C-c C-c) is called inside a doctest-example
1478 region, it executes all examples in the buffer.
1479
1480 If ADD-MODE-EXT-CLASSES is true, then register the new classes in
1481 `mmm-mode-ext-classes-alist', which will cause them to be used by
1482 default in the following modes:
1483
1484 doctest-docstring: python-mode
1485 doctest-example: rst-mode
1486
1487 If FIX-MMM-FONTIFY-REGION-BUG is true, then register a hook that will
1488 fix a bug in `mmm-fontify-region' that affects some (but not all)
1489 versions of emacs. (See `doctest-fixed-mmm-fontify-region' for more
1490 info.)"
1491 (interactive)
1492 (require 'mmm-auto)
1493 (mmm-add-classes
1494 '(
1495 ;; === doctest-docstring ===
1496 (doctest-docstring :submode doctest-mode
1497
1498 ;; The front is any triple-quote. Include it in the submode region,
1499 ;; to prevent clashes between the two syntax tables over quotes.
1500 :front "\\(\"\"\"\\|'''\\)" :include-front t
1501
1502 ;; The back matches the front. Include just the first character
1503 ;; of the quote. If we didn't include at least one quote, then
1504 ;; the outer modes quote-counting would be thrown off. But if
1505 ;; we include all three, we run into a bug in mmm-mode. See
1506 ;; <http://tinyurl.com/2fa83w> for more info about the bug.
1507 :save-matches t :back "~1" :back-offset 1 :end-not-begin t
1508
1509 ;; Define a skeleton for entering new docstrings.
1510 :insert ((?d docstring nil @ "\"\"" @ "\"" \n
1511 _ \n "\"" @ "\"\"" @)))
1512
1513 ;; === doctest-example ===
1514 (doctest-example
1515 :submode doctest-mode
1516 ;; The front is an optionally indented prompt.
1517 :front "^[ \t]*>>>" :include-front t
1518 ;; The back is a blank line.
1519 :back "^[ \t]*$"
1520 ;; Define a skeleton for entering new docstrings.
1521 :insert ((?e doctest-example nil
1522 @ @ " >>> " _ "\n\n" @ @)))))
1523
1524 ;; Register some local variables that need to be saved.
1525 (add-to-list 'mmm-save-local-variables
1526 '(doctest-results-buffer buffer))
1527 (add-to-list 'mmm-save-local-variables
1528 '(doctest-example-markers buffer))
1529
1530 ;; Register association with modes, if requested.
1531 (when add-mode-ext-classes
1532 (mmm-add-mode-ext-class 'python-mode nil 'doctest-docstring)
1533 (mmm-add-mode-ext-class 'rst-mode nil 'doctest-example))
1534
1535 ;; Fix the buggy mmm-fontify-region, if requested.
1536 (when fix-mmm-fontify-region-bug
1537 (add-hook 'mmm-mode-hook 'doctest-fix-mmm-fontify-region-bug)))
1538
1539 (defvar doctest-old-mmm-fontify-region 'nil
1540 "Used to hold the original definition of `mmm-fontify-region' when it
1541 is rebound by `doctest-fix-mmm-fontify-region-bug'.")
1542
1543 (defun doctest-fix-mmm-fontify-region-bug ()
1544 "A function for `mmm-mode-hook' which fixes a potential bug in
1545 `mmm-fontify-region' by using `doctest-fixed-mmm-fontify-region'
1546 instead. (See `doctest-fixed-mmm-fontify-region' for more info.)"
1547 (setq font-lock-fontify-region-function
1548 'doctest-fixed-mmm-fontify-region))
1549
1550 (defun doctest-fixed-mmm-fontify-region (start stop &optional loudly)
1551 "A replacement for `mmm-fontify-region', which fixes a bug caused by
1552 versions of emacs where post-command-hooks are run *before*
1553 fontification. `mmm-mode' assumes that its post-command-hook will be
1554 run after fontification; and if it's not, then mmm-mode can end up
1555 with the wrong local variables, keymap, etc. after fontification. We
1556 fix that here by redefining `mmm-fontify-region' to remember what
1557 submode overlay it started in; and to return to that overlay after
1558 fontification is complete. The original definition of
1559 `mmm-fontify-region' is stored in `doctest-old-mmm-fontify-region'."
1560 (let ((overlay mmm-current-overlay))
1561 (mmm-fontify-region start stop loudly)
1562 (if (and overlay (or (< (point) (overlay-start overlay))
1563 (> (point) (overlay-end overlay))))
1564 (goto-char (overlay-start overlay)))
1565 (mmm-update-submode-region)))
1566
1567 (defun doctest-in-mmm-docstring-overlay ()
1568 (and (featurep 'mmm-auto)
1569 (mmm-overlay-at (point))
1570 (save-excursion
1571 (goto-char (overlay-start (mmm-overlay-at (point))))
1572 (looking-at "\"\"\"\\|\'\'\'"))))
1573
1574 (defun doctest-narrow-to-mmm-overlay ()
1575 "If we're in an mmm-mode overlay, then narrow to that overlay.
1576 This is useful, e.g., to keep from interpreting the close-quote of a
1577 docstring as part of the example's output."
1578 (let ((bounds (doctest-mmm-overlay-bounds)))
1579 (when bounds (narrow-to-region (car bounds) (cdr bounds)))))
1580
1581 (defun doctest-default-margin-in-mmm-docstring-overlay ()
1582 (save-excursion 666 (save-excursion
1583 (let ((pos (car (doctest-mmm-overlay-bounds)))) 667 (set-buffer doctest-results-buffer)
1584 (goto-char pos) 668 (toggle-read-only nil)
1585 (when (doctest-looking-back "\"\"\"\\|\'\'\'") 669 (beginning-of-buffer)
1586 (setq pos (- pos 3))) 670 (while (re-search-forward doctest-example-source-re nil t)
1587 (beginning-of-line) 671 (replace-match "" nil nil))
1588 (- pos (point))))) 672 (toggle-read-only t)))
1589 673
1590 (defun doctest-mmm-overlay-bounds () 674 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1591 (when (featurep 'mmm-auto) 675 ;; Doctest Results Mode (output of doctest-execute-buffer)
1592 (let ((overlay (mmm-overlay-at (point)))) 676 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1593 (when overlay 677 ;; [XX] Todo:
1594 (let ((start (overlay-start overlay)) 678 ;; - Make it read-only?
1595 (end (overlay-end overlay))) 679 ;; - Hitting enter goes to the corresponding error
1596 (when (doctest-in-mmm-docstring-overlay) 680 ;; - Clicking goes to corresponding error (not as useful)
1597 (save-excursion 681
1598 (goto-char start) 682
1599 (re-search-forward "[\"\']*") 683 (defconst doctest-results-divider-re
1600 (setq start (point)) 684 "^\\([*]\\{60,\\}\\)$")
1601 (goto-char end) 685
1602 (while (doctest-looking-back "[\"\']") 686 (defconst doctest-results-loc-re
1603 (backward-char 1)) 687 "^File \"\\([^\"]+\\)\", line \\([0-9]+\\), in \\([^\n]+\\)")
1604 (setq end (point)))) 688
1605 (cons start end)))))) 689 (defconst doctest-results-header-re
1606 690 "^\\([a-zA-Z0-9 ]+:\\)$")
1607 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 691
1608 ;;; Helper functions 692 (defconst doctest-results-font-lock-keywords
1609 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 693 `((,doctest-results-divider-re
1610 694 (0 'doctest-results-divider-face))
1611 (defun doctest-on-source-line-p (&optional prompt) 695 (,doctest-results-loc-re
1612 "Return true if the current line is a source line. The optional 696 (0 'doctest-results-loc-face))
1613 argument prompt can be used to specify which type of source 697 (,doctest-results-header-re
1614 line (... or >>>)." 698 (0 'doctest-results-header-face))
699 (doctest-results-selection-matcher
700 (0 'doctest-results-selection-face t))))
701
702 (defun doctest-results-selection-matcher (limit)
703 "Matches from `doctest-selected-failure' to the end of the
704 line. This is used to highlight the currently selected failure."
705 (when (and doctest-selected-failure
706 (<= (point) doctest-selected-failure)
707 (< doctest-selected-failure limit))
708 (goto-char doctest-selected-failure)
709 (search-forward-regexp "[^\n]+" limit)))
710
711 ;; Register the font-lock keywords (xemacs)
712 (put 'doctest-results-mode 'font-lock-defaults
713 '(doctest-results-font-lock-keywords))
714
715 ;; Register the font-lock keywords (gnu emacs)
716 (defvar font-lock-defaults-alist nil) ; in case we're in xemacs
717 (setq font-lock-defaults-alist
718 (append font-lock-defaults-alist
719 `((doctest-results-mode
720 doctest-results-font-lock-keywords
721 nil nil nil nil))))
722
723 ;; Define the mode
724 (define-derived-mode doctest-results-mode text-mode "Doctest Results"
725 "docstring"
726 ;; Enable font-lock mode.
727 (if (featurep 'font-lock) (font-lock-mode 1))
728 ;; Keep track of which failure is selected
729 (set (make-local-variable 'doctest-selected-failure) nil)
730 ;; Make the buffer read-only.
731 (toggle-read-only t))
732
733 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
734 ;; Helper functions
735 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
736
737 (defun on-doctest-source-line ()
738 "Return true if the current line is a source line."
1615 (save-excursion 739 (save-excursion
1616 (beginning-of-line) 740 (beginning-of-line)
1617 ;; Check if we're looking at a prompt (of the right type). 741 (looking-at doctest-prompt-re)))
1618 (when (and (looking-at doctest-prompt-re) 742
1619 (or (null prompt) 743 (defun on-empty-doctest-source-line ()
1620 (equal prompt (substring (match-string 2) 0 3))))
1621 ;; Scan backwards to make sure there's a >>> somewhere. Otherwise,
1622 ;; this might be a '...' in the text or in an example's output.
1623 (while (looking-at "^[ \t]*[.][.][.]")
1624 (forward-line -1))
1625 (looking-at "^[ \t]*>>>"))))
1626
1627 (defun doctest-on-empty-source-line-p ()
1628 "Return true if the current line contains a bare prompt." 744 "Return true if the current line contains a bare prompt."
1629 (save-excursion 745 (save-excursion
1630 (beginning-of-line) 746 (beginning-of-line)
1631 (and (doctest-on-source-line-p) 747 (looking-at (concat doctest-prompt-re "$"))))
1632 (looking-at (concat doctest-prompt-re "$"))))) 748
1633 749 (defun on-doctest-output-line ()
1634 (defun doctest-on-output-line-p ()
1635 "Return true if the current line is an output line." 750 "Return true if the current line is an output line."
1636 (save-excursion 751 (save-excursion
1637 (beginning-of-line) 752 (beginning-of-line)
1638 ;; The line must not be blank or a source line. 753 (let ((prompt-or-blankline (concat doctest-prompt-re "\\|" "^[ \t]*\n")))
1639 (when (not (or (doctest-on-source-line-p) (looking-at "[ \t]*$"))) 754 ;; The line must not be blank or start with a prompt.
1640 ;; The line must follow a source line, with no intervening blank 755 (when (not (looking-at prompt-or-blankline))
1641 ;; lines. 756 ;; The line must follow a line starting with a prompt, with
1642 (while (not (or (doctest-on-source-line-p) (looking-at "[ \t]*$") 757 ;; no intervening blank lines.
1643 (= (point) (point-min)))) 758 (search-backward-regexp prompt-or-blankline nil t)
1644 (forward-line -1)) 759 (looking-at doctest-prompt-re)))))
1645 (doctest-on-source-line-p)))) 760
1646 761 (defun find-doctest-output-line (&optional limit)
1647 (defun doctest-find-output-line (&optional limit)
1648 "Move forward to the next doctest output line (staying within 762 "Move forward to the next doctest output line (staying within
1649 the given bounds). Return the character position of the doctest 763 the given bounds). Return the character position of the doctest
1650 output line if one was found, and false otherwise." 764 output line if one was found, and false otherwise."
1651 (let ((found-it nil) ; point where we found an output line 765 (let ((found-it nil) ; point where we found an output line
1652 (limit (or limit (point-max)))) ; default value for limit 766 (limit (or limit (point-max)))) ; default value for limit
1653 (save-excursion 767 (save-excursion
1654 ;; Keep moving forward, one line at a time, until we find a 768 ;; Keep moving forward, one line at a time, until we find a
1655 ;; doctest output line. 769 ;; doctest output line.
1656 (while (and (not found-it) (< (point) limit) (not (eobp))) 770 (while (and (not found-it) (< (point) limit) (not (eobp)))
1657 (if (and (not (eolp)) (doctest-on-output-line-p)) 771 (if (and (not (eolp)) (on-doctest-output-line))
1658 (setq found-it (point)) 772 (setq found-it (point))
1659 (forward-line)))) 773 (forward-line))))
1660 ;; If we found a doctest output line, then go to it. 774 ;; If we found a doctest output line, then go to it.
1661 (if found-it (goto-char found-it)))) 775 (if found-it (goto-char found-it))))
1662 776
1663 (defun doctest-line-indentation ()
1664 "Helper for doctest-replace-output: return the whitespace indentation
1665 at the beginning of this line."
1666 (save-excursion
1667 (end-of-line)
1668 (re-search-backward "^\\( *\\)" nil t)
1669 (match-string 1)))
1670
1671 (defun doctest-optionflags (&optional diff)
1672 "Return a string describing the optionflags that should be used
1673 by doctest. If DIFF is non-nil, then add the REPORT_UDIFF flag."
1674 (let ((flags "0"))
1675 (dolist (flag doctest-optionflags)
1676 (setq flags (concat flags "|" flag)))
1677 (if diff (concat flags "|" "REPORT_UDIFF") flags)))
1678
1679 (defun doctest-results-loc-re ()
1680 "Return the regexp that should be used to look for doctest example
1681 location markers in doctest's output (based on which version of
1682 doctest was used"
1683 (cond
1684 ((equal doctest-results-py-version 'py21)
1685 doctest-py21-results-loc-re)
1686 ((equal doctest-results-py-version 'py24)
1687 doctest-py24-results-loc-re)
1688 (t (error "bad value for doctest-results-py-version"))))
1689
1690 (defun doctest-results-buffer-name ()
1691 "Return the buffer name that should be used for the doctest results
1692 buffer. This is computed from the variable
1693 `doctest-results-buffer-name'."
1694 (doctest-replace-regexp-in-string
1695 "%[nfN]"
1696 (lambda (sym)
1697 (cond ((equal sym "%n") (buffer-name))
1698 ((equal sym "%N") (doctest-replace-regexp-in-string
1699 "[.]doctest$" "" (buffer-name) t))
1700 ((equal sym "%f") (buffer-file-name))))
1701 doctest-results-buffer-name t))
1702
1703 (defun doctest-script (input-file globs-file diff)
1704 "..."
1705 (doctest-replace-regexp-in-string
1706 "%[tnflm]"
1707 (lambda (sym)
1708 (cond ((equal sym "%n") (buffer-name))
1709 ((equal sym "%f") (buffer-file-name))
1710 ((equal sym "%l") (doctest-optionflags diff))
1711 ((equal sym "%t") input-file)
1712 ((equal sym "%m") (or globs-file ""))))
1713 doctest-script t))
1714
1715 (defun doctest-hide-example-source ()
1716 "Delete the source code listings from the results buffer (since it's
1717 easy enough to see them in the original buffer)"
1718 (save-excursion
1719 (set-buffer doctest-results-buffer)
1720 (toggle-read-only 0)
1721 (goto-char (point-min))
1722 (while (re-search-forward doctest-example-source-re nil t)
1723 (replace-match "" nil nil))
1724 (toggle-read-only t)))
1725
1726 (defun doctest-results-buffer-valid-p ()
1727 "Return true if this buffer has a live results buffer; and that
1728 results buffer reports this buffer as its source buffer. (Two
1729 buffers in doctest-mode might point to the same results buffer;
1730 but only one of them will be equal to that results buffer's
1731 source buffer."
1732 (let ((cur-buf (current-buffer)))
1733 (and (buffer-live-p doctest-results-buffer)
1734 (save-excursion
1735 (set-buffer doctest-results-buffer)
1736 (equal cur-buf doctest-source-buffer)))))
1737
1738 (defun doctest-update-mode-line (value)
1739 "Update the doctest mode line with the given string value. This
1740 is used to display information about asynchronous processes that
1741 are run by doctest-mode."
1742 (setq doctest-mode-line-process
1743 value)
1744 (force-mode-line-update t))
1745
1746 (defun doctest-version () 777 (defun doctest-version ()
1747 "Echo the current version of `doctest-mode' in the minibuffer." 778 "Echo the current version of `doctest-mode' in the minibuffer."
1748 (interactive) 779 (interactive)
1749 (message "Using `doctest-mode' version %s" doctest-version)) 780 (message "Using `doctest-mode' version %s" doctest-version))
1750 781
1751 (defun doctest-warn (msg &rest args) 782 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1752 "Display a doctest warning message." 783 ;; Utility functions
1753 (if (fboundp 'display-warning) 784 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1754 (display-warning 'doctest (apply 'format msg args))
1755 (apply 'message msg args)))
1756
1757 (defun doctest-debug (msg &rest args)
1758 "Display a doctest debug message."
1759 (if (fboundp 'display-warning)
1760 (display-warning 'doctest (apply 'format msg args) 'debug)
1761 (apply 'message msg args)))
1762 785
1763 (defvar doctest-serial-number 0) ;used if broken-temp-names. 786 (defvar doctest-serial-number 0) ;used if broken-temp-names.
1764 (defun doctest-temp-name () 787 (defun doctest-temp-name ()
1765 "Return a new temporary filename, for use in calling doctest."
1766 (if (memq 'broken-temp-names features) 788 (if (memq 'broken-temp-names features)
1767 (let 789 (let
1768 ((sn doctest-serial-number) 790 ((sn doctest-serial-number)
1769 (pid (and (fboundp 'emacs-pid) (emacs-pid)))) 791 (pid (and (fboundp 'emacs-pid) (emacs-pid))))
1770 (setq doctest-serial-number (1+ doctest-serial-number)) 792 (setq doctest-serial-number (1+ doctest-serial-number))
1771 (if pid 793 (if pid
1772 (format "doctest-%d-%d" sn pid) 794 (format "doctest-%d-%d" sn pid)
1773 (format "doctest-%d" sn))) 795 (format "doctest-%d" sn)))
1774 (make-temp-name "doctest-"))) 796 (make-temp-name "doctest-")))
1775 797
798 (defun column-at-char (pos)
799 "Return the column of the given character position"
800 (save-excursion (goto-char pos) (current-column)))
801
802 (defun doctest-looking-back (regexp)
803 "Return True if the text before point matches the given regular
804 expression. Like looking-at except backwards and slower. (This
805 is available as `looking-back' in GNU emacs and
806 `looking-at-backwards' in XEmacs, but it's easy enough to define
807 from scratch such that it works under both.)"
808 (save-excursion
809 (let ((orig-pos (point)))
810 ;; Search backwards for the regexp.
811 (if (re-search-backward regexp nil t)
812 ;; Check if it ends at the original point.
813 (= orig-pos (match-end 0))))))
814
1776 (defun doctest-fontify-line (charpos) 815 (defun doctest-fontify-line (charpos)
1777 "Run font-lock-fontify-region on the line containing the given 816 "Run font-lock-fontify-region on the line containing the given
1778 position." 817 position."
1779 (if (and charpos (functionp 'font-lock-fontify-region)) 818 (if charpos
1780 (save-excursion 819 (save-excursion
1781 (goto-char charpos) 820 (goto-char charpos)
1782 (let ((beg (progn (beginning-of-line) (point))) 821 (let ((beg (progn (beginning-of-line) (point)))
1783 (end (progn (end-of-line) (point)))) 822 (end (progn (end-of-line) (point))))
1784 (font-lock-fontify-region beg end))))) 823 (font-lock-fontify-region beg end)))))
1785
1786 (defun doctest-do-auto-fill ()
1787 "If the current line is a soucre line or an output line, do nothing.
1788 Otherwise, call (do-auto-fill)."
1789 (cond
1790 ;; Don't wrap source lines.
1791 ((doctest-on-source-line-p) nil)
1792 ;; Don't wrap output lines
1793 ((doctest-on-output-line-p) nil)
1794 ;; Wrap all other lines
1795 (t (do-auto-fill))))
1796
1797 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1798 ;;; Emacs Compatibility Functions
1799 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1800 ;; Define compatible versions of functions that are defined
1801 ;; for some versions of emacs but not others.
1802
1803 ;; Backwards compatibility: looking-back
1804 (cond ((fboundp 'looking-back) ;; Emacs 22.x
1805 (defalias 'doctest-looking-back 'looking-back))
1806 (t
1807 (defun doctest-looking-back (regexp)
1808 "Return true if text before point matches REGEXP."
1809 (save-excursion
1810 (let ((orig-pos (point)))
1811 ;; Search backwards for the regexp.
1812 (if (re-search-backward regexp nil t)
1813 ;; Check if it ends at the original point.
1814 (= orig-pos (match-end 0))))))))
1815
1816 ;; Backwards compatibility: replace-regexp-in-string
1817 (cond ((fboundp 'replace-regexp-in-string)
1818 (defalias 'doctest-replace-regexp-in-string 'replace-regexp-in-string))
1819 (t ;; XEmacs 21.x or Emacs 20.x
1820 (defun doctest-replace-regexp-in-string
1821 (regexp rep string &optional fixedcase literal)
1822 "Replace all matches for REGEXP with REP in STRING."
1823 (let ((start 0))
1824 (while (and (< start (length string))
1825 (string-match regexp string start))
1826 (setq start (+ (match-end 0) 1))
1827 (let ((newtext (if (functionp rep)
1828 (save-match-data
1829 (funcall rep (match-string 0 string)))
1830 rep)))
1831 (setq string (replace-match newtext fixedcase
1832 literal string)))))
1833 string)))
1834
1835 ;; Backwards compatibility: line-number
1836 (cond ((fboundp 'line-number) ;; XEmacs
1837 (defalias 'doctest-line-number 'line-number))
1838 ((fboundp 'line-number-at-pos) ;; Emacs 22.x
1839 (defalias 'doctest-line-number 'line-number-at-pos))
1840 (t ;; Emacs 21.x
1841 (defun doctest-line-number (&optional pos)
1842 "Return the line number of POS (default=point)."
1843 (1+ (count-lines 1
1844 (save-excursion (progn (beginning-of-line)
1845 (or pos (point)))))))))
1846
1847 ;; Backwards compatibility: process-live-p
1848 (cond ((fboundp 'process-live-p) ;; XEmacs
1849 (defalias 'doctest-process-live-p 'process-live-p))
1850 (t ;; Emacs
1851 (defun doctest-process-live-p (process)
1852 (and (processp process)
1853 (equal (process-status process) 'run)))))
1854
1855 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1856 ;;; Doctest Results Mode (output of doctest-execute-buffer)
1857 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1858
1859 ;; Register the font-lock keywords (xemacs)
1860 (put 'doctest-results-mode 'font-lock-defaults
1861 '(doctest-results-font-lock-keywords))
1862
1863 ;; Register the font-lock keywords (older versions of gnu emacs)
1864 (when (boundp 'font-lock-defaults-alist)
1865 (add-to-list 'font-lock-defaults-alist
1866 '(doctest-results-mode doctest-results-font-lock-keywords
1867 nil nil nil nil)))
1868
1869 (defvar doctest-selected-failure nil
1870 "The location of the currently selected failure.
1871 This variable is uffer-local to doctest-results-mode buffers.")
1872
1873 (defvar doctest-source-buffer nil
1874 "The buffer that spawned this one.
1875 This variable is uffer-local to doctest-results-mode buffers.")
1876
1877 (defvar doctest-results-py-version nil
1878 "A symbol indicating which version of Python was used to generate
1879 the results in a doctest-results-mode buffer. Can be either the
1880 symbol `py21' or the symbol `py24'.
1881 This variable is uffer-local to doctest-results-mode buffers.")
1882
1883 ;; Keymap for doctest-results-mode.
1884 (defconst doctest-results-mode-map
1885 (let ((map (make-keymap)))
1886 (define-key map [return] 'doctest-select-failure)
1887 map)
1888 "Keymap for doctest-results-mode.")
1889
1890 ;; Syntax table for doctest-results-mode.
1891 (defvar doctest-results-mode-syntax-table nil
1892 "Syntax table used in `doctest-results-mode' buffers.")
1893 (when (not doctest-results-mode-syntax-table)
1894 (setq doctest-results-mode-syntax-table (make-syntax-table))
1895 (dolist (entry '(("(" . "()") ("[" . "(]") ("{" . "(}")
1896 (")" . ")(") ("]" . ")[") ("}" . "){")
1897 ("$%&*+-/<=>|'\"`" . ".") ("_" . "w")))
1898 (dolist (char (string-to-list (car entry)))
1899 (modify-syntax-entry char (cdr entry)
1900 doctest-results-mode-syntax-table))))
1901
1902 ;; Define the mode
1903 (defun doctest-results-mode ()
1904 "A major mode used to display the results of running doctest.
1905 See `doctest-mode'.
1906
1907 \\{doctest-results-mode-map}"
1908 (interactive)
1909 824
1910 ;; Declare local variables. 825 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1911 (kill-all-local-variables) 826 ;; Syntax Table
1912 (make-local-variable 'font-lock-defaults) 827 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1913 (make-local-variable 'doctest-selected-failure) 828
1914 (make-local-variable 'doctest-source-buffer) 829 ;; We do *NOT* currently use this, because it applies too
1915 (make-local-variable 'doctest-results-py-version) 830 ;; indiscrimanantly. In particular, we don't want "'" and '"' treated
1916 831 ;; as quote marks on text lines. But there's no good way to prevent
1917 ;; Define local variables. 832 ;; it.
1918 (setq major-mode 'doctest-results-mode 833 (defvar doctest-syntax-alist nil
1919 mode-name "Doctest-Results" 834 "Syntax alist used in `doctest-mode' buffers.")
1920 mode-line-process 'doctest-mode-line-process 835 (setq doctest-syntax-alist '((?\( . "()") (?\[ . "(]") (?\{ . "(}")
1921 font-lock-defaults '(doctest-results-font-lock-keywords 836 (?\) . ")(") (?\] . ")[") (?\} . "){")
1922 nil nil nil nil)) 837 (?\$ . "." ) (?\% . "." ) (?\& . "." )
1923 ;; Define keymap. 838 (?\* . "." ) (?\+ . "." ) (?\- . "." )
1924 (use-local-map doctest-results-mode-map) 839 (?\/ . "." ) (?\< . "." ) (?\= . "." )
1925 840 (?\> . "." ) (?\| . "." ) (?\_ . "w" )
1926 ;; Define the syntax table. 841 (?\' . "\"") (?\" . "\"") (?\` . "$" )
1927 (set-syntax-table doctest-results-mode-syntax-table) 842 (?\# . "<" ) (?\n . ">" )))
1928 843
1929 ;; Enable font-lock mode. 844 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1930 (if (featurep 'font-lock) (font-lock-mode 1))) 845 ;; Key Bindings
1931 846 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1932 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 847
1933 ;;; Doctest Mode
1934 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1935
1936 ;; Register the font-lock keywords (xemacs)
1937 (put 'doctest-mode 'font-lock-defaults '(doctest-font-lock-keywords
1938 nil nil nil nil))
1939
1940 ;; Register the font-lock keywords (older versions of gnu emacs)
1941 (when (boundp 'font-lock-defaults-alist)
1942 (add-to-list 'font-lock-defaults-alist
1943 '(doctest-mode doctest-font-lock-keywords
1944 nil nil nil nil)))
1945
1946 (defvar doctest-results-buffer nil
1947 "The output buffer for doctest-mode.
1948 This variable is buffer-local to doctest-mode buffers.")
1949
1950 (defvar doctest-example-markers nil
1951 "A list mapping markers to the line numbers at which they appeared
1952 in the buffer at the time doctest was last run. This is used to find
1953 'original' line numbers, which can be used to search the doctest
1954 output buffer. It's encoded as a list of (MARKER . POS) tuples, in
1955 reverse POS order.
1956 This variable is buffer-local to doctest-mode buffers.")
1957
1958 ;; These are global, since we only one run process at a time:
1959 (defvar doctest-async-process nil
1960 "The process object created by the asynchronous doctest process")
1961 (defvar doctest-async-process-tempfiles nil
1962 "A list of tempfile names created by the asynchronous doctest process")
1963 (defvar doctest-async-process-buffer nil
1964 "The source buffer for the asynchronous doctest process")
1965 (defvar doctest-mode-line-process ""
1966 "A string displayed on the modeline, to indicate when doctest is
1967 running asynchronously.")
1968
1969 ;; Keymap for doctest-mode. n.b.: we intentionally define [tab]
1970 ;; rather than overriding indent-line-function, since we don't want
1971 ;; doctest-indent-source-line to be called by do-auto-fill.
1972 (defconst doctest-mode-map 848 (defconst doctest-mode-map
1973 (let ((map (make-keymap))) 849 (let ((map (make-keymap)))
1974 (define-key map [backspace] 'doctest-electric-backspace) 850 (define-key map [backspace] 'doctest-electric-backspace)
1975 (define-key map [return] 'doctest-newline-and-indent) 851 (define-key map [return] 'doctest-newline-and-indent)
1976 (define-key map [tab] 'doctest-indent-source-line) 852 (define-key map [tab] 'doctest-indent-source-line)
1977 (define-key map ":" 'doctest-electric-colon) 853 (define-key map ":" 'doctest-electric-colon)
1978 (define-key map "\C-c\C-v" 'doctest-version) 854 (define-key map "\C-c\C-v" 'doctest-version)
1979 (define-key map "\C-c\C-c" 'doctest-execute) 855 (define-key map "\C-c\C-c" 'doctest-execute-buffer)
1980 (define-key map "\C-c\C-d" 'doctest-execute-with-diff)
1981 (define-key map "\C-c\C-n" 'doctest-next-failure) 856 (define-key map "\C-c\C-n" 'doctest-next-failure)
1982 (define-key map "\C-c\C-p" 'doctest-prev-failure) 857 (define-key map "\C-c\C-p" 'doctest-prev-failure)
1983 (define-key map "\C-c\C-a" 'doctest-first-failure) 858 (define-key map "\C-c\C-a" 'doctest-first-failure)
1984 (define-key map "\C-c\C-e" 'doctest-last-failure)
1985 (define-key map "\C-c\C-z" 'doctest-last-failure) 859 (define-key map "\C-c\C-z" 'doctest-last-failure)
1986 (define-key map "\C-c\C-r" 'doctest-replace-output)
1987 (define-key map "\C-c|" 'doctest-execute-region)
1988 map) 860 map)
1989 "Keymap for doctest-mode.") 861 "Keymap for doctest-mode.")
1990 862
1991 ;; Syntax table for doctest-mode. 863 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1992 (defvar doctest-mode-syntax-table nil 864 ;; Define the mode
1993 "Syntax table used in `doctest-mode' buffers.") 865 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1994 (when (not doctest-mode-syntax-table) 866
1995 (setq doctest-mode-syntax-table (make-syntax-table)) 867 ;; Register the font-lock keywords (xemacs)
1996 (dolist (entry '(("(" . "()") ("[" . "(]") ("{" . "(}") 868 (put 'doctest-mode 'font-lock-defaults '(doctest-font-lock-keywords))
1997 (")" . ")(") ("]" . ")[") ("}" . "){") 869
1998 ("$%&*+-/<=>|'\"`" . ".") ("_" . "w"))) 870 ;; Register the font-lock keywords (gnu emacs)
1999 (dolist (char (string-to-list (car entry))) 871 (defvar font-lock-defaults-alist nil) ; in case we're in xemacs
2000 (modify-syntax-entry char (cdr entry) doctest-mode-syntax-table)))) 872 (setq font-lock-defaults-alist
873 (append font-lock-defaults-alist
874 `((doctest-mode doctest-font-lock-keywords nil nil nil nil))))
2001 875
2002 ;; Use doctest mode for files ending in .doctest 876 ;; Use doctest mode for files ending in .doctest
2003 ;;;###autoload 877 ;;;###autoload
2004 (add-to-list 'auto-mode-alist '("\\.doctest$" . doctest-mode)) 878 (add-to-list 'auto-mode-alist '("\\.doctest$" . doctest-mode))
2005 879
2006 ;;;###autoload 880 ;;;###autoload
2007 (defun doctest-mode () 881 (define-derived-mode doctest-mode text-mode "Doctest"
2008 "A major mode for editing text files that contain Python 882 "A major mode for editing text files that contain Python
2009 doctest examples. Doctest is a testing framework for Python that 883 doctest examples. Doctest is a testing framework for Python that
2010 emulates an interactive session, and checks the result of each 884 emulates an interactive session, and checks the result of each
2011 command. For more information, see the Python library reference: 885 command. For more information, see the Python library reference:
2012 <http://docs.python.org/lib/module-doctest.html> 886 <http://docs.python.org/lib/module-doctest.html>
2023 specific output faces. 897 specific output faces.
2024 898
2025 - 'Text lines' are any other lines. They are not processed in 899 - 'Text lines' are any other lines. They are not processed in
2026 any special way. 900 any special way.
2027 901
2028 \\{doctest-mode-map}" 902 \\{doctest-mode-map}
2029 (interactive) 903 "
2030
2031 ;; Declare local variables.
2032 (kill-all-local-variables)
2033 (make-local-variable 'font-lock-defaults)
2034 (make-local-variable 'doctest-results-buffer)
2035 (make-local-variable 'doctest-example-markers)
2036
2037 ;; Define local variables.
2038 (setq major-mode 'doctest-mode
2039 mode-name "Doctest"
2040 mode-line-process 'doctest-mode-line-process
2041 font-lock-defaults '(doctest-font-lock-keywords
2042 nil nil nil nil))
2043
2044 ;; Define keymap.
2045 (use-local-map doctest-mode-map)
2046
2047 ;; Define the syntax table.
2048 (set-syntax-table doctest-mode-syntax-table)
2049
2050 ;; Enable auto-fill mode. 904 ;; Enable auto-fill mode.
2051 (auto-fill-mode 1) 905 (auto-fill-mode 1)
2052 (setq auto-fill-function 'doctest-do-auto-fill)
2053 906
2054 ;; Enable font-lock mode. 907 ;; Enable font-lock mode.
2055 (if (featurep 'font-lock) (font-lock-mode 1)) 908 (if (featurep 'font-lock) (font-lock-mode 1))
2056 909
2057 ;; Run the mode hook. 910 ;; Register our indentation function.
2058 (run-hooks 'doctest-mode-hook)) 911 (set (make-local-variable 'indent-line-function)
912 'doctest-indent-source-line)
913
914 ;; Keep track of our results buffer.
915 (set (make-local-variable 'doctest-results-buffer) nil)
916 )
2059 917
2060 (provide 'doctest-mode) 918 (provide 'doctest-mode)
2061 ;;; doctest-mode.el ends here 919 ;;; doctest-mode.el ends here