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
|
# -*- coding: utf-8 -*-
# Useful doc: https://www.sphinx-doc.org/en/master/extdev/markupapi.html
# Example: https://www.sphinx-doc.org/en/master/development/tutorials/recipe.html
import os
from docutils.parsers.rst import Directive, directives
from docutils import nodes
from docutils.statemachine import StringList
from sphinx.util.osutil import copyfile
from sphinx.util import logging
CSS_FILE = 'showfile.css'
JS_FILE = 'showfile.js'
class ShowFileDirective(Directive):
"""
Show a file or propose it to download.
"""
has_content = False
optional_arguments = 1
option_spec = {
'language': directives.unchanged
}
def run(self):
filename = self.arguments[0]
language = "python"
if 'language' in self.options:
language = self.options['language']
logger = logging.getLogger(__name__)
# logger.info('showfile {} in {}'.format(filename, language))
new_content = [
'.. toggle-header::',
' :header: View {}'.format(filename),
'',
' `Download {} <https://framagit.org/simgrid/simgrid/tree/master/{}>`_'.format(os.path.basename(filename), filename),
'',
' .. literalinclude:: ../../{}'.format(filename),
' :language: {}'.format(language),
''
]
for idx, line in enumerate(new_content):
# logger.info('{} {}'.format(idx,line))
self.content.data.insert(idx, line)
self.content.items.insert(idx, (None, idx))
node = nodes.container()
self.state.nested_parse(self.content, self.content_offset, node)
return node.children
class ExampleTabDirective(Directive):
"""
A group-tab for a given language, in the presentation of the examples.
"""
has_content = True
optional_arguments = 0
mandatory_argument = 0
def run(self):
self.assert_has_content()
filename = self.content[0].strip()
self.content.trim_start(1)
(language, langcode) = (None, None)
if filename[-3:] == '.py':
language = 'Python'
langcode = 'py'
elif filename[-2:] == '.c':
language = 'C'
langcode = 'c'
elif filename[-4:] == '.cpp':
language = 'C++'
langcode = 'cpp'
elif filename[-4:] == '.xml':
language = 'XML'
langcode = 'xml'
elif filename[-5:] == '.java':
language = 'Java'
langcode = 'java'
else:
raise Exception("Unknown language '{}'. Please choose '.cpp', '.py', '.c', '.java' or '.xml'".format(language))
for idx, line in enumerate(self.content.data):
self.content.data[idx] = ' ' + line
for idx, line in enumerate([
'.. group-tab:: {}'.format(language),
' ']):
self.content.data.insert(idx, line)
self.content.items.insert(idx, (None, idx))
for line in [
'',
' .. showfile:: {}'.format(filename),
' :language: {}'.format(langcode),
'']:
idx = len(self.content.data)
self.content.data.insert(idx, line)
self.content.items.insert(idx, (None, idx))
# logger = logging.getLogger(__name__)
# logger.info('------------------')
# for line in self.content.data:
# logger.info('{}'.format(line))
node = nodes.container()
self.state.nested_parse(self.content, self.content_offset, node)
return node.children
class ToggleDirective(Directive):
has_content = True
option_spec = {
'header': directives.unchanged,
'show': directives.flag
}
optional_arguments = 1
def run(self):
node = nodes.container()
node['classes'].append('toggle-content')
if "show" not in self.options:
# This :show: thing is not working, and I fail to see why.
# Only the hidden-content class gets a call to hide() in the Javascript,
# and :show:n block# still get hidden when I load the page.
# No idea what's going on (Mt)
node['classes'].append('hidden-content')
par = nodes.container()
par['classes'].append('toggle-header')
if self.arguments and self.arguments[0]:
par['classes'].append(self.arguments[0])
self.state.nested_parse(StringList([self.options["header"]]), self.content_offset, par)
self.state.nested_parse(self.content, self.content_offset, node)
return [par, node]
def add_assets(app):
app.add_css_file(CSS_FILE)
app.add_js_file(JS_FILE)
def copy_assets(app, exception):
if app.builder.name not in ['html', 'readthedocs'] or exception:
return
logger = logging.getLogger(__name__)
logger.info('Copying showfile stylesheet/javascript... ', nonl=True)
dest = os.path.join(app.builder.outdir, '_static', CSS_FILE)
source = os.path.join(os.path.abspath(os.path.dirname(__file__)), CSS_FILE)
copyfile(source, dest)
dest = os.path.join(app.builder.outdir, '_static', JS_FILE)
source = os.path.join(os.path.abspath(os.path.dirname(__file__)), JS_FILE)
copyfile(source, dest)
logger.info('done')
def setup(app):
app.add_directive('toggle-header', ToggleDirective)
app.add_directive('showfile', ShowFileDirective)
app.add_directive('example-tab', ExampleTabDirective)
app.connect('builder-inited', add_assets)
app.connect('build-finished', copy_assets)
|