comparison .elisp/doctest-mode.el @ 174:014e745b2d04

python-mode: updating to current bzr version This is bzr revision 351, which is revision-id: barry@python.org-20090320013721-awwzwv1mfl7hicdf
author Augie Fackler <durin42@gmail.com>
date Wed, 16 Dec 2009 22:57:36 -0600
parents b5d75594b356
children
comparison
equal deleted inserted replaced
173:fd92c15701ae 174:014e745b2d04
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 Edward Loper 3 ;; Copyright (C) 2004-2007 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.2" 10 (defconst doctest-version "0.5 alpha"
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 ;; Known bugs: 25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26 ;; - Some places assume prompts are 4 chars (but they can be 3 26 ;;; Table of Contents
27 ;; if they're bare). 27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
28 ;; - String literals are not colored correctly. (We need to color 28 ;; 1. Customization: use-editable variables to customize
29 ;; string literals on source lines, but *not* output lines or 29 ;; doctest-mode.
30 ;; text lines; this is hard to do.) 30 ;;
31 ;; - Output lines starting with "..." are mistakenly interpreted 31 ;; 2. Fonts: defines new font-lock faces.
32 ;; as (continuation) source lines. 32 ;;
33 33 ;; 3. Constants: various consts (mainly regexps) used by the rest
34 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 34 ;; of the code.
35 ;; Customizable Constants 35 ;;
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
36 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 71 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
37 72
38 (defgroup doctest nil 73 (defgroup doctest nil
39 "Support for the Python doctest framework" 74 "Support for the Python doctest framework"
40 :group 'languages 75 :group 'languages
61 (funcall ok "/usr/tmp") 96 (funcall ok "/usr/tmp")
62 (funcall ok "/tmp") 97 (funcall ok "/tmp")
63 (funcall ok "/var/tmp") 98 (funcall ok "/var/tmp")
64 (funcall ok ".") 99 (funcall ok ".")
65 (error (concat "Couldn't find a usable temp directory -- " 100 (error (concat "Couldn't find a usable temp directory -- "
66 "set `doctest-temp-directory'")))) 101 "set `doctest-temp-directory'"))))
67 102 "Directory used for temporary files created when running doctest.
68 "*Directory used for temporary files created when running doctest.
69 By default, the first directory from this list that exists and that you 103 By default, the first directory from this list that exists and that you
70 can write into: the value (if any) of the environment variable TMPDIR, 104 can write into: the value (if any) of the environment variable TMPDIR,
71 /usr/tmp, /tmp, /var/tmp, or the current directory." 105 /usr/tmp, /tmp, /var/tmp, or the current directory."
72 :type 'string 106 :type 'string
73 :group 'doctest) 107 :group 'doctest)
74 108
75 (defcustom hide-example-source t 109 (defcustom doctest-hide-example-source nil
76 "If true, then don't display the example source code for each 110 "If true, then don't display the example source code for each
77 failure in the results buffer." 111 failure in the results buffer."
78 :type 'boolean 112 :type 'boolean
79 :group 'doctest) 113 :group 'doctest)
80 114
81 (defcustom doctest-python-command "python" 115 (defcustom doctest-python-command "python"
82 "Shell command used to start the python interpreter") 116 "Shell command used to start the python interpreter"
83 117 :type 'string
84 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 118 :group 'doctest)
85 ;; Fonts 119
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
86 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 177 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
87 178
88 (defface doctest-prompt-face 179 (defface doctest-prompt-face
89 '((((class color) (background dark)) 180 '((((class color) (background dark))
90 (:foreground "#68f")) 181 (:foreground "#68f"))
147 (t (:foreground "#f00"))) 238 (t (:foreground "#f00")))
148 "Face for selected example's prompt" 239 "Face for selected example's prompt"
149 :group 'doctest) 240 :group 'doctest)
150 241
151 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 242 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
152 ;; Constants 243 ;;; Constants
153 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 244 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
154 245
155 (defconst doctest-prompt-re 246 (defconst doctest-prompt-re
156 "^\\([ \t]*\\)\\(>>> ?\\|[.][.][.] ?\\)\\([ \t]*\\)" 247 "^\\(?:\\([ \t]*\\)\\(>>> ?\\|[.][.][.] ?\\)\\([ \t]*\\)\\)"
157 "Regular expression for doctest prompts. It defines three groups: 248 "Regular expression for doctest prompts. It defines three groups:
158 the pre-prompt margin; the prompt; and the post-prompt indentation.") 249 the pre-prompt margin; the prompt; and the post-prompt indentation.")
159 250
160 (defconst doctest-open-block-re 251 (defconst doctest-open-block-re
161 "[^\n]+:[ \t]*\\(#.*\\)?$" 252 "[^\n]+:[ \t]*\\(#.*\\)?$"
162 "Regular expression for a line that opens a block") 253 "Regular expression for a line that opens a block")
163 254
164 (defconst doctest-close-block-re 255 (defconst doctest-close-block-re
165 "\\(return\\|raise\\|break\\|continue\\|pass\\)\\b" 256 "\\(return\\|raise\\|break\\|continue\\|pass\\)\\b"
166 "Regular expression for a line that closes a block") 257 "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.")
167 314
168 (defconst doctest-outdent-re 315 (defconst doctest-outdent-re
169 (concat "\\(" (mapconcat 'identity 316 (concat "\\(" (mapconcat 'identity
170 '("else:" 317 '("else:"
171 "except\\(\\s +.*\\)?:" 318 "except\\(\\s +.*\\)?:"
175 "\\)") 322 "\\)")
176 "Regular expression for a line that should be outdented. Any line 323 "Regular expression for a line that should be outdented. Any line
177 that matches `doctest-outdent-re', but does not follow a line matching 324 that matches `doctest-outdent-re', but does not follow a line matching
178 `doctest-no-outdent-re', will be outdented.") 325 `doctest-no-outdent-re', will be outdented.")
179 326
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.
180 (defconst doctest-no-outdent-re 330 (defconst doctest-no-outdent-re
181 (concat 331 (concat
182 "\\(" 332 "\\("
183 (mapconcat 'identity 333 (mapconcat 'identity
184 (list "try:" 334 (list "try:"
193 "\\)") 343 "\\)")
194 "Regular expression matching lines not to outdent after. Any line 344 "Regular expression matching lines not to outdent after. Any line
195 that matches `doctest-outdent-re', but does not follow a line matching 345 that matches `doctest-outdent-re', but does not follow a line matching
196 `doctest-no-outdent-re', will be outdented.") 346 `doctest-no-outdent-re', will be outdented.")
197 347
198 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 348 (defconst doctest-script
199 ;; Colorization support (font-lock mode) 349 "\
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)
200 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 451 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
201 452
202 ;; Define the font-lock keyword table. 453 ;; Define the font-lock keyword table.
203 (defconst doctest-font-lock-keywords 454 (defconst doctest-font-lock-keywords
204 (let ((prompt "^[ \t]*\\(>>>\\|\\.\\.\\.\\)") 455 `(
205 (kw1 (mapconcat 'identity 456 ;; The following pattern colorizes source lines. In particular,
206 '("and" "assert" "break" "class" 457 ;; it first matches prompts, and then looks for any of the
207 "continue" "def" "del" "elif" 458 ;; following matches *on the same line* as the prompt. It uses
208 "else" "except" "exec" "for" 459 ;; the form:
209 "from" "global" "if" "import" 460 ;;
210 "in" "is" "lambda" "not" 461 ;; (MATCHER MATCH-HIGHLIGHT
211 "or" "pass" "print" "raise" 462 ;; (ANCHOR-MATCHER nil nil MATCH-HIGHLIGHT))
212 "return" "while" "yield" 463 ;;
213 ) 464 ;; See the variable documentation for font-lock-keywords for a
214 "\\|")) 465 ;; description of what each of those means.
215 (kw2 (mapconcat 'identity 466 ("^[ \t]*\\(>>>\\|\\.\\.\\.\\)"
216 '("else:" "except:" "finally:" "try:") 467 (1 'doctest-prompt-face)
217 "\\|")) 468 (doctest-source-matcher
218 (kw3 (mapconcat 'identity 469 nil nil
219 '("ArithmeticError" "AssertionError" 470 (1 'font-lock-comment-face t t) ; comments
220 "AttributeError" "DeprecationWarning" "EOFError" 471 (2 'font-lock-keyword-face t t) ; def/class
221 "Ellipsis" "EnvironmentError" "Exception" "False" 472 (3 'font-lock-type-face t t) ; func/class name
222 "FloatingPointError" "FutureWarning" "IOError" 473 ;; group 4 (builtins preceeded by '.') gets no colorization.
223 "ImportError" "IndentationError" "IndexError" 474 (5 'font-lock-keyword-face t t) ; keywords & builtins
224 "KeyError" "KeyboardInterrupt" "LookupError" 475 (6 'font-lock-preprocessor-face t t) ; decorators
225 "MemoryError" "NameError" "None" "NotImplemented" 476 (7 'font-lock-string-face t t) ; strings
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))
318 )) 477 ))
319 "Expressions to highlight in Doctest mode.") 478
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.")
320 510
321 (defun doctest-output-line-matcher (limit) 511 (defun doctest-output-line-matcher (limit)
322 "A `font-lock-keyword' MATCHER that returns t if the current 512 "A `font-lock-keyword' MATCHER that returns t if the current
323 line is the expected output for a doctest example, and if so, 513 line is the expected output for a doctest example, and if so,
324 sets `match-data' so that group 0 spans the current line." 514 sets `match-data' so that group 0 spans the current line."
325 ;; The real work is done by find-doctest-output-line. 515 ;; The real work is done by doctest-find-output-line.
326 (when (find-doctest-output-line limit) 516 (when (doctest-find-output-line limit)
327 ;; If we found one, then mark the entire line. 517 ;; If we found one, then mark the entire line.
328 (beginning-of-line) 518 (beginning-of-line)
329 (search-forward-regexp "[^\n]*" limit))) 519 (re-search-forward "[^\n]*" limit)))
330 520
331 ;; [XX] Under construction. 521 (defun doctest-traceback-line-matcher (limit)
332 (defun doctest-selection-matcher (limit) 522 "A `font-lock-keyword' MATCHER that returns t if the current line is
333 (let (found-it) 523 the beginning of a traceback, and if so, sets `match-data' so that
334 (while (and (not found-it) 524 group 0 spans the entire traceback. n.b.: limit is ignored."
335 (search-forward-regexp "^[ \t]*\\(>>>\\|[.][.][.]\\)" 525 (beginning-of-line)
336 limit t)) 526 (when (looking-at doctest-traceback-re)
337 (if (get-text-property (point) 'doctest-selected) 527 (goto-char (match-end 0))
338 (setq found-it t))) 528 t))
339 found-it)) 529
340 530 (defun doctest-source-matcher (limit)
341 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 531 "A `font-lock-keyword' MATCHER that returns t if the current line
342 ;; Source line indentation 532 contains a Python source expression that should be highlighted
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
343 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 602 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
344 603
345 (defun doctest-indent-source-line (&optional dedent-only) 604 (defun doctest-indent-source-line (&optional dedent-only)
346 "Re-indent the current line, as doctest source code. I.e., add a 605 "Re-indent the current line, as doctest source code. I.e., add a
347 prompt to the current line if it doesn't have one, and re-indent the 606 prompt to the current line if it doesn't have one, and re-indent the
354 (let ((new-indent (doctest-current-source-line-indentation dedent-only)) 613 (let ((new-indent (doctest-current-source-line-indentation dedent-only))
355 (new-margin (doctest-current-source-line-margin)) 614 (new-margin (doctest-current-source-line-margin))
356 (line-had-prompt (looking-at doctest-prompt-re))) 615 (line-had-prompt (looking-at doctest-prompt-re)))
357 ;; Delete the old prompt (if any). 616 ;; Delete the old prompt (if any).
358 (when line-had-prompt 617 (when line-had-prompt
359 (goto-char (match-end 1)) 618 (goto-char (match-beginning 2))
360 (delete-char 4)) 619 (delete-char (- (match-end 2) (match-beginning 2))))
361 ;; Delete the old indentation. 620 ;; Delete the old indentation.
362 (delete-backward-char (skip-chars-forward " \t")) 621 (delete-backward-char (skip-chars-forward " \t"))
363 ;; If it's a continuation line, or a new PS1 prompt, 622 ;; If it's a continuation line, or a new PS1 prompt,
364 ;; then copy the margin. 623 ;; then copy the margin.
365 (when (or new-indent (not line-had-prompt)) 624 (when (or new-indent (not line-had-prompt))
377 636
378 (defun doctest-current-source-line-indentation (&optional dedent-only) 637 (defun doctest-current-source-line-indentation (&optional dedent-only)
379 "Return the post-prompt indent to use for this line. This is an 638 "Return the post-prompt indent to use for this line. This is an
380 integer for a continuation lines, and nil for non-continuation lines." 639 integer for a continuation lines, and nil for non-continuation lines."
381 (save-excursion 640 (save-excursion
382 (let ((prev-line-indent 0) 641 ;; Examine the previous doctest line (if present).
383 (curr-line-indent 0) 642 (let* ((prev-stmt-info (doctest-prev-statement-info))
384 (prev-line-opens-block nil) 643 (prev-stmt-indent (nth 0 prev-stmt-info))
385 (prev-line-closes-block nil) 644 (continuation-indent (nth 1 prev-stmt-info))
386 (curr-line-outdented nil)) 645 (prev-stmt-opens-block (nth 2 prev-stmt-info))
646 (prev-stmt-closes-block (nth 3 prev-stmt-info))
647 (prev-stmt-blocks-outdent (nth 4 prev-stmt-info))
648 )
387 ;; Examine this doctest line. 649 ;; Examine this doctest line.
388 (beginning-of-line) 650 (let* ((curr-line-indent 0)
389 (when (looking-at doctest-prompt-re) 651 (curr-line-outdented nil))
652 (beginning-of-line)
653 (when (looking-at doctest-prompt-re)
390 (setq curr-line-indent (- (match-end 3) (match-beginning 3))) 654 (setq curr-line-indent (- (match-end 3) (match-beginning 3)))
391 (goto-char (match-end 3))) 655 (goto-char (match-end 3)))
392 (setq curr-line-outdented (looking-at doctest-outdent-re)) 656 (setq curr-line-outdented (and (looking-at doctest-outdent-re)
393 ;; Examine the previous line. 657 (not prev-stmt-blocks-outdent)))
394 (when (= (forward-line -1) 0) ; move up a line 658 ;; Compute the overall indent.
395 (when (looking-at doctest-prompt-re) ; is it a source line? 659 (let ((indent (or continuation-indent
396 (let ((indent-beg (column-at-char (match-beginning 3))) 660 (+ prev-stmt-indent
397 (indent-end (column-at-char (match-end 3)))) 661 (if curr-line-outdented -4 0)
398 (setq prev-line-indent (- indent-end indent-beg)) 662 (if prev-stmt-opens-block 4 0)
399 (goto-char (match-end 3)) 663 (if prev-stmt-closes-block -4 0)))))
400 (if (looking-at doctest-open-block-re) 664 ;; If dedent-only is true, then make sure we don't indent.
401 (setq prev-line-opens-block t)) 665 (when dedent-only
402 (if (looking-at doctest-close-block-re) 666 (setq indent (min indent curr-line-indent)))
403 (setq prev-line-closes-block t)) 667 ;; If indent=0 and we're not outdented, then set indent to
404 (if (looking-at doctest-no-outdent-re) 668 ;; nil (to signify the start of a new source example).
405 (setq curr-line-outdented nil)) 669 (when (and (= indent 0)
406 ))) 670 (not (or curr-line-outdented continuation-indent)))
407 (let ((indent (+ prev-line-indent 671 (setq indent nil))
408 (if curr-line-outdented -4 0) 672 ;; Return the indentation.
409 (if prev-line-opens-block 4 0) 673 indent)))))
410 (if prev-line-closes-block -4 0)))) 674
411 ;; If dedent-only is true, then make sure we don't indent. 675 (defun doctest-prev-statement-info (&optional debug)
412 (when dedent-only 676 (save-excursion
413 (setq indent (min indent curr-line-indent))) 677 (forward-line -1)
414 ;; If indent=0 and we're not outdented, then set indent to 678 (doctest-statement-info debug)))
415 ;; nil (to signify the start of a new source example). 679
416 (when (and (= indent 0) (not curr-line-outdented)) 680 (defun doctest-statement-info (&optional debug)
417 (setq indent nil)) 681 "Collect info about the previous statement, and return it as a list:
418 ;; Return the indentation. 682
419 indent)))) 683 (INDENT, CONTINUATION, OPENS-BLOCK, CLOSES-BLOCK, BLOCKS-OUTDENT,
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)))))))
420 787
421 (defun doctest-current-source-line-margin () 788 (defun doctest-current-source-line-margin ()
422 "Return the pre-prompt margin to use for this source line. This is 789 "Return the pre-prompt margin to use for this source line. This is
423 copied from the most recent source line, or set to 790 copied from the most recent source line, or set to
424 `doctest-default-margin' if there are no preceeding source lines." 791 `doctest-default-margin' if there are no preceeding source lines."
425 (save-excursion 792 (save-excursion
426 (beginning-of-line) 793 (save-restriction
427 (if (search-backward-regexp doctest-prompt-re nil t) 794 (when (doctest-in-mmm-docstring-overlay)
428 (let ((margin-beg (column-at-char (match-beginning 1))) 795 (doctest-narrow-to-mmm-overlay))
429 (margin-end (column-at-char (match-end 1)))) 796 (beginning-of-line)
430 (- margin-end margin-beg)) 797 (forward-line -1)
431 doctest-default-margin))) 798 (while (and (not (doctest-on-source-line-p))
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))))
432 806
433 (defun doctest-electric-backspace () 807 (defun doctest-electric-backspace ()
434 "Delete the preceeding character, level of indentation, or 808 "Delete the preceeding character, level of indentation, or
435 prompt. 809 prompt.
436 810
506 ;; If we're avoiding trailing spaces, then delete WS before point. 880 ;; If we're avoiding trailing spaces, then delete WS before point.
507 (if doctest-avoid-trailing-whitespace 881 (if doctest-avoid-trailing-whitespace
508 (delete-char (- (skip-chars-backward " \t")))) 882 (delete-char (- (skip-chars-backward " \t"))))
509 (cond 883 (cond
510 ;; If we're on an empty prompt, delete it. 884 ;; If we're on an empty prompt, delete it.
511 ((on-empty-doctest-source-line) 885 ((doctest-on-empty-source-line-p)
512 (delete-region (match-beginning 0) (match-end 0)) 886 (delete-region (match-beginning 0) (match-end 0))
513 (insert-char ?\n 1)) 887 (insert-char ?\n 1))
514 ;; If we're on a doctest line, add a new prompt. 888 ;; If we're on a doctest line, add a new prompt.
515 ((on-doctest-source-line) 889 ((doctest-on-source-line-p)
516 (insert-char ?\n 1) 890 (insert-char ?\n 1)
517 (doctest-indent-source-line)) 891 (doctest-indent-source-line))
518 ;; If we're in doctest output, indent to the margin. 892 ;; If we're in doctest output, indent to the margin.
519 ((on-doctest-output-line) 893 ((doctest-on-output-line-p)
520 (insert-char ?\n 1) 894 (insert-char ?\n 1)
521 (insert-char ?\ (doctest-current-source-line-margin))) 895 (insert-char ?\ (doctest-current-source-line-margin)))
522 ;; Otherwise, just add a newline. 896 ;; Otherwise, just add a newline.
523 (t (insert-char ?\n 1)))) 897 (t (insert-char ?\n 1))))
524 898
525 (defun doctest-electric-colon () 899 (defun doctest-electric-colon ()
526 "Insert a colon, and dedent the line when appropriate." 900 "Insert a colon, and dedent the line when appropriate."
527 (interactive "*") 901 (interactive "*")
528 (insert-char ?: 1) 902 (insert-char ?: 1)
529 (when (on-doctest-source-line) 903 (when (doctest-on-source-line-p)
530 (doctest-indent-source-line t))) 904 (doctest-indent-source-line t)))
531 905
532 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 906 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
533 ;; Code Execution 907 ;;; Code Execution
534 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 908 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
535 909
536 ;; Add support for options (eg diff!) 910 (defun doctest-execute ()
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
537 (defun doctest-execute-buffer () 930 (defun doctest-execute-buffer ()
538 "Run doctest on the current buffer, and display the results in the 931 "Run doctest on the current buffer, and display the results in the
539 *doctest-output* buffer." 932 *doctest-output* buffer."
540 (interactive "*") 933 (interactive)
541 (setq doctest-results-buffer (get-buffer-create "*doctest-output*")) 934 (doctest-execute-region (point-min) (point-max) nil nil))
542 (let* ((temp (concat (doctest-temp-name) ".py")) 935
543 (tempfile (expand-file-name temp doctest-temp-directory)) 936 (defun doctest-execute-region (start end &optional diff
544 (cur-buf (current-buffer)) 937 check-for-mmm-docstring-overlay)
545 (in-buf (get-buffer-create "*doctest-input*")) 938 "Run doctest on the current buffer, and display the results in the
546 (beg (point-min)) (end (point-max)) 939 *doctest-output* buffer."
547 (script (concat "from doctest import *\n" 940 (interactive "r")
548 "doc = open('" tempfile "').read()\n" 941 ;; If it's already running, give the user a chance to restart it.
549 "test = DocTestParser().get_doctest(" 942 (when (doctest-process-live-p doctest-async-process)
550 "doc, {}, '" (buffer-name) "', '" 943 (when (y-or-n-p "Doctest is already running. Restart it? ")
551 (buffer-file-name) "', 0)\n" 944 (doctest-cancel-async-process)
552 "r = DocTestRunner()\n" 945 (message "Killing doctest...")))
553 "r.run(test)\n")) 946 (cond
554 (cmd (concat doctest-python-command " -c \"" script "\""))) 947 ((and doctest-async (doctest-process-live-p doctest-async-process))
555 ;; Write buffer to a file. 948 (message "Can't run two doctest processes at once!"))
556 (save-excursion 949 (t
557 (set-buffer in-buf) 950 (let* ((results-buf-name (doctest-results-buffer-name))
558 (insert-buffer-substring cur-buf beg end) 951 (in-docstring (and check-for-mmm-docstring-overlay
559 (write-file tempfile)) 952 (doctest-in-mmm-docstring-overlay)))
560 ;; Run doctest 953 (temp (doctest-temp-name)) (dir doctest-temp-directory)
561 (shell-command cmd doctest-results-buffer) 954 (input-file (expand-file-name (concat temp ".py") dir))
562 ;; Delete the temp file 955 (globs-file (when in-docstring
563 (delete-file tempfile) 956 (expand-file-name (concat temp "-globs.py") dir)))
564 ;; Set mode on output buffer. 957 (cur-buf (current-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))
565 (save-excursion 1147 (save-excursion
566 (set-buffer doctest-results-buffer) 1148 (set-buffer doctest-results-buffer)
567 (doctest-results-mode)) 1149 (goto-char (point-max))
568 ;; If any tests failed, display them. 1150 (while (re-search-backward (doctest-results-loc-re) nil t)
569 (cond ((> (buffer-size doctest-results-buffer) 0) 1151 (let ((lineno (string-to-int (match-string 1))))
570 (message "Test failed!") 1152 (when (equal doctest-results-py-version 'py21)
571 (display-buffer doctest-results-buffer) 1153 (setq lineno (+ lineno 1)))
572 (doctest-postprocess-results)) 1154 (while (and markers (< lineno (cdar markers)))
573 (t 1155 (set-marker (caar markers) nil)
574 (message "Test passed!") 1156 (setq markers (cdr markers)))
575 (if (get-buffer-window doctest-results-buffer) 1157 (if (and markers (= lineno (cdar markers)))
576 (delete-window (get-buffer-window doctest-results-buffer))))))) 1158 (push (pop markers) filtered)
577 1159 (doctest-warn "Example expected on line %d but not found %s"
578 (defun doctest-postprocess-results () 1160 lineno markers)))))
579 (doctest-next-failure 1) 1161 (dolist (marker-info markers)
580 (if hide-example-source 1162 (set-marker (car marker-info) nil))
581 (hide-example-source))) 1163 (setq doctest-example-markers filtered)))
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
582 1186
583 (defun doctest-next-failure (count) 1187 (defun doctest-next-failure (count)
584 "Move to the top of the next failing example, and highlight the 1188 "Move to the top of the next failing example, and highlight the
585 example's failure description in *doctest-output*." 1189 example's failure description in *doctest-output*."
586 (interactive "p") 1190 (interactive "p")
587 (let (lineno) 1191 (cond
588 (cond 1192 ((and doctest-async (doctest-process-live-p doctest-async-process))
589 ((not (buffer-live-p doctest-results-buffer)) 1193 (message "Wait for doctest to finish running!"))
590 (message "Run doctest first! (C-c C-c)")) 1194 ((not (doctest-results-buffer-valid-p))
591 (t 1195 (message "Run doctest first! (C-c C-c)"))
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)))
592 (save-excursion 1201 (save-excursion
593 (let ((orig-window (selected-window)) 1202 (set-buffer doctest-results-buffer)
594 (results-window (display-buffer doctest-results-buffer))) 1203 ;; Pick up where we left off.
595 ;; Switch to the results window (so its point gets updated) 1204 ;; (nb: doctest-selected-failure is buffer-local)
596 (if results-window (select-window results-window)) 1205 (goto-char (or doctest-selected-failure (point-min)))
597 ;; Pick up where we left off. 1206 ;; Skip past anything on *this* line.
598 ;; (nb: doctest-selected-failure is buffer-local) 1207 (if (>= count 0) (end-of-line) (beginning-of-line))
599 (goto-char (or doctest-selected-failure (point-min))) 1208 ;; Look for the next failure
600 ;; Skip past anything on *this* line. 1209 (when
601 (if (>= count 0) (end-of-line) (beginning-of-line)) 1210 (if (>= count 0)
602 ;; Look for the next failure 1211 (re-search-forward (doctest-results-loc-re) nil t count)
603 (if (>= count 0) 1212 (re-search-backward (doctest-results-loc-re) nil t (- count)))
604 (re-search-forward doctest-results-loc-re nil t count) 1213 ;; We found a failure:
605 (re-search-backward doctest-results-loc-re nil t (- count))) 1214 (let ((old-selected-failure doctest-selected-failure))
606 (cond 1215 (beginning-of-line)
607 ;; We found a failure: 1216 ;; Extract the line number for the doctest file.
608 ((match-string 2) 1217 (let ((orig-lineno (string-to-int (match-string 1))))
609 (let ((old-selected-failure doctest-selected-failure)) 1218 (when (equal doctest-results-py-version 'py21)
610 ;; Extract the line number for the doctest file. 1219 (setq orig-lineno (+ orig-lineno 1)))
611 (setq lineno (string-to-int (match-string 2))) 1220 (dolist (marker-info example-markers)
612 ;; Store our position for next time. 1221 (when (= orig-lineno (cdr marker-info))
613 (beginning-of-line) 1222 (setq marker (car marker-info)))))
614 (setq doctest-selected-failure (point)) 1223
615 ;; Update selection. 1224 ;; Update the window cursor.
616 (doctest-fontify-line old-selected-failure) 1225 (beginning-of-line)
617 (doctest-fontify-line doctest-selected-failure))) 1226 (set-window-point results-window (point))
618 ;; We didn't find a failure: 1227 ;; Store our position for next time.
619 (t 1228 (setq doctest-selected-failure (point))
620 (message "No failures found!"))) 1229 ;; Update selection.
621 ;; Return to the original window 1230 (doctest-fontify-line old-selected-failure)
622 (select-window orig-window))))) 1231 (doctest-fontify-line doctest-selected-failure))))
623 1232
624 (when lineno 1233 (cond
625 ;; Move point to the selected failure. 1234 ;; We found a failure -- move point to the selected failure.
626 (goto-line lineno) 1235 (marker
627 ; ;; Highlight it. [XX] Under construction. 1236 (goto-char (marker-position marker))
628 ; (let ((beg (save-excursion (beginning-of-line) (point))) 1237 (beginning-of-line))
629 ; (end (save-excursion (end-of-line) (point)))) 1238 ;; We didn't find a failure, but there is one -- wrap.
630 ; (add-text-properties (point-min) (point-max) '(doctest-selected nil)) 1239 ((> (length doctest-example-markers) 0)
631 ; (add-text-properties beg end '(doctest-selected t)) 1240 (if (>= count 0) (doctest-first-failure) (doctest-last-failure)))
632 ; (doctest-fontify-line (point))) 1241 ;; We didn't find a failure -- alert the user.
633 ))) 1242 (t (message "No failures found!")))))))
634 1243
635 (defun doctest-prev-failure (count) 1244 (defun doctest-prev-failure (count)
636 "Move to the top of the previous failing example, and highlight 1245 "Move to the top of the previous failing example, and highlight
637 the example's failure description in *doctest-output*." 1246 the example's failure description in *doctest-output*."
638 (interactive "p") 1247 (interactive "p")
639 (doctest-next-failure (- count))) 1248 (doctest-next-failure (- count)))
640 1249
641 (defun doctest-first-failure () 1250 (defun doctest-first-failure ()
642 (interactive "") 1251 "Move to the top of the first failing example, and highlight
1252 the example's failure description in *doctest-output*."
1253 (interactive)
643 (if (buffer-live-p doctest-results-buffer) 1254 (if (buffer-live-p doctest-results-buffer)
644 (save-excursion 1255 (save-excursion
645 (set-buffer doctest-results-buffer) 1256 (set-buffer doctest-results-buffer)
646 (let ((old-selected-failure doctest-selected-failure)) 1257 (let ((old-selected-failure doctest-selected-failure))
647 (setq doctest-selected-failure (point-min)) 1258 (setq doctest-selected-failure (point-min))
648 (doctest-fontify-line old-selected-failure)))) 1259 (doctest-fontify-line old-selected-failure))))
649 (doctest-next-failure 1)) 1260 (doctest-next-failure 1))
650 1261
651 (defun doctest-last-failure () 1262 (defun doctest-last-failure ()
652 (interactive "") 1263 "Move to the top of the last failing example, and highlight
1264 the example's failure description in *doctest-output*."
1265 (interactive)
653 (if (buffer-live-p doctest-results-buffer) 1266 (if (buffer-live-p doctest-results-buffer)
654 (save-excursion 1267 (save-excursion
655 (set-buffer doctest-results-buffer) 1268 (set-buffer doctest-results-buffer)
656 (let ((old-selected-failure doctest-selected-failure)) 1269 (let ((old-selected-failure doctest-selected-failure))
657 (setq doctest-selected-failure (point-max)) 1270 (setq doctest-selected-failure (point-max))
658 (doctest-fontify-line old-selected-failure)))) 1271 (doctest-fontify-line old-selected-failure))))
659 (doctest-next-failure -1)) 1272 (doctest-next-failure -1))
660 1273
661 (defconst doctest-example-source-re 1274 (defun doctest-select-failure ()
662 "^Failed example:\n\\(\n\\| [^\n]*\n\\)+") 1275 "Move to the top of the currently selected example, and select that
663 (defun hide-example-source () 1276 example in the source buffer. Intended for use in the results
664 "Delete the source code listings from the results buffer (since it's 1277 buffer."
665 easy enough to see them in the original buffer)" 1278 (interactive)
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 ()
666 (save-excursion 1582 (save-excursion
667 (set-buffer doctest-results-buffer) 1583 (let ((pos (car (doctest-mmm-overlay-bounds))))
668 (toggle-read-only nil) 1584 (goto-char pos)
669 (beginning-of-buffer) 1585 (when (doctest-looking-back "\"\"\"\\|\'\'\'")
670 (while (re-search-forward doctest-example-source-re nil t) 1586 (setq pos (- pos 3)))
671 (replace-match "" nil nil)) 1587 (beginning-of-line)
672 (toggle-read-only t))) 1588 (- pos (point)))))
673 1589
674 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1590 (defun doctest-mmm-overlay-bounds ()
675 ;; Doctest Results Mode (output of doctest-execute-buffer) 1591 (when (featurep 'mmm-auto)
676 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1592 (let ((overlay (mmm-overlay-at (point))))
677 ;; [XX] Todo: 1593 (when overlay
678 ;; - Make it read-only? 1594 (let ((start (overlay-start overlay))
679 ;; - Hitting enter goes to the corresponding error 1595 (end (overlay-end overlay)))
680 ;; - Clicking goes to corresponding error (not as useful) 1596 (when (doctest-in-mmm-docstring-overlay)
681 1597 (save-excursion
682 1598 (goto-char start)
683 (defconst doctest-results-divider-re 1599 (re-search-forward "[\"\']*")
684 "^\\([*]\\{60,\\}\\)$") 1600 (setq start (point))
685 1601 (goto-char end)
686 (defconst doctest-results-loc-re 1602 (while (doctest-looking-back "[\"\']")
687 "^File \"\\([^\"]+\\)\", line \\([0-9]+\\), in \\([^\n]+\\)") 1603 (backward-char 1))
688 1604 (setq end (point))))
689 (defconst doctest-results-header-re 1605 (cons start end))))))
690 "^\\([a-zA-Z0-9 ]+:\\)$") 1606
691 1607 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
692 (defconst doctest-results-font-lock-keywords 1608 ;;; Helper functions
693 `((,doctest-results-divider-re 1609 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
694 (0 'doctest-results-divider-face)) 1610
695 (,doctest-results-loc-re 1611 (defun doctest-on-source-line-p (&optional prompt)
696 (0 'doctest-results-loc-face)) 1612 "Return true if the current line is a source line. The optional
697 (,doctest-results-header-re 1613 argument prompt can be used to specify which type of source
698 (0 'doctest-results-header-face)) 1614 line (... or >>>)."
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."
739 (save-excursion 1615 (save-excursion
740 (beginning-of-line) 1616 (beginning-of-line)
741 (looking-at doctest-prompt-re))) 1617 ;; Check if we're looking at a prompt (of the right type).
742 1618 (when (and (looking-at doctest-prompt-re)
743 (defun on-empty-doctest-source-line () 1619 (or (null prompt)
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 ()
744 "Return true if the current line contains a bare prompt." 1628 "Return true if the current line contains a bare prompt."
745 (save-excursion 1629 (save-excursion
746 (beginning-of-line) 1630 (beginning-of-line)
747 (looking-at (concat doctest-prompt-re "$")))) 1631 (and (doctest-on-source-line-p)
748 1632 (looking-at (concat doctest-prompt-re "$")))))
749 (defun on-doctest-output-line () 1633
1634 (defun doctest-on-output-line-p ()
750 "Return true if the current line is an output line." 1635 "Return true if the current line is an output line."
751 (save-excursion 1636 (save-excursion
752 (beginning-of-line) 1637 (beginning-of-line)
753 (let ((prompt-or-blankline (concat doctest-prompt-re "\\|" "^[ \t]*\n"))) 1638 ;; The line must not be blank or a source line.
754 ;; The line must not be blank or start with a prompt. 1639 (when (not (or (doctest-on-source-line-p) (looking-at "[ \t]*$")))
755 (when (not (looking-at prompt-or-blankline)) 1640 ;; The line must follow a source line, with no intervening blank
756 ;; The line must follow a line starting with a prompt, with 1641 ;; lines.
757 ;; no intervening blank lines. 1642 (while (not (or (doctest-on-source-line-p) (looking-at "[ \t]*$")
758 (search-backward-regexp prompt-or-blankline nil t) 1643 (= (point) (point-min))))
759 (looking-at doctest-prompt-re))))) 1644 (forward-line -1))
760 1645 (doctest-on-source-line-p))))
761 (defun find-doctest-output-line (&optional limit) 1646
1647 (defun doctest-find-output-line (&optional limit)
762 "Move forward to the next doctest output line (staying within 1648 "Move forward to the next doctest output line (staying within
763 the given bounds). Return the character position of the doctest 1649 the given bounds). Return the character position of the doctest
764 output line if one was found, and false otherwise." 1650 output line if one was found, and false otherwise."
765 (let ((found-it nil) ; point where we found an output line 1651 (let ((found-it nil) ; point where we found an output line
766 (limit (or limit (point-max)))) ; default value for limit 1652 (limit (or limit (point-max)))) ; default value for limit
767 (save-excursion 1653 (save-excursion
768 ;; Keep moving forward, one line at a time, until we find a 1654 ;; Keep moving forward, one line at a time, until we find a
769 ;; doctest output line. 1655 ;; doctest output line.
770 (while (and (not found-it) (< (point) limit) (not (eobp))) 1656 (while (and (not found-it) (< (point) limit) (not (eobp)))
771 (if (and (not (eolp)) (on-doctest-output-line)) 1657 (if (and (not (eolp)) (doctest-on-output-line-p))
772 (setq found-it (point)) 1658 (setq found-it (point))
773 (forward-line)))) 1659 (forward-line))))
774 ;; If we found a doctest output line, then go to it. 1660 ;; If we found a doctest output line, then go to it.
775 (if found-it (goto-char found-it)))) 1661 (if found-it (goto-char found-it))))
776 1662
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
777 (defun doctest-version () 1746 (defun doctest-version ()
778 "Echo the current version of `doctest-mode' in the minibuffer." 1747 "Echo the current version of `doctest-mode' in the minibuffer."
779 (interactive) 1748 (interactive)
780 (message "Using `doctest-mode' version %s" doctest-version)) 1749 (message "Using `doctest-mode' version %s" doctest-version))
781 1750
782 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1751 (defun doctest-warn (msg &rest args)
783 ;; Utility functions 1752 "Display a doctest warning message."
784 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1753 (if (fboundp 'display-warning)
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)))
785 1762
786 (defvar doctest-serial-number 0) ;used if broken-temp-names. 1763 (defvar doctest-serial-number 0) ;used if broken-temp-names.
787 (defun doctest-temp-name () 1764 (defun doctest-temp-name ()
1765 "Return a new temporary filename, for use in calling doctest."
788 (if (memq 'broken-temp-names features) 1766 (if (memq 'broken-temp-names features)
789 (let 1767 (let
790 ((sn doctest-serial-number) 1768 ((sn doctest-serial-number)
791 (pid (and (fboundp 'emacs-pid) (emacs-pid)))) 1769 (pid (and (fboundp 'emacs-pid) (emacs-pid))))
792 (setq doctest-serial-number (1+ doctest-serial-number)) 1770 (setq doctest-serial-number (1+ doctest-serial-number))
793 (if pid 1771 (if pid
794 (format "doctest-%d-%d" sn pid) 1772 (format "doctest-%d-%d" sn pid)
795 (format "doctest-%d" sn))) 1773 (format "doctest-%d" sn)))
796 (make-temp-name "doctest-"))) 1774 (make-temp-name "doctest-")))
797 1775
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
815 (defun doctest-fontify-line (charpos) 1776 (defun doctest-fontify-line (charpos)
816 "Run font-lock-fontify-region on the line containing the given 1777 "Run font-lock-fontify-region on the line containing the given
817 position." 1778 position."
818 (if charpos 1779 (if (and charpos (functionp 'font-lock-fontify-region))
819 (save-excursion 1780 (save-excursion
820 (goto-char charpos) 1781 (goto-char charpos)
821 (let ((beg (progn (beginning-of-line) (point))) 1782 (let ((beg (progn (beginning-of-line) (point)))
822 (end (progn (end-of-line) (point)))) 1783 (end (progn (end-of-line) (point))))
823 (font-lock-fontify-region beg end))))) 1784 (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)
824 1909
825 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1910 ;; Declare local variables.
826 ;; Syntax Table 1911 (kill-all-local-variables)
827 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1912 (make-local-variable 'font-lock-defaults)
828 1913 (make-local-variable 'doctest-selected-failure)
829 ;; We do *NOT* currently use this, because it applies too 1914 (make-local-variable 'doctest-source-buffer)
830 ;; indiscrimanantly. In particular, we don't want "'" and '"' treated 1915 (make-local-variable 'doctest-results-py-version)
831 ;; as quote marks on text lines. But there's no good way to prevent 1916
832 ;; it. 1917 ;; Define local variables.
833 (defvar doctest-syntax-alist nil 1918 (setq major-mode 'doctest-results-mode
834 "Syntax alist used in `doctest-mode' buffers.") 1919 mode-name "Doctest-Results"
835 (setq doctest-syntax-alist '((?\( . "()") (?\[ . "(]") (?\{ . "(}") 1920 mode-line-process 'doctest-mode-line-process
836 (?\) . ")(") (?\] . ")[") (?\} . "){") 1921 font-lock-defaults '(doctest-results-font-lock-keywords
837 (?\$ . "." ) (?\% . "." ) (?\& . "." ) 1922 nil nil nil nil))
838 (?\* . "." ) (?\+ . "." ) (?\- . "." ) 1923 ;; Define keymap.
839 (?\/ . "." ) (?\< . "." ) (?\= . "." ) 1924 (use-local-map doctest-results-mode-map)
840 (?\> . "." ) (?\| . "." ) (?\_ . "w" ) 1925
841 (?\' . "\"") (?\" . "\"") (?\` . "$" ) 1926 ;; Define the syntax table.
842 (?\# . "<" ) (?\n . ">" ))) 1927 (set-syntax-table doctest-results-mode-syntax-table)
843 1928
844 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1929 ;; Enable font-lock mode.
845 ;; Key Bindings 1930 (if (featurep 'font-lock) (font-lock-mode 1)))
846 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1931
847 1932 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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.
848 (defconst doctest-mode-map 1972 (defconst doctest-mode-map
849 (let ((map (make-keymap))) 1973 (let ((map (make-keymap)))
850 (define-key map [backspace] 'doctest-electric-backspace) 1974 (define-key map [backspace] 'doctest-electric-backspace)
851 (define-key map [return] 'doctest-newline-and-indent) 1975 (define-key map [return] 'doctest-newline-and-indent)
852 (define-key map [tab] 'doctest-indent-source-line) 1976 (define-key map [tab] 'doctest-indent-source-line)
853 (define-key map ":" 'doctest-electric-colon) 1977 (define-key map ":" 'doctest-electric-colon)
854 (define-key map "\C-c\C-v" 'doctest-version) 1978 (define-key map "\C-c\C-v" 'doctest-version)
855 (define-key map "\C-c\C-c" 'doctest-execute-buffer) 1979 (define-key map "\C-c\C-c" 'doctest-execute)
1980 (define-key map "\C-c\C-d" 'doctest-execute-with-diff)
856 (define-key map "\C-c\C-n" 'doctest-next-failure) 1981 (define-key map "\C-c\C-n" 'doctest-next-failure)
857 (define-key map "\C-c\C-p" 'doctest-prev-failure) 1982 (define-key map "\C-c\C-p" 'doctest-prev-failure)
858 (define-key map "\C-c\C-a" 'doctest-first-failure) 1983 (define-key map "\C-c\C-a" 'doctest-first-failure)
1984 (define-key map "\C-c\C-e" 'doctest-last-failure)
859 (define-key map "\C-c\C-z" 'doctest-last-failure) 1985 (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)
860 map) 1988 map)
861 "Keymap for doctest-mode.") 1989 "Keymap for doctest-mode.")
862 1990
863 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1991 ;; Syntax table for doctest-mode.
864 ;; Define the mode 1992 (defvar doctest-mode-syntax-table nil
865 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1993 "Syntax table used in `doctest-mode' buffers.")
866 1994 (when (not doctest-mode-syntax-table)
867 ;; Register the font-lock keywords (xemacs) 1995 (setq doctest-mode-syntax-table (make-syntax-table))
868 (put 'doctest-mode 'font-lock-defaults '(doctest-font-lock-keywords)) 1996 (dolist (entry '(("(" . "()") ("[" . "(]") ("{" . "(}")
869 1997 (")" . ")(") ("]" . ")[") ("}" . "){")
870 ;; Register the font-lock keywords (gnu emacs) 1998 ("$%&*+-/<=>|'\"`" . ".") ("_" . "w")))
871 (defvar font-lock-defaults-alist nil) ; in case we're in xemacs 1999 (dolist (char (string-to-list (car entry)))
872 (setq font-lock-defaults-alist 2000 (modify-syntax-entry char (cdr entry) doctest-mode-syntax-table))))
873 (append font-lock-defaults-alist
874 `((doctest-mode doctest-font-lock-keywords nil nil nil nil))))
875 2001
876 ;; Use doctest mode for files ending in .doctest 2002 ;; Use doctest mode for files ending in .doctest
877 ;;;###autoload 2003 ;;;###autoload
878 (add-to-list 'auto-mode-alist '("\\.doctest$" . doctest-mode)) 2004 (add-to-list 'auto-mode-alist '("\\.doctest$" . doctest-mode))
879 2005
880 ;;;###autoload 2006 ;;;###autoload
881 (define-derived-mode doctest-mode text-mode "Doctest" 2007 (defun doctest-mode ()
882 "A major mode for editing text files that contain Python 2008 "A major mode for editing text files that contain Python
883 doctest examples. Doctest is a testing framework for Python that 2009 doctest examples. Doctest is a testing framework for Python that
884 emulates an interactive session, and checks the result of each 2010 emulates an interactive session, and checks the result of each
885 command. For more information, see the Python library reference: 2011 command. For more information, see the Python library reference:
886 <http://docs.python.org/lib/module-doctest.html> 2012 <http://docs.python.org/lib/module-doctest.html>
897 specific output faces. 2023 specific output faces.
898 2024
899 - 'Text lines' are any other lines. They are not processed in 2025 - 'Text lines' are any other lines. They are not processed in
900 any special way. 2026 any special way.
901 2027
902 \\{doctest-mode-map} 2028 \\{doctest-mode-map}"
903 " 2029 (interactive)
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
904 ;; Enable auto-fill mode. 2050 ;; Enable auto-fill mode.
905 (auto-fill-mode 1) 2051 (auto-fill-mode 1)
2052 (setq auto-fill-function 'doctest-do-auto-fill)
906 2053
907 ;; Enable font-lock mode. 2054 ;; Enable font-lock mode.
908 (if (featurep 'font-lock) (font-lock-mode 1)) 2055 (if (featurep 'font-lock) (font-lock-mode 1))
909 2056
910 ;; Register our indentation function. 2057 ;; Run the mode hook.
911 (set (make-local-variable 'indent-line-function) 2058 (run-hooks 'doctest-mode-hook))
912 'doctest-indent-source-line)
913
914 ;; Keep track of our results buffer.
915 (set (make-local-variable 'doctest-results-buffer) nil)
916 )
917 2059
918 (provide 'doctest-mode) 2060 (provide 'doctest-mode)
919 ;;; doctest-mode.el ends here 2061 ;;; doctest-mode.el ends here