-
Notifications
You must be signed in to change notification settings - Fork 0
/
hidden_code_block.py
127 lines (94 loc) · 3.56 KB
/
hidden_code_block.py
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
"""Simple, inelegant Sphinx extension which adds a directive for a
highlighted code-block that may be toggled hidden and shown in HTML.
This is possibly useful for teaching courses.
The directive, like the standard code-block directive, takes
a language argument and an optional linenos parameter. The
hidden-code-block adds starthidden and label as optional
parameters.
Examples:
.. hidden-code-block:: python
:starthidden: False
a = 10
b = a + 5
.. hidden-code-block:: python
:label: --- SHOW/HIDE ---
x = 10
y = x + 5
Thanks to http://www.javascriptkit.com/javatutors/dom3.shtml for
inspiration on the javascript.
Thanks to Milad 'animal' Fatenejad for suggesting this extension
in the first place.
Written by Anthony 'el Scopz' Scopatz, January 2012.
Released under the WTFPL (http://sam.zoy.org/wtfpl/).
"""
from docutils import nodes
from docutils.parsers.rst import directives
from sphinx.directives.code import CodeBlock
HCB_COUNTER = 0
js_showhide = """\
<script type="text/javascript">
function showhide(element){
if (!document.getElementById)
return
if (element.style.display == "block")
element.style.display = "none"
else
element.style.display = "block"
};
</script>
"""
def nice_bool(arg):
tvalues = ('true', 't', 'yes', 'y')
fvalues = ('false', 'f', 'no', 'n')
arg = directives.choice(arg, tvalues + fvalues)
return arg in tvalues
class hidden_code_block(nodes.General, nodes.FixedTextElement):
pass
class HiddenCodeBlock(CodeBlock):
"""Hidden code block is Hidden"""
option_spec = dict(starthidden=nice_bool,
label=str,
**CodeBlock.option_spec)
def run(self):
# Body of the method is more or less copied from CodeBlock
code = u'\n'.join(self.content)
hcb = hidden_code_block(code, code)
hcb['language'] = self.arguments[0]
hcb['linenos'] = 'linenos' in self.options
hcb['starthidden'] = self.options.get('starthidden', True)
hcb['label'] = self.options.get('label', '+ show/hide code')
hcb.line = self.lineno
return [hcb]
def visit_hcb_html(self, node):
"""Visit hidden code block"""
global HCB_COUNTER
HCB_COUNTER += 1
# We want to use the original highlighter so that we don't
# have to reimplement it. However it raises a SkipNode
# error at the end of the function call. Thus we intercept
# it and raise it again later.
try:
self.visit_literal_block(node)
except nodes.SkipNode:
pass
# The last element of the body should be the literal code
# block that was just made.
code_block = self.body[-1]
fill_header = {'divname': 'hiddencodeblock{0}'.format(HCB_COUNTER),
'startdisplay': 'none' if node['starthidden'] else 'block',
'label': node.get('label'),
}
divheader = ("""<a href="javascript:showhide(document.getElementById('{divname}'))">"""
"""{label}</a><br />"""
'''<div id="{divname}" style="display: {startdisplay}">'''
).format(**fill_header)
code_block = js_showhide + divheader + code_block + "</div>"
# reassign and exit
self.body[-1] = code_block
raise nodes.SkipNode
def depart_hcb_html(self, node):
"""Depart hidden code block"""
# Stub because of SkipNode in visit
def setup(app):
app.add_directive('hidden-code-block', HiddenCodeBlock)
app.add_node(hidden_code_block, html=(visit_hcb_html, depart_hcb_html))