[svn] add first part of jinja documentation

--HG--
branch : trunk
diff --git a/docs/generate.py b/docs/generate.py
new file mode 100755
index 0000000..0c155c0
--- /dev/null
+++ b/docs/generate.py
@@ -0,0 +1,275 @@
+#!/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()
+
+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>\
+''')
+
+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):
+    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
+    result.write(TEMPLATE.render(c).encode('utf-8'))
+    result.close()
+
+
+def run(dst, sources=()):
+    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)
+        finally:
+            f.close()
+
+
+def main(dst='build/', *sources):
+    return run(os.path.realpath(dst), sources)
+
+
+if __name__ == '__main__':
+    main(*sys.argv[1:])