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

Initial import from svn.
author Augie Fackler <durin42@gmail.com>
date Wed, 26 Nov 2008 10:56:09 -0600
parents
children b5d75594b356
comparison
equal deleted inserted replaced
-1:000000000000 0:c30d68fbd368
1 ;;; doctest-mode.el --- Major mode for editing Python doctest files
2
3 ;; Copyright (C) 2004-2007 Edward Loper
4
5 ;; Author: Edward Loper
6 ;; Maintainer: edloper@alum.mit.edu
7 ;; Created: Aug 2004
8 ;; Keywords: python doctest unittest test docstring
9
10 (defconst doctest-version "0.5 alpha"
11 "`doctest-mode' version number.")
12
13 ;; This software is provided as-is, without express or implied
14 ;; warranty. Permission to use, copy, modify, distribute or sell this
15 ;; software, without fee, for any purpose and by any individual or
16 ;; organization, is hereby granted, provided that the above copyright
17 ;; notice and this paragraph appear in all copies.
18
19 ;; This is a major mode for editing text files that contain Python
20 ;; doctest examples. Doctest is a testing framework for Python that
21 ;; emulates an interactive session, and checks the result of each
22 ;; command. For more information, see the Python library reference:
23 ;; <http://docs.python.org/lib/module-doctest.html>
24
25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26 ;;; Table of Contents
27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
28 ;; 1. Customization: use-editable variables to customize
29 ;; doctest-mode.
30 ;;
31 ;; 2. Fonts: defines new font-lock faces.
32 ;;
33 ;; 3. Constants: various consts (mainly regexps) used by the rest
34 ;; of the code.
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
71 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
72
73 (defgroup doctest nil
74 "Support for the Python doctest framework"
75 :group 'languages
76 :prefix "doctest-")
77
78 (defcustom doctest-default-margin 4
79 "The default pre-prompt margin for doctest examples."
80 :type 'integer
81 :group 'doctest)
82
83 (defcustom doctest-avoid-trailing-whitespace t
84 "If true, then delete trailing whitespace when inserting a newline."
85 :type 'boolean
86 :group 'doctest)
87
88 (defcustom doctest-temp-directory
89 (let ((ok '(lambda (x)
90 (and x
91 (setq x (expand-file-name x)) ; always true
92 (file-directory-p x)
93 (file-writable-p x)
94 x))))
95 (or (funcall ok (getenv "TMPDIR"))
96 (funcall ok "/usr/tmp")
97 (funcall ok "/tmp")
98 (funcall ok "/var/tmp")
99 (funcall ok ".")
100 (error (concat "Couldn't find a usable temp directory -- "
101 "set `doctest-temp-directory'"))))
102 "Directory used for temporary files created when running doctest.
103 By default, the first directory from this list that exists and that you
104 can write into: the value (if any) of the environment variable TMPDIR,
105 /usr/tmp, /tmp, /var/tmp, or the current directory."
106 :type 'string
107 :group 'doctest)
108
109 (defcustom doctest-hide-example-source nil
110 "If true, then don't display the example source code for each
111 failure in the results buffer."
112 :type 'boolean
113 :group 'doctest)
114
115 (defcustom doctest-python-command "python"
116 "Shell command used to start the python interpreter"
117 :type 'string
118 :group 'doctest)
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
177 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
178
179 (defface doctest-prompt-face
180 '((((class color) (background dark))
181 (:foreground "#68f"))
182 (t (:foreground "#226")))
183 "Face for Python prompts in doctest examples."
184 :group 'doctest)
185
186 (defface doctest-output-face
187 '((((class color) (background dark))
188 (:foreground "#afd"))
189 (t (:foreground "#262")))
190 "Face for the output of doctest examples."
191 :group 'doctest)
192
193 (defface doctest-output-marker-face
194 '((((class color) (background dark))
195 (:foreground "#0f0"))
196 (t (:foreground "#080")))
197 "Face for markers in the output of doctest examples."
198 :group 'doctest)
199
200 (defface doctest-output-traceback-face
201 '((((class color) (background dark))
202 (:foreground "#f88"))
203 (t (:foreground "#622")))
204 "Face for traceback headers in the output of doctest examples."
205 :group 'doctest)
206
207 (defface doctest-results-divider-face
208 '((((class color) (background dark))
209 (:foreground "#08f"))
210 (t (:foreground "#00f")))
211 "Face for dividers in the doctest results window."
212 :group 'doctest)
213
214 (defface doctest-results-loc-face
215 '((((class color) (background dark))
216 (:foreground "#0f8"))
217 (t (:foreground "#084")))
218 "Face for location headers in the doctest results window."
219 :group 'doctest)
220
221 (defface doctest-results-header-face
222 '((((class color) (background dark))
223 (:foreground "#8ff"))
224 (t (:foreground "#088")))
225 "Face for sub-headers in the doctest results window."
226 :group 'doctest)
227
228 (defface doctest-results-selection-face
229 '((((class color) (background dark))
230 (:foreground "#ff0" :background "#008"))
231 (t (:background "#088" :foreground "#fff")))
232 "Face for selected failure's location header in the results window."
233 :group 'doctest)
234
235 (defface doctest-selection-face
236 '((((class color) (background dark))
237 (:foreground "#ff0" :background "#00f" :bold t))
238 (t (:foreground "#f00")))
239 "Face for selected example's prompt"
240 :group 'doctest)
241
242 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
243 ;;; Constants
244 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
245
246 (defconst doctest-prompt-re
247 "^\\(?:\\([ \t]*\\)\\(>>> ?\\|[.][.][.] ?\\)\\([ \t]*\\)\\)"
248 "Regular expression for doctest prompts. It defines three groups:
249 the pre-prompt margin; the prompt; and the post-prompt indentation.")
250
251 (defconst doctest-open-block-re
252 "[^\n]+:[ \t]*\\(#.*\\)?$"
253 "Regular expression for a line that opens a block")
254
255 (defconst doctest-close-block-re
256 "\\(return\\|raise\\|break\\|continue\\|pass\\)\\b"
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.")
314
315 (defconst doctest-outdent-re
316 (concat "\\(" (mapconcat 'identity
317 '("else:"
318 "except\\(\\s +.*\\)?:"
319 "finally:"
320 "elif\\s +.*:")
321 "\\|")
322 "\\)")
323 "Regular expression for a line that should be outdented. Any line
324 that matches `doctest-outdent-re', but does not follow a line matching
325 `doctest-no-outdent-re', will be outdented.")
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.
330 (defconst doctest-no-outdent-re
331 (concat
332 "\\("
333 (mapconcat 'identity
334 (list "try:"
335 "except\\(\\s +.*\\)?:"
336 "while\\s +.*:"
337 "for\\s +.*:"
338 "if\\s +.*:"
339 "elif\\s +.*:"
340 "\\(return\\|raise\\|break\\|continue\\|pass\\)[ \t\n]"
341 )
342 "\\|")
343 "\\)")
344 "Regular expression matching lines not to outdent after. Any line
345 that matches `doctest-outdent-re', but does not follow a line matching
346 `doctest-no-outdent-re', will be outdented.")
347
348 (defconst doctest-script
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)
451 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
452
453 ;; Define the font-lock keyword table.
454 (defconst doctest-font-lock-keywords
455 `(
456 ;; The following pattern colorizes source lines. In particular,
457 ;; it first matches prompts, and then looks for any of the
458 ;; following matches *on the same line* as the prompt. It uses
459 ;; the form:
460 ;;
461 ;; (MATCHER MATCH-HIGHLIGHT
462 ;; (ANCHOR-MATCHER nil nil MATCH-HIGHLIGHT))
463 ;;
464 ;; See the variable documentation for font-lock-keywords for a
465 ;; description of what each of those means.
466 ("^[ \t]*\\(>>>\\|\\.\\.\\.\\)"
467 (1 'doctest-prompt-face)
468 (doctest-source-matcher
469 nil nil
470 (1 'font-lock-comment-face t t) ; comments
471 (2 'font-lock-keyword-face t t) ; def/class
472 (3 'font-lock-type-face t t) ; func/class name
473 ;; group 4 (builtins preceeded by '.') gets no colorization.
474 (5 'font-lock-keyword-face t t) ; keywords & builtins
475 (6 'font-lock-preprocessor-face t t) ; decorators
476 (7 'font-lock-string-face t t) ; strings
477 ))
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.")
510
511 (defun doctest-output-line-matcher (limit)
512 "A `font-lock-keyword' MATCHER that returns t if the current
513 line is the expected output for a doctest example, and if so,
514 sets `match-data' so that group 0 spans the current line."
515 ;; The real work is done by doctest-find-output-line.
516 (when (doctest-find-output-line limit)
517 ;; If we found one, then mark the entire line.
518 (beginning-of-line)
519 (re-search-forward "[^\n]*" limit)))
520
521 (defun doctest-traceback-line-matcher (limit)
522 "A `font-lock-keyword' MATCHER that returns t if the current line is
523 the beginning of a traceback, and if so, sets `match-data' so that
524 group 0 spans the entire traceback. n.b.: limit is ignored."
525 (beginning-of-line)
526 (when (looking-at doctest-traceback-re)
527 (goto-char (match-end 0))
528 t))
529
530 (defun doctest-source-matcher (limit)
531 "A `font-lock-keyword' MATCHER that returns t if the current line
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
602 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
603
604 (defun doctest-indent-source-line (&optional dedent-only)
605 "Re-indent the current line, as doctest source code. I.e., add a
606 prompt to the current line if it doesn't have one, and re-indent the
607 source code (to the right of the prompt). If `dedent-only' is true,
608 then don't increase the indentation level any."
609 (interactive "*")
610 (let ((indent-end nil))
611 (save-excursion
612 (beginning-of-line)
613 (let ((new-indent (doctest-current-source-line-indentation dedent-only))
614 (new-margin (doctest-current-source-line-margin))
615 (line-had-prompt (looking-at doctest-prompt-re)))
616 ;; Delete the old prompt (if any).
617 (when line-had-prompt
618 (goto-char (match-beginning 2))
619 (delete-char (- (match-end 2) (match-beginning 2))))
620 ;; Delete the old indentation.
621 (delete-backward-char (skip-chars-forward " \t"))
622 ;; If it's a continuation line, or a new PS1 prompt,
623 ;; then copy the margin.
624 (when (or new-indent (not line-had-prompt))
625 (beginning-of-line)
626 (delete-backward-char (skip-chars-forward " \t"))
627 (insert-char ?\ new-margin))
628 ;; Add the new prompt.
629 (insert-string (if new-indent "... " ">>> "))
630 ;; Add the new indentation
631 (if new-indent (insert-char ?\ new-indent))
632 (setq indent-end (point))))
633 ;; If we're left of the indentation end, then move up to the
634 ;; indentation end.
635 (if (< (point) indent-end) (goto-char indent-end))))
636
637 (defun doctest-current-source-line-indentation (&optional dedent-only)
638 "Return the post-prompt indent to use for this line. This is an
639 integer for a continuation lines, and nil for non-continuation lines."
640 (save-excursion
641 ;; Examine the previous doctest line (if present).
642 (let* ((prev-stmt-info (doctest-prev-statement-info))
643 (prev-stmt-indent (nth 0 prev-stmt-info))
644 (continuation-indent (nth 1 prev-stmt-info))
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 )
649 ;; Examine this doctest line.
650 (let* ((curr-line-indent 0)
651 (curr-line-outdented nil))
652 (beginning-of-line)
653 (when (looking-at doctest-prompt-re)
654 (setq curr-line-indent (- (match-end 3) (match-beginning 3)))
655 (goto-char (match-end 3)))
656 (setq curr-line-outdented (and (looking-at doctest-outdent-re)
657 (not prev-stmt-blocks-outdent)))
658 ;; Compute the overall indent.
659 (let ((indent (or continuation-indent
660 (+ prev-stmt-indent
661 (if curr-line-outdented -4 0)
662 (if prev-stmt-opens-block 4 0)
663 (if prev-stmt-closes-block -4 0)))))
664 ;; If dedent-only is true, then make sure we don't indent.
665 (when dedent-only
666 (setq indent (min indent curr-line-indent)))
667 ;; If indent=0 and we're not outdented, then set indent to
668 ;; nil (to signify the start of a new source example).
669 (when (and (= indent 0)
670 (not (or curr-line-outdented continuation-indent)))
671 (setq indent nil))
672 ;; Return the indentation.
673 indent)))))
674
675 (defun doctest-prev-statement-info (&optional debug)
676 (save-excursion
677 (forward-line -1)
678 (doctest-statement-info debug)))
679
680 (defun doctest-statement-info (&optional debug)
681 "Collect info about the previous statement, and return it as a list:
682
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)))))))
787
788 (defun doctest-current-source-line-margin ()
789 "Return the pre-prompt margin to use for this source line. This is
790 copied from the most recent source line, or set to
791 `doctest-default-margin' if there are no preceeding source lines."
792 (save-excursion
793 (save-restriction
794 (when (doctest-in-mmm-docstring-overlay)
795 (doctest-narrow-to-mmm-overlay))
796 (beginning-of-line)
797 (forward-line -1)
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))))
806
807 (defun doctest-electric-backspace ()
808 "Delete the preceeding character, level of indentation, or
809 prompt.
810
811 If point is at the leftmost column, delete the preceding newline.
812
813 Otherwise, if point is at the first non-whitespace character
814 following an indented source line's prompt, then reduce the
815 indentation to the next multiple of 4; and update the source line's
816 prompt, when necessary.
817
818 Otherwise, if point is at the first non-whitespace character
819 following an unindented source line's prompt, then remove the
820 prompt (converting the line to an output line or text line).
821
822 Otherwise, if point is at the first non-whitespace character of a
823 line, the delete the line's indentation.
824
825 Otherwise, delete the preceeding character.
826 "
827 (interactive "*")
828 (cond
829 ;; Beginning of line: delete preceeding newline.
830 ((bolp) (backward-delete-char 1))
831
832 ;; First non-ws char following prompt: dedent or remove prompt.
833 ((and (looking-at "[^ \t\n]\\|$") (doctest-looking-back doctest-prompt-re))
834 (let* ((prompt-beg (match-beginning 2))
835 (indent-beg (match-beginning 3)) (indent-end (match-end 3))
836 (old-indent (- indent-end indent-beg))
837 (new-indent (* (/ (- old-indent 1) 4) 4)))
838 (cond
839 ;; Indented source line: dedent it.
840 ((> old-indent 0)
841 (goto-char indent-beg)
842 (delete-region indent-beg indent-end)
843 (insert-char ?\ new-indent)
844 ;; Change prompt to PS1, when appropriate.
845 (when (and (= new-indent 0) (not (looking-at doctest-outdent-re)))
846 (delete-backward-char 4)
847 (insert-string ">>> ")))
848 ;; Non-indented source line: remove prompt.
849 (t
850 (goto-char indent-end)
851 (delete-region prompt-beg indent-end)))))
852
853 ;; First non-ws char of a line: delete all indentation.
854 ((and (looking-at "[^ \n\t]\\|$") (doctest-looking-back "^[ \t]+"))
855 (delete-region (match-beginning 0) (match-end 0)))
856
857 ;; Otherwise: delete a character.
858 (t
859 (backward-delete-char 1))))
860
861 (defun doctest-newline-and-indent ()
862 "Insert a newline, and indent the new line appropriately.
863
864 If the current line is a source line containing a bare prompt,
865 then clear the current line, and insert a newline.
866
867 Otherwise, if the current line is a source line, then insert a
868 newline, and add an appropriately indented prompt to the new
869 line.
870
871 Otherwise, if the current line is an output line, then insert a
872 newline and indent the new line to match the example's margin.
873
874 Otherwise, insert a newline.
875
876 If `doctest-avoid-trailing-whitespace' is true, then clear any
877 whitespace to the left of the point before inserting a newline.
878 "
879 (interactive "*")
880 ;; If we're avoiding trailing spaces, then delete WS before point.
881 (if doctest-avoid-trailing-whitespace
882 (delete-char (- (skip-chars-backward " \t"))))
883 (cond
884 ;; If we're on an empty prompt, delete it.
885 ((doctest-on-empty-source-line-p)
886 (delete-region (match-beginning 0) (match-end 0))
887 (insert-char ?\n 1))
888 ;; If we're on a doctest line, add a new prompt.
889 ((doctest-on-source-line-p)
890 (insert-char ?\n 1)
891 (doctest-indent-source-line))
892 ;; If we're in doctest output, indent to the margin.
893 ((doctest-on-output-line-p)
894 (insert-char ?\n 1)
895 (insert-char ?\ (doctest-current-source-line-margin)))
896 ;; Otherwise, just add a newline.
897 (t (insert-char ?\n 1))))
898
899 (defun doctest-electric-colon ()
900 "Insert a colon, and dedent the line when appropriate."
901 (interactive "*")
902 (insert-char ?: 1)
903 (when (doctest-on-source-line-p)
904 (doctest-indent-source-line t)))
905
906 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
907 ;;; Code Execution
908 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
909
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
930 (defun doctest-execute-buffer ()
931 "Run doctest on the current buffer, and display the results in the
932 *doctest-output* buffer."
933 (interactive)
934 (doctest-execute-region (point-min) (point-max) nil nil))
935
936 (defun doctest-execute-region (start end &optional diff
937 check-for-mmm-docstring-overlay)
938 "Run doctest on the current buffer, and display the results in the
939 *doctest-output* buffer."
940 (interactive "r")
941 ;; If it's already running, give the user a chance to restart it.
942 (when (doctest-process-live-p doctest-async-process)
943 (when (y-or-n-p "Doctest is already running. Restart it? ")
944 (doctest-cancel-async-process)
945 (message "Killing doctest...")))
946 (cond
947 ((and doctest-async (doctest-process-live-p doctest-async-process))
948 (message "Can't run two doctest processes at once!"))
949 (t
950 (let* ((results-buf-name (doctest-results-buffer-name))
951 (in-docstring (and check-for-mmm-docstring-overlay
952 (doctest-in-mmm-docstring-overlay)))
953 (temp (doctest-temp-name)) (dir doctest-temp-directory)
954 (input-file (expand-file-name (concat temp ".py") dir))
955 (globs-file (when in-docstring
956 (expand-file-name (concat temp "-globs.py") dir)))
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))
1147 (save-excursion
1148 (set-buffer doctest-results-buffer)
1149 (goto-char (point-max))
1150 (while (re-search-backward (doctest-results-loc-re) nil t)
1151 (let ((lineno (string-to-int (match-string 1))))
1152 (when (equal doctest-results-py-version 'py21)
1153 (setq lineno (+ lineno 1)))
1154 (while (and markers (< lineno (cdar markers)))
1155 (set-marker (caar markers) nil)
1156 (setq markers (cdr markers)))
1157 (if (and markers (= lineno (cdar markers)))
1158 (push (pop markers) filtered)
1159 (doctest-warn "Example expected on line %d but not found %s"
1160 lineno markers)))))
1161 (dolist (marker-info markers)
1162 (set-marker (car marker-info) nil))
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1186
1187 (defun doctest-next-failure (count)
1188 "Move to the top of the next failing example, and highlight the
1189 example's failure description in *doctest-output*."
1190 (interactive "p")
1191 (cond
1192 ((and doctest-async (doctest-process-live-p doctest-async-process))
1193 (message "Wait for doctest to finish running!"))
1194 ((not (doctest-results-buffer-valid-p))
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)))
1201 (save-excursion
1202 (set-buffer doctest-results-buffer)
1203 ;; Pick up where we left off.
1204 ;; (nb: doctest-selected-failure is buffer-local)
1205 (goto-char (or doctest-selected-failure (point-min)))
1206 ;; Skip past anything on *this* line.
1207 (if (>= count 0) (end-of-line) (beginning-of-line))
1208 ;; Look for the next failure
1209 (when
1210 (if (>= count 0)
1211 (re-search-forward (doctest-results-loc-re) nil t count)
1212 (re-search-backward (doctest-results-loc-re) nil t (- count)))
1213 ;; We found a failure:
1214 (let ((old-selected-failure doctest-selected-failure))
1215 (beginning-of-line)
1216 ;; Extract the line number for the doctest file.
1217 (let ((orig-lineno (string-to-int (match-string 1))))
1218 (when (equal doctest-results-py-version 'py21)
1219 (setq orig-lineno (+ orig-lineno 1)))
1220 (dolist (marker-info example-markers)
1221 (when (= orig-lineno (cdr marker-info))
1222 (setq marker (car marker-info)))))
1223
1224 ;; Update the window cursor.
1225 (beginning-of-line)
1226 (set-window-point results-window (point))
1227 ;; Store our position for next time.
1228 (setq doctest-selected-failure (point))
1229 ;; Update selection.
1230 (doctest-fontify-line old-selected-failure)
1231 (doctest-fontify-line doctest-selected-failure))))
1232
1233 (cond
1234 ;; We found a failure -- move point to the selected failure.
1235 (marker
1236 (goto-char (marker-position marker))
1237 (beginning-of-line))
1238 ;; We didn't find a failure, but there is one -- wrap.
1239 ((> (length doctest-example-markers) 0)
1240 (if (>= count 0) (doctest-first-failure) (doctest-last-failure)))
1241 ;; We didn't find a failure -- alert the user.
1242 (t (message "No failures found!")))))))
1243
1244 (defun doctest-prev-failure (count)
1245 "Move to the top of the previous failing example, and highlight
1246 the example's failure description in *doctest-output*."
1247 (interactive "p")
1248 (doctest-next-failure (- count)))
1249
1250 (defun doctest-first-failure ()
1251 "Move to the top of the first failing example, and highlight
1252 the example's failure description in *doctest-output*."
1253 (interactive)
1254 (if (buffer-live-p doctest-results-buffer)
1255 (save-excursion
1256 (set-buffer doctest-results-buffer)
1257 (let ((old-selected-failure doctest-selected-failure))
1258 (setq doctest-selected-failure (point-min))
1259 (doctest-fontify-line old-selected-failure))))
1260 (doctest-next-failure 1))
1261
1262 (defun doctest-last-failure ()
1263 "Move to the top of the last failing example, and highlight
1264 the example's failure description in *doctest-output*."
1265 (interactive)
1266 (if (buffer-live-p doctest-results-buffer)
1267 (save-excursion
1268 (set-buffer doctest-results-buffer)
1269 (let ((old-selected-failure doctest-selected-failure))
1270 (setq doctest-selected-failure (point-max))
1271 (doctest-fontify-line old-selected-failure))))
1272 (doctest-next-failure -1))
1273
1274 (defun doctest-select-failure ()
1275 "Move to the top of the currently selected example, and select that
1276 example in the source buffer. Intended for use in the results
1277 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 ()
1582 (save-excursion
1583 (let ((pos (car (doctest-mmm-overlay-bounds))))
1584 (goto-char pos)
1585 (when (doctest-looking-back "\"\"\"\\|\'\'\'")
1586 (setq pos (- pos 3)))
1587 (beginning-of-line)
1588 (- pos (point)))))
1589
1590 (defun doctest-mmm-overlay-bounds ()
1591 (when (featurep 'mmm-auto)
1592 (let ((overlay (mmm-overlay-at (point))))
1593 (when overlay
1594 (let ((start (overlay-start overlay))
1595 (end (overlay-end overlay)))
1596 (when (doctest-in-mmm-docstring-overlay)
1597 (save-excursion
1598 (goto-char start)
1599 (re-search-forward "[\"\']*")
1600 (setq start (point))
1601 (goto-char end)
1602 (while (doctest-looking-back "[\"\']")
1603 (backward-char 1))
1604 (setq end (point))))
1605 (cons start end))))))
1606
1607 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1608 ;;; Helper functions
1609 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1610
1611 (defun doctest-on-source-line-p (&optional prompt)
1612 "Return true if the current line is a source line. The optional
1613 argument prompt can be used to specify which type of source
1614 line (... or >>>)."
1615 (save-excursion
1616 (beginning-of-line)
1617 ;; Check if we're looking at a prompt (of the right type).
1618 (when (and (looking-at doctest-prompt-re)
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 ()
1628 "Return true if the current line contains a bare prompt."
1629 (save-excursion
1630 (beginning-of-line)
1631 (and (doctest-on-source-line-p)
1632 (looking-at (concat doctest-prompt-re "$")))))
1633
1634 (defun doctest-on-output-line-p ()
1635 "Return true if the current line is an output line."
1636 (save-excursion
1637 (beginning-of-line)
1638 ;; The line must not be blank or a source line.
1639 (when (not (or (doctest-on-source-line-p) (looking-at "[ \t]*$")))
1640 ;; The line must follow a source line, with no intervening blank
1641 ;; lines.
1642 (while (not (or (doctest-on-source-line-p) (looking-at "[ \t]*$")
1643 (= (point) (point-min))))
1644 (forward-line -1))
1645 (doctest-on-source-line-p))))
1646
1647 (defun doctest-find-output-line (&optional limit)
1648 "Move forward to the next doctest output line (staying within
1649 the given bounds). Return the character position of the doctest
1650 output line if one was found, and false otherwise."
1651 (let ((found-it nil) ; point where we found an output line
1652 (limit (or limit (point-max)))) ; default value for limit
1653 (save-excursion
1654 ;; Keep moving forward, one line at a time, until we find a
1655 ;; doctest output line.
1656 (while (and (not found-it) (< (point) limit) (not (eobp)))
1657 (if (and (not (eolp)) (doctest-on-output-line-p))
1658 (setq found-it (point))
1659 (forward-line))))
1660 ;; If we found a doctest output line, then go to it.
1661 (if found-it (goto-char found-it))))
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
1746 (defun doctest-version ()
1747 "Echo the current version of `doctest-mode' in the minibuffer."
1748 (interactive)
1749 (message "Using `doctest-mode' version %s" doctest-version))
1750
1751 (defun doctest-warn (msg &rest args)
1752 "Display a doctest warning message."
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)))
1762
1763 (defvar doctest-serial-number 0) ;used if broken-temp-names.
1764 (defun doctest-temp-name ()
1765 "Return a new temporary filename, for use in calling doctest."
1766 (if (memq 'broken-temp-names features)
1767 (let
1768 ((sn doctest-serial-number)
1769 (pid (and (fboundp 'emacs-pid) (emacs-pid))))
1770 (setq doctest-serial-number (1+ doctest-serial-number))
1771 (if pid
1772 (format "doctest-%d-%d" sn pid)
1773 (format "doctest-%d" sn)))
1774 (make-temp-name "doctest-")))
1775
1776 (defun doctest-fontify-line (charpos)
1777 "Run font-lock-fontify-region on the line containing the given
1778 position."
1779 (if (and charpos (functionp 'font-lock-fontify-region))
1780 (save-excursion
1781 (goto-char charpos)
1782 (let ((beg (progn (beginning-of-line) (point)))
1783 (end (progn (end-of-line) (point))))
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)
1909
1910 ;; Declare local variables.
1911 (kill-all-local-variables)
1912 (make-local-variable 'font-lock-defaults)
1913 (make-local-variable 'doctest-selected-failure)
1914 (make-local-variable 'doctest-source-buffer)
1915 (make-local-variable 'doctest-results-py-version)
1916
1917 ;; Define local variables.
1918 (setq major-mode 'doctest-results-mode
1919 mode-name "Doctest-Results"
1920 mode-line-process 'doctest-mode-line-process
1921 font-lock-defaults '(doctest-results-font-lock-keywords
1922 nil nil nil nil))
1923 ;; Define keymap.
1924 (use-local-map doctest-results-mode-map)
1925
1926 ;; Define the syntax table.
1927 (set-syntax-table doctest-results-mode-syntax-table)
1928
1929 ;; Enable font-lock mode.
1930 (if (featurep 'font-lock) (font-lock-mode 1)))
1931
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.
1972 (defconst doctest-mode-map
1973 (let ((map (make-keymap)))
1974 (define-key map [backspace] 'doctest-electric-backspace)
1975 (define-key map [return] 'doctest-newline-and-indent)
1976 (define-key map [tab] 'doctest-indent-source-line)
1977 (define-key map ":" 'doctest-electric-colon)
1978 (define-key map "\C-c\C-v" 'doctest-version)
1979 (define-key map "\C-c\C-c" 'doctest-execute)
1980 (define-key map "\C-c\C-d" 'doctest-execute-with-diff)
1981 (define-key map "\C-c\C-n" 'doctest-next-failure)
1982 (define-key map "\C-c\C-p" 'doctest-prev-failure)
1983 (define-key map "\C-c\C-a" 'doctest-first-failure)
1984 (define-key map "\C-c\C-e" 'doctest-last-failure)
1985 (define-key map "\C-c\C-z" 'doctest-last-failure)
1986 (define-key map "\C-c\C-r" 'doctest-replace-output)
1987 (define-key map "\C-c|" 'doctest-execute-region)
1988 map)
1989 "Keymap for doctest-mode.")
1990
1991 ;; Syntax table for doctest-mode.
1992 (defvar doctest-mode-syntax-table nil
1993 "Syntax table used in `doctest-mode' buffers.")
1994 (when (not doctest-mode-syntax-table)
1995 (setq doctest-mode-syntax-table (make-syntax-table))
1996 (dolist (entry '(("(" . "()") ("[" . "(]") ("{" . "(}")
1997 (")" . ")(") ("]" . ")[") ("}" . "){")
1998 ("$%&*+-/<=>|'\"`" . ".") ("_" . "w")))
1999 (dolist (char (string-to-list (car entry)))
2000 (modify-syntax-entry char (cdr entry) doctest-mode-syntax-table))))
2001
2002 ;; Use doctest mode for files ending in .doctest
2003 ;;;###autoload
2004 (add-to-list 'auto-mode-alist '("\\.doctest$" . doctest-mode))
2005
2006 ;;;###autoload
2007 (defun doctest-mode ()
2008 "A major mode for editing text files that contain Python
2009 doctest examples. Doctest is a testing framework for Python that
2010 emulates an interactive session, and checks the result of each
2011 command. For more information, see the Python library reference:
2012 <http://docs.python.org/lib/module-doctest.html>
2013
2014 `doctest-mode' defines three kinds of line, each of which is
2015 treated differently:
2016
2017 - 'Source lines' are lines consisting of a Python prompt
2018 ('>>>' or '...'), followed by source code. Source lines are
2019 colored (similarly to `python-mode') and auto-indented.
2020
2021 - 'Output lines' are non-blank lines immediately following
2022 source lines. They are colored using several doctest-
2023 specific output faces.
2024
2025 - 'Text lines' are any other lines. They are not processed in
2026 any special way.
2027
2028 \\{doctest-mode-map}"
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
2050 ;; Enable auto-fill mode.
2051 (auto-fill-mode 1)
2052 (setq auto-fill-function 'doctest-do-auto-fill)
2053
2054 ;; Enable font-lock mode.
2055 (if (featurep 'font-lock) (font-lock-mode 1))
2056
2057 ;; Run the mode hook.
2058 (run-hooks 'doctest-mode-hook))
2059
2060 (provide 'doctest-mode)
2061 ;;; doctest-mode.el ends here