blob: bd4cab644a9bd52402836fd31e1cea6ae005a37e [file] [log] [blame]
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +02001# -*- coding: utf-8 -*-
2"""
3 Jinja Documentation Extensions
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Support for automatically documenting filters and tests.
7
8 :copyright: Copyright 2008 by Armin Ronacher.
9 :license: BSD.
10"""
Armin Ronacher157531b2008-04-28 16:14:03 +020011import re
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +020012import inspect
Armin Ronacher157531b2008-04-28 16:14:03 +020013from types import BuiltinFunctionType
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +020014from docutils import nodes
15from docutils.statemachine import ViewList
16from sphinx.ext.autodoc import prepare_docstring
17
18
Armin Ronacher157531b2008-04-28 16:14:03 +020019from pygments.style import Style
20from pygments.token import Keyword, Name, Comment, String, Error, \
21 Number, Operator, Generic
22
23
24class JinjaStyle(Style):
25 title = 'Jinja Style'
26 default_style = ""
27 styles = {
28 Comment: 'italic #aaaaaa',
29 Comment.Preproc: 'noitalic #B11414',
30 Comment.Special: 'italic #505050',
31
32 Keyword: 'bold #B80000',
33 Keyword.Type: '#808080',
34
35 Operator.Word: '#333333',
36
37 Name.Builtin: '#333333',
38 Name.Function: '#333333',
39 Name.Class: 'bold #333333',
40 Name.Namespace: 'bold #333333',
41 Name.Entity: 'bold #363636',
42 Name.Attribute: '#686868',
43 Name.Tag: 'bold #686868',
44 Name.Decorator: '#686868',
45
46 String: '#BE9B5D',
47 Number: '#FF0000',
48
49 Generic.Heading: 'bold #000080',
50 Generic.Subheading: 'bold #800080',
51 Generic.Deleted: '#aa0000',
52 Generic.Inserted: '#00aa00',
53 Generic.Error: '#aa0000',
54 Generic.Emph: 'italic',
55 Generic.Strong: 'bold',
56 Generic.Prompt: '#555555',
57 Generic.Output: '#888888',
58 Generic.Traceback: '#aa0000',
59
60 Error: '#F00 bg:#FAA'
61 }
62
63_sig_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*(\(.*?\))')
64
65
66def format_function(name, aliases, func):
67 lines = inspect.getdoc(func).splitlines()
68 signature = '()'
69 if isinstance(func, BuiltinFunctionType):
70 match = _sig_re.match(lines[0])
71 if match is not None:
72 del lines[:1 + bool(lines and not lines[0])]
73 signature = match.group(1)
74 else:
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +020075 try:
Armin Ronacher157531b2008-04-28 16:14:03 +020076 argspec = inspect.getargspec(func)
77 if getattr(func, 'environmentfilter', False) or \
78 getattr(func, 'contextfilter', False):
79 del argspec[0][0]
80 signature = inspect.formatargspec(*argspec)
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +020081 except:
Armin Ronacher157531b2008-04-28 16:14:03 +020082 pass
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +020083 result = ['.. function:: %s%s' % (name, signature), '']
Armin Ronacher157531b2008-04-28 16:14:03 +020084 result.extend(' ' + line for line in lines)
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +020085 if aliases:
86 result.extend(('', ' :aliases: %s' % ', '.join(
87 '``%s``' % x for x in sorted(aliases))))
88 return result
89
90
Armin Ronacher157531b2008-04-28 16:14:03 +020091def dump_functions(mapping):
92 def directive(dirname, arguments, options, content, lineno,
93 content_offset, block_text, state, state_machine):
94 reverse_mapping = {}
95 for name, func in mapping.iteritems():
96 reverse_mapping.setdefault(func, []).append(name)
97 filters = []
98 for func, names in reverse_mapping.iteritems():
99 aliases = sorted(names, key=lambda x: len(x))
100 name = aliases.pop()
101 filters.append((name, aliases, func))
102 filters.sort()
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +0200103
Armin Ronacher157531b2008-04-28 16:14:03 +0200104 result = ViewList()
105 for name, aliases, func in filters:
106 for item in format_function(name, aliases, func):
107 result.append(item, '<jinjaext>')
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +0200108
Armin Ronacher157531b2008-04-28 16:14:03 +0200109 node = nodes.paragraph()
110 state.nested_parse(result, content_offset, node)
111 return node.children
112 return directive
113
114
115from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS
116jinja_filters = dump_functions(DEFAULT_FILTERS)
117jinja_tests = dump_functions(DEFAULT_TESTS)
Armin Ronacher3c8b7ad2008-04-28 13:52:21 +0200118
119
120def setup(app):
Armin Ronacher157531b2008-04-28 16:14:03 +0200121 app.add_directive('jinjafilters', jinja_filters, 0, (0, 0, 0))
122 app.add_directive('jinjatests', jinja_tests, 0, (0, 0, 0))