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
|
"""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.
https://github.com/scopatz/hiddencode
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))
|