Code

ModelMiddleware: toc_builder.py

File toc_builder.py, 5.2 KB (added by Gevara, 8 years ago)

Needed for the ReSTMiddleware example.

Line 
1"""
2Mostly stolen from www.djangoproject.com, with a slight change that involves putting
3<a>'s on headings back into the mix - TOC didn't want to work without them for me.
4"""
5
6from docutils import nodes, utils
7from docutils.core import publish_parts
8from docutils.writers import html4css1
9
10
11def build_document(input_string, source_path=None, destination_path=None,
12                    input_encoding='unicode', doctitle=1, initial_header_level=1):
13    overrides = {'input_encoding': input_encoding,
14                 'doctitle_xform': doctitle,
15                 'initial_header_level': initial_header_level}
16    writer = DjangoHTMLWriter()
17    parts = publish_parts(
18        input_string,
19        writer=writer,
20        settings_overrides=overrides,
21        )
22    return {'html_body' : parts['html_body'], 'toc' : parts['toc']}
23
24
25
26class DjangoHTMLWriter(html4css1.Writer):
27    def __init__(self):
28        html4css1.Writer.__init__(self)
29        self.translator_class = DjangoHTMLTranslator
30
31    def translate(self):
32        # build the document
33        html4css1.Writer.translate(self)
34
35        # build the contents
36        contents = self.build_contents(self.document)
37        contents_doc = self.document.copy()
38        contents_doc.children = contents
39        contents_visitor = self.translator_class(contents_doc)
40        contents_doc.walkabout(contents_visitor)
41        self.parts['toc'] = "<ul class='toc'>%s</ul>" % ''.join(contents_visitor.fragment)
42
43    def build_contents(self, node, level=0):
44        level += 1
45        sections = []
46        i = len(node) - 1
47        while i >= 0 and isinstance(node[i], nodes.section):
48            sections.append(node[i])
49            i -= 1
50        sections.reverse()
51        entries = []
52        autonum = 0
53        depth = 4   # XXX FIXME
54        for section in sections:
55            title = section[0]
56            entrytext = title
57            try:
58                reference = nodes.reference('', '', refid=section['ids'][0], *entrytext)
59            except IndexError:
60                continue
61            ref_id = self.document.set_id(reference)
62            entry = nodes.paragraph('', '', reference)
63            item = nodes.list_item('', entry)
64            if level < depth:
65                subsects = self.build_contents(section, level)
66                item += subsects
67            entries.append(item)
68        if entries:
69            contents = nodes.bullet_list('', *entries)
70            return contents
71        else:
72            return []
73
74class DjangoHTMLTranslator(html4css1.HTMLTranslator):
75    def visit_table(self, node):
76        """Remove the damn border=1 from the standard HTML writer"""
77        self.body.append(self.starttag(node, 'table', CLASS='docutils'))
78
79    def visit_title(self, node, move_ids=1):
80        """Only 6 section levels are supported by HTML."""
81        check_id = 0
82        close_tag = '</p>\n'
83        if isinstance(node.parent, nodes.topic):
84            self.body.append(
85                  self.starttag(node, 'p', '', CLASS='topic-title first'))
86            check_id = 1
87        elif isinstance(node.parent, nodes.sidebar):
88            self.body.append(
89                  self.starttag(node, 'p', '', CLASS='sidebar-title'))
90            check_id = 1
91        elif isinstance(node.parent, nodes.Admonition):
92            self.body.append(
93                  self.starttag(node, 'p', '', CLASS='admonition-title'))
94            check_id = 1
95        elif isinstance(node.parent, nodes.table):
96            self.body.append(
97                  self.starttag(node, 'caption', ''))
98            check_id = 1
99            close_tag = '</caption>\n'
100        elif isinstance(node.parent, nodes.document):
101            self.body.append(self.starttag(node, 'h1', '', CLASS='title'))
102            self.context.append('</h1>\n')
103            self.in_document_title = len(self.body)
104        else:
105            assert isinstance(node.parent, nodes.section)
106            h_level = self.section_level + self.initial_header_level - 1
107            atts = {}
108            if (len(node.parent) >= 2 and
109                isinstance(node.parent[1], nodes.subtitle)):
110                atts['CLASS'] = 'with-subtitle'
111            self.body.append(
112                  self.starttag(node, 'h%s' % h_level, '', **atts))
113            atts = {}
114            # !!! conditional to be removed in Docutils 0.5:
115            if move_ids:
116                if node.parent['ids']:
117                    atts['ids'] = node.parent['ids']
118            if node.hasattr('refid'):
119                atts['class'] = 'toc-backref'
120                atts['href'] = '#' + node['refid']
121            if atts:
122                self.body.append(self.starttag({}, 'a', '', **atts))
123                self.context.append('</a></h%s>\n' % (h_level))
124            else:
125                self.context.append('</h%s>\n' % (h_level))
126        # !!! conditional to be removed in Docutils 0.5:
127        if check_id:
128            if node.parent['ids']:
129                atts={'ids': node.parent['ids']}
130                self.body.append(
131                    self.starttag({}, 'a', '', **atts))
132                self.context.append('</a>' + close_tag)
133            else:
134                self.context.append(close_tag)
135