1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
# -*- coding: utf-8 -*-
"""
sphinx.writer
~~~~~~~~~~~~~
docutils writers handling Sphinx' custom nodes.
:copyright: 2007 by Georg Brandl.
:license: Python license.
"""
from docutils import nodes
from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator
from .smartypants import sphinx_smarty_pants
class HTMLWriter(Writer):
def __init__(self, config):
Writer.__init__(self)
if config.get('use_smartypants', False):
self.translator_class = SmartyPantsHTMLTranslator
else:
self.translator_class = HTMLTranslator
version_text = {
'deprecated': 'Deprecated in version %s',
'versionchanged': 'Changed in version %s',
'versionadded': 'New in version %s',
}
class HTMLTranslator(BaseTranslator):
"""
Our custom HTML translator.
"""
def __init__(self, *args, **kwds):
self.no_smarty = 0
BaseTranslator.__init__(self, *args, **kwds)
self.highlightlang = 'python'
def visit_desc(self, node):
self.body.append(self.starttag(node, 'dl', CLASS=node['desctype']))
def depart_desc(self, node):
self.body.append('</dl>\n\n')
def visit_desc_signature(self, node):
# the id is set automatically
self.body.append(self.starttag(node, 'dt'))
# anchor for per-desc interactive data
if node.parent['desctype'] != 'describe' and node['ids'] and node['first']:
self.body.append('<!--#%s#-->' % node['ids'][0])
if node.parent['desctype'] in ('class', 'exception'):
self.body.append('%s ' % node.parent['desctype'])
def depart_desc_signature(self, node):
self.body.append('</dt>\n')
def visit_desc_classname(self, node):
self.body.append(self.starttag(node, 'tt', '', CLASS='descclassname'))
def depart_desc_classname(self, node):
self.body.append('</tt>')
def visit_desc_name(self, node):
self.body.append(self.starttag(node, 'tt', '', CLASS='descname'))
def depart_desc_name(self, node):
self.body.append('</tt>')
def visit_desc_parameterlist(self, node):
self.body.append('<big>(</big>')
self.first_param = 1
def depart_desc_parameterlist(self, node):
self.body.append('<big>)</big>')
def visit_desc_parameter(self, node):
if not self.first_param:
self.body.append(', ')
else:
self.first_param = 0
if not node.hasattr('noemph'):
self.body.append('<em>')
def depart_desc_parameter(self, node):
if not node.hasattr('noemph'):
self.body.append('</em>')
def visit_desc_optional(self, node):
self.body.append('<span class="optional">[</span>')
def depart_desc_optional(self, node):
self.body.append('<span class="optional">]</span>')
def visit_desc_content(self, node):
self.body.append(self.starttag(node, 'dd', ''))
def depart_desc_content(self, node):
self.body.append('</dd>')
def visit_refcount(self, node):
self.body.append(self.starttag(node, 'em', '', CLASS='refcount'))
def depart_refcount(self, node):
self.body.append('</em>')
def visit_versionmodified(self, node):
self.body.append(self.starttag(node, 'p'))
text = version_text[node['type']] % node['version']
if len(node):
text += ': '
else:
text += '.'
self.body.append('<span class="versionmodified">%s</span>' % text)
def depart_versionmodified(self, node):
self.body.append('</p>\n')
# overwritten -- we don't want source comments to show up in the HTML
def visit_comment(self, node):
raise nodes.SkipNode
# overwritten
def visit_admonition(self, node, name=''):
self.body.append(self.start_tag_with_title(
node, 'div', CLASS=('admonition ' + name)))
if name and name != 'seealso':
node.insert(0, nodes.title(name, self.language.labels[name]))
self.set_first_last(node)
def visit_seealso(self, node):
self.visit_admonition(node, 'seealso')
def depart_seealso(self, node):
self.depart_admonition(node)
# overwritten
def visit_title(self, node, move_ids=1):
# if we have a section we do our own processing in order
# to have ids in the hN-tags and not in additional a-tags
if isinstance(node.parent, nodes.section):
h_level = self.section_level + self.initial_header_level - 1
if node.parent.get('ids'):
attrs = {'ids': node.parent['ids']}
else:
attrs = {}
self.body.append(self.starttag(node, 'h%d' % h_level, '', **attrs))
self.context.append('</h%d>\n' % h_level)
else:
BaseTranslator.visit_title(self, node, move_ids)
# overwritten
def visit_literal_block(self, node):
from .highlighting import highlight_block
self.body.append(highlight_block(node.rawsource, self.highlightlang))
raise nodes.SkipNode
def visit_productionlist(self, node):
self.body.append(self.starttag(node, 'pre'))
names = []
for production in node:
names.append(production['tokenname'])
maxlen = max(len(name) for name in names)
for production in node:
if production['tokenname']:
self.body.append(self.starttag(production, 'strong', ''))
self.body.append(production['tokenname'].ljust(maxlen) +
'</strong> ::= ')
lastname = production['tokenname']
else:
self.body.append('%s ' % (' '*len(lastname)))
production.walkabout(self)
self.body.append('\n')
self.body.append('</pre>\n')
raise nodes.SkipNode
def depart_productionlist(self, node):
pass
def visit_production(self, node):
pass
def depart_production(self, node):
pass
def visit_centered(self, node):
self.body.append(self.starttag(node, 'center') + '<strong>')
def depart_centered(self, node):
self.body.append('</strong></center>')
def visit_compact_paragraph(self, node):
pass
def depart_compact_paragraph(self, node):
pass
def visit_highlightlang(self, node):
self.highlightlang = node['lang']
def depart_highlightlang(self, node):
pass
def visit_toctree(self, node):
# this only happens when formatting a toc from env.tocs -- in this
# case we don't want to include the subtree
raise nodes.SkipNode
def visit_index(self, node):
raise nodes.SkipNode
class SmartyPantsHTMLTranslator(HTMLTranslator):
"""
Handle ordinary text via smartypants, converting quotes and dashes
to the correct entities.
"""
def __init__(self, *args, **kwds):
self.no_smarty = 0
HTMLTranslator.__init__(self, *args, **kwds)
def visit_literal(self, node):
self.no_smarty += 1
try:
# this raises SkipNode
HTMLTranslator.visit_literal(self, node)
finally:
self.no_smarty -= 1
def visit_productionlist(self, node):
self.no_smarty += 1
try:
HTMLTranslator.visit_productionlist(self, node)
finally:
self.no_smarty -= 1
def encode(self, text):
text = HTMLTranslator.encode(self, text)
if self.no_smarty <= 0:
text = sphinx_smarty_pants(text)
return text
|