"""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 = """\
"""
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 = (""""""
"""{label}
"""
'''