Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of scm.gforge.inria.fr:/gitroot/simgrid/simgrid
[simgrid.git] / docs / source / _ext / hidden_code_block.py
1 """Simple, inelegant Sphinx extension which adds a directive for a
2 highlighted code-block that may be toggled hidden and shown in HTML.  
3 This is possibly useful for teaching courses.
4
5 The directive, like the standard code-block directive, takes
6 a language argument and an optional linenos parameter.  The
7 hidden-code-block adds starthidden and label as optional 
8 parameters.
9
10 Examples:
11
12 .. hidden-code-block:: python
13     :starthidden: False
14
15     a = 10
16     b = a + 5
17
18 .. hidden-code-block:: python
19     :label: --- SHOW/HIDE ---
20
21     x = 10
22     y = x + 5
23
24 Thanks to http://www.javascriptkit.com/javatutors/dom3.shtml for 
25 inspiration on the javascript.  
26
27 Thanks to Milad 'animal' Fatenejad for suggesting this extension 
28 in the first place.
29
30 Written by Anthony 'el Scopz' Scopatz, January 2012.
31 https://github.com/scopatz/hiddencode
32
33 Released under the WTFPL (http://sam.zoy.org/wtfpl/).
34 """
35
36 from docutils import nodes
37 from docutils.parsers.rst import directives
38 from sphinx.directives.code import CodeBlock
39
40 HCB_COUNTER = 0
41
42 js_showhide = """\
43 <script type="text/javascript">
44     function showhide(element){
45         if (!document.getElementById)
46             return
47
48         if (element.style.display == "block")
49             element.style.display = "none"
50         else
51             element.style.display = "block"
52     };
53 </script>
54 """
55
56 def nice_bool(arg):
57     tvalues = ('true',  't', 'yes', 'y')
58     fvalues = ('false', 'f', 'no',  'n')
59     arg = directives.choice(arg, tvalues + fvalues)
60     return arg in tvalues
61
62
63 class hidden_code_block(nodes.General, nodes.FixedTextElement):
64     pass
65
66
67 class HiddenCodeBlock(CodeBlock):
68     """Hidden code block is Hidden"""
69
70     option_spec = dict(starthidden=nice_bool, 
71                        label=str,
72                        **CodeBlock.option_spec)
73
74     def run(self):
75         # Body of the method is more or less copied from CodeBlock
76         code = u'\n'.join(self.content)
77         hcb = hidden_code_block(code, code)
78         hcb['language'] = self.arguments[0]
79         hcb['linenos'] = 'linenos' in self.options
80         hcb['starthidden'] = self.options.get('starthidden', True)
81         hcb['label'] = self.options.get('label', '+ show/hide code')
82         hcb.line = self.lineno
83         return [hcb]
84
85
86 def visit_hcb_html(self, node):
87     """Visit hidden code block"""
88     global HCB_COUNTER
89     HCB_COUNTER += 1
90
91     # We want to use the original highlighter so that we don't
92     # have to reimplement it.  However it raises a SkipNode 
93     # error at the end of the function call.  Thus we intercept
94     # it and raise it again later.
95     try: 
96         self.visit_literal_block(node)
97     except nodes.SkipNode:
98         pass
99
100     # The last element of the body should be the literal code 
101     # block that was just made.
102     code_block = self.body[-1]
103
104     fill_header = {'divname': 'hiddencodeblock{0}'.format(HCB_COUNTER), 
105                    'startdisplay': 'none' if node['starthidden'] else 'block', 
106                    'label': node.get('label'), 
107                    }
108
109     divheader = ("""<a href="javascript:showhide(document.getElementById('{divname}'))">"""
110                  """{label}</a><br />"""
111                  '''<div id="{divname}" style="display: {startdisplay}">'''
112                  ).format(**fill_header)
113
114     code_block = js_showhide + divheader + code_block + "</div>"
115
116     # reassign and exit
117     self.body[-1] = code_block
118     raise nodes.SkipNode
119
120
121 def depart_hcb_html(self, node):
122     """Depart hidden code block"""
123     # Stub because of SkipNode in visit
124
125
126 def setup(app):
127     app.add_directive('hidden-code-block', HiddenCodeBlock)
128     app.add_node(hidden_code_block, html=(visit_hcb_html, depart_hcb_html))