formations/algo/algofundoc/ext/question.py

210 lines
7.3 KiB
Python
Raw Normal View History

2018-08-24 14:09:11 +02:00
# -*- coding: utf-8 -*-
"""
question
~~~~~~~~
Allow questions to be inserted into your documentation.
The questionlist directive collects
all questions of your project and lists them along with a backlink to the
original location.
:copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
global questno, qno
questno = 1
qno = 0
from docutils import nodes
from sphinx.locale import _
from sphinx.environment import NoUri
from sphinx.util.nodes import set_source_info
#from sphinx.util.compat import Directive #, make_admonition
from docutils.parsers.rst.directives.admonitions import Directive
#from docutils.parsers.rst.directives.admonitions import make_admonition
class question_node(nodes.Admonition, nodes.Element): pass
class questionlist(nodes.General, nodes.Element): pass
def make_admonition(node_class, name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
if not content:
error = state_machine.reporter.error(
'The "%s" admonition is empty; content required.' % (name),
nodes.literal_block(block_text, block_text), line=lineno)
return [error]
text = '\n'.join(content)
admonition_node = node_class(text)
if arguments:
title_text = arguments[0]
textnodes, messages = state.inline_text(title_text, lineno)
admonition_node += nodes.title(title_text, '', *textnodes)
admonition_node += messages
if 'class' in options:
classes = options['class']
else:
classes = ['admonition-' + nodes.make_id(title_text)]
admonition_node['classes'] += classes
state.nested_parse(content, content_offset, admonition_node)
return [admonition_node]
class Question(Directive):
"""
A question entry, displayed in the form of an admonition.
"""
has_content = True
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = False
option_spec = {'number': int}
def run(self):
global questno
questno = self.options.get('number', questno)
env = self.state.document.settings.env
targetid = 'index-%s' % env.new_serialno('index')
targetnode = nodes.target('', '', ids=[targetid])
self.options['class'] = [_('question')]
ad = make_admonition(question_node, self.name,
[_('Question %d'%questno)], self.options,
self.content, self.lineno, self.content_offset,
self.block_text, self.state, self.state_machine)
questno += 1
set_source_info(self, ad[0])
return [targetnode] + ad
def process_questions(app, doctree):
# collect all questions in the environment
# this is not done in the directive itself because it some transformations
# must have already been run, e.g. substitutions
env = app.builder.env
if not hasattr(env, 'question_all_questions'):
env.question_all_questions = []
global qno
for node in doctree.traverse(question_node):
try:
targetnode = node.parent[node.parent.index(node) - 1]
if not isinstance(targetnode, nodes.target):
raise IndexError
except IndexError:
targetnode = None
qno += 1
env.question_all_questions.append({
'docname': env.docname,
'source': node.source or env.doc2path(env.docname),
'lineno': node.line,
'question': node.deepcopy(),
'questno': qno,
'target': targetnode,
})
class QuestionList(Directive):
"""
A list of all question entries.
"""
has_content = False
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
# Simply insert an empty questionlist node which will be replaced later
# when process_question_nodes is called
return [questionlist('')]
def process_question_nodes(app, doctree, fromdocname):
### if not app.config['question_include_questions']:
if False:
for node in doctree.traverse(question_node):
node.parent.remove(node)
# Replace all questionlist nodes with a list of the collected questions.
# Augment each question with a backlink to the original location.
env = app.builder.env
if not hasattr(env, 'question_all_questions'):
env.question_all_questions = []
for node in doctree.traverse(questionlist):
### if not app.config['question_include_questions']:
# if False:
# node.replace_self([])
# continue
content = []
for question_info in env.question_all_questions:
qno = question_info['questno']
para = nodes.paragraph(classes=['question-source'])
description = _('<<original entry>>')
desc1 = description[:description.find('<<')]
desc2 = description[description.find('>>')+2:]
para += nodes.Text(desc1, desc1)
# Create a reference
newnode = nodes.reference('', '', internal=True)
### innernode = nodes.emphasis(_('original entry'), _('original entry'))
innernode = nodes.strong(_('Question %d'%qno), _('Question %d'%qno))
try:
newnode['refuri'] = app.builder.get_relative_uri(
fromdocname, question_info['docname'])
newnode['refuri'] += '#' + question_info['target']['refid']
except NoUri:
# ignore if no URI can be determined, e.g. for LaTeX output
pass
newnode.append(innernode)
para += newnode
para += nodes.Text(desc2, desc2)
# (Recursively) resolve references in the question content
question_entry = question_info['question']
env.resolve_references(question_entry, question_info['docname'],
app.builder)
# Insert into the questionlist
### content.append(question_entry)
content.append(para)
node.replace_self(content)
def purge_questions(app, env, docname):
if not hasattr(env, 'question_all_questions'):
return
env.question_all_questions = [question for question in env.question_all_questions
if question['docname'] != docname]
def visit_question_node(self, node):
self.visit_admonition(node)
def depart_question_node(self, node):
self.depart_admonition(node)
def setup(app):
app.add_config_value('question_include_questions', False, False)
app.add_node(questionlist)
app.add_node(question_node,
html=(visit_question_node, depart_question_node),
latex=(visit_question_node, depart_question_node),
text=(visit_question_node, depart_question_node),
man=(visit_question_node, depart_question_node),
texinfo=(visit_question_node, depart_question_node))
app.add_directive('question', Question)
app.add_directive('questionlist', QuestionList)
app.connect('doctree-read', process_questions)
app.connect('doctree-resolved', process_question_nodes)
app.connect('env-purge-doc', purge_questions)