Mercurial > dotfiles
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 |