blob: a6dbb4e432ce863ea232d166bb56a3b415b67e1d [file] [log] [blame]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Generate Jinja Documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Generates a bunch of html files containing the documentation.
:copyright: 2006-2007 by Armin Ronacher, Georg Brandl.
:license: BSD, see LICENSE for more details.
"""
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
import re
import inspect
from datetime import datetime
from cgi import escape
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.core import publish_parts
from docutils.writers import html4css1
from jinja import Environment
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
def generate_list_of_filters():
from jinja.filters import FILTERS
result = []
filters = {}
for name, f in FILTERS.iteritems():
if not f in filters:
filters[f] = ([name], inspect.getdoc(f))
else:
filters[f][0].append(name)
for names, _ in filters.itervalues():
names.sort(key=lambda x: -len(x))
for names, doc in sorted(filters.values(), key=lambda x: x[0][0].lower()):
name = names[0]
if len(names) > 1:
aliases = '\n\n :Aliases: %s\n' % ', '.join(names[1:])
else:
aliases = ''
doclines = []
for line in doc.splitlines():
doclines.append(' ' + line)
doc = '\n'.join(doclines)
result.append('`%s`\n%s%s' % (name, doc, aliases))
return '\n'.join(result)
def generate_list_of_tests():
from jinja.tests import TESTS
result = []
tests = {}
for name, f in TESTS.iteritems():
if not f in tests:
tests[f] = ([name], inspect.getdoc(f))
else:
tests[f][0].append(name)
for names, _ in tests.itervalues():
names.sort(key=lambda x: -len(x))
for names, doc in sorted(tests.values(), key=lambda x: x[0][0].lower()):
name = names[0]
if len(names) > 1:
aliases = '\n\n :Aliases: %s\n' % ', '.join(names[1:])
else:
aliases = ''
doclines = []
for line in doc.splitlines():
doclines.append(' ' + line)
doc = '\n'.join(doclines)
result.append('`%s`\n%s%s' % (name, doc, aliases))
return '\n'.join(result)
def generate_list_of_loaders():
from jinja import loaders as loader_module
result = []
loaders = []
for item in loader_module.__all__:
loaders.append(getattr(loader_module, item))
loaders.sort(key=lambda x: x.__name__.lower())
for loader in loaders:
doclines = []
for line in inspect.getdoc(loader).splitlines():
doclines.append(' ' + line)
result.append('`%s`\n%s' % (loader.__name__, '\n'.join(doclines)))
return '\n\n'.join(result)
e = Environment()
PYGMENTS_FORMATTER = HtmlFormatter(style='pastie', cssclass='syntax')
LIST_OF_FILTERS = generate_list_of_filters()
LIST_OF_TESTS = generate_list_of_tests()
LIST_OF_LOADERS = generate_list_of_loaders()
FULL_TEMPLATE = e.from_string('''\
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>{{ title }} &mdash; Jinja Documentation</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="style.css" type="text/css">
<style type="text/css">
{{ style|e }}
</style>
</head>
<body>
<div id="content">
{% if file_id == 'index' %}
<div id="jinjalogo"></div>
<h2 class="subheading plain">{{ title }}</h2>
{% else %}
<h1 class="heading"><span>Jinja</span></h1>
<h2 class="subheading">{{ title }}</h2>
{% endif %}
{% if file_id != 'index' or toc %}
<div id="toc">
<h2>Navigation</h2>
<ul>
<li><a href="index.html">back to index</a></li>
</ul>
{% if toc %}
<h2>Contents</h2>
<ul class="contents">
{% for key, value in toc %}
<li><a href="{{ key }}">{{ value }}</a></li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endif %}
<div id="contentwrapper">
{{ body }}
</div>
</div>
</body>
<!-- generated on: {{ generation_date }}
file id: {{ file_id }} -->
</html>\
''')
PREPROC_TEMPLATE = e.from_string('''\
<!-- TITLE -->{{ title }}<!-- ENDTITLE -->
<!-- TOC -->{% for key, value in toc %}<li><a href="{{
key }}">{{ value }}</a></li>{% endfor %}<!-- ENDTOC -->
<!-- BODY -->{{ body }}<!-- ENDBODY -->\
''')
def pygments_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
try:
lexer = get_lexer_by_name(arguments[0])
except ValueError:
# no lexer found
lexer = get_lexer_by_name('text')
parsed = highlight(u'\n'.join(content), lexer, PYGMENTS_FORMATTER)
return [nodes.raw('', parsed, format="html")]
pygments_directive.arguments = (1, 0, 1)
pygments_directive.content = 1
directives.register_directive('sourcecode', pygments_directive)
def create_translator(link_style):
class Translator(html4css1.HTMLTranslator):
def visit_reference(self, node):
refuri = node.get('refuri')
if refuri is not None and '/' not in refuri and refuri.endswith('.txt'):
node['refuri'] = link_style(refuri[:-4])
html4css1.HTMLTranslator.visit_reference(self, node)
return Translator
class DocumentationWriter(html4css1.Writer):
def __init__(self, link_style):
html4css1.Writer.__init__(self)
self.translator_class = create_translator(link_style)
def translate(self):
html4css1.Writer.translate(self)
# generate table of contents
contents = self.build_contents(self.document)
contents_doc = self.document.copy()
contents_doc.children = contents
contents_visitor = self.translator_class(contents_doc)
contents_doc.walkabout(contents_visitor)
self.parts['toc'] = self._generated_toc
def build_contents(self, node, level=0):
sections = []
i = len(node) - 1
while i >= 0 and isinstance(node[i], nodes.section):
sections.append(node[i])
i -= 1
sections.reverse()
toc = []
for section in sections:
try:
reference = nodes.reference('', '', refid=section['ids'][0], *section[0])
except IndexError:
continue
ref_id = reference['refid']
text = escape(reference.astext().encode('utf-8'))
toc.append((ref_id, text))
self._generated_toc = [('#%s' % href, caption) for href, caption in toc]
# no further processing
return []
def generate_documentation(data, link_style):
writer = DocumentationWriter(link_style)
data = data.replace('[[list_of_filters]]', LIST_OF_FILTERS)\
.replace('[[list_of_tests]]', LIST_OF_TESTS)\
.replace('[[list_of_loaders]]', LIST_OF_LOADERS)
parts = publish_parts(
data,
writer=writer,
settings_overrides={
'initial_header_level': 3,
'field_name_limit': 50,
}
)
return {
'title': parts['title'].encode('utf-8'),
'body': parts['body'].encode('utf-8'),
'toc': parts['toc']
}
def handle_file(filename, fp, dst, preproc):
now = datetime.now()
title = os.path.basename(filename)[:-4]
content = fp.read()
parts = generate_documentation(content, (lambda x: './%s.html' % x))
result = file(os.path.join(dst, title + '.html'), 'w')
c = dict(parts)
c['style'] = PYGMENTS_FORMATTER.get_style_defs('.syntax')
c['generation_date'] = now
c['file_id'] = title
if preproc:
tmpl = PREPROC_TEMPLATE
else:
tmpl = FULL_TEMPLATE
result.write(tmpl.render(c).encode('utf-8'))
result.close()
def run(dst, preproc, sources=(), handle_file=handle_file):
path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))
if not sources:
sources = [os.path.join(path, fn) for fn in os.listdir(path)]
for fn in sources:
if not os.path.isfile(fn):
continue
print 'Processing %s' % fn
f = open(fn)
try:
handle_file(fn, f, dst, preproc)
finally:
f.close()
def main(dst='build/', preproc=False, *sources):
run(os.path.realpath(dst), str(preproc).lower() == 'true', sources)
if __name__ == '__main__':
main(*sys.argv[1:])