`todo directive now supports :name:` option

This commit is contained in:
Takeshi KOMIYA
2019-03-24 16:35:20 +09:00
parent ab268c3138
commit 8d5ef57c6f
3 changed files with 15 additions and 36 deletions

View File

@@ -51,6 +51,7 @@ Features added
* Add a helper method ``SphinxDirective.set_source_info()`` * Add a helper method ``SphinxDirective.set_source_info()``
* #6180: Support ``--keep-going`` with BuildDoc setup command * #6180: Support ``--keep-going`` with BuildDoc setup command
* ``math`` directive now supports ``:class:`` option * ``math`` directive now supports ``:class:`` option
* todo: ``todo`` directive now supports ``:name:`` option
Bugs fixed Bugs fixed
---------- ----------

View File

@@ -22,6 +22,7 @@ from sphinx.errors import NoUri
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_refnode
from sphinx.util.texescape import tex_escape_map from sphinx.util.texescape import tex_escape_map
if False: if False:
@@ -55,6 +56,7 @@ class Todo(BaseAdmonition, SphinxDirective):
final_argument_whitespace = False final_argument_whitespace = False
option_spec = { option_spec = {
'class': directives.class_option, 'class': directives.class_option,
'name': directives.unchanged,
} }
def run(self): def run(self):
@@ -67,13 +69,10 @@ class Todo(BaseAdmonition, SphinxDirective):
return [todo] return [todo]
elif isinstance(todo, todo_node): elif isinstance(todo, todo_node):
todo.insert(0, nodes.title(text=_('Todo'))) todo.insert(0, nodes.title(text=_('Todo')))
self.add_name(todo)
self.set_source_info(todo) self.set_source_info(todo)
self.state.document.note_explicit_target(todo)
targetid = 'index-%s' % self.env.new_serialno('index') return [todo]
# Stash the target to be retrieved later in latex_visit_todo_node.
todo['targetref'] = '%s:%s' % (self.env.docname, targetid)
targetnode = nodes.target('', '', ids=[targetid])
return [targetnode, todo]
else: else:
raise RuntimeError # never reached here raise RuntimeError # never reached here
@@ -89,20 +88,14 @@ def process_todos(app, doctree):
for node in doctree.traverse(todo_node): for node in doctree.traverse(todo_node):
app.emit('todo-defined', node) app.emit('todo-defined', node)
try:
targetnode = node.parent[node.parent.index(node) - 1]
if not isinstance(targetnode, nodes.target):
raise IndexError
except IndexError:
targetnode = None
newnode = node.deepcopy() newnode = node.deepcopy()
del newnode['ids'] newnode['ids'] = []
env.todo_all_todos.append({ # type: ignore env.todo_all_todos.append({ # type: ignore
'docname': env.docname, 'docname': env.docname,
'source': node.source or env.doc2path(env.docname), 'source': node.source or env.doc2path(env.docname),
'lineno': node.line, 'lineno': node.line,
'todo': newnode, 'todo': newnode,
'target': targetnode, 'target': node['ids'][0],
}) })
if env.config.todo_emit_warnings: if env.config.todo_emit_warnings:
@@ -167,27 +160,16 @@ def process_todo_nodes(app, doctree, fromdocname):
para += nodes.Text(desc1, desc1) para += nodes.Text(desc1, desc1)
# Create a reference # Create a reference
newnode = nodes.reference('', '', internal=True)
innernode = nodes.emphasis(_('original entry'), _('original entry')) innernode = nodes.emphasis(_('original entry'), _('original entry'))
try: try:
newnode['refuri'] = app.builder.get_relative_uri( para += make_refnode(app.builder, fromdocname, todo_info['docname'],
fromdocname, todo_info['docname']) todo_info['target'], innernode)
if 'refid' in todo_info['target']:
newnode['refuri'] += '#' + todo_info['target']['refid']
else:
newnode['refuri'] += '#' + todo_info['target']['ids'][0]
except NoUri: except NoUri:
# ignore if no URI can be determined, e.g. for LaTeX output # ignore if no URI can be determined, e.g. for LaTeX output
pass pass
newnode.append(innernode)
para += newnode
para += nodes.Text(desc2, desc2) para += nodes.Text(desc2, desc2)
todo_entry = todo_info['todo'] todo_entry = todo_info['todo']
# Remove targetref from the (copied) node to avoid emitting a
# duplicate label of the original entry when we walk this node.
if 'targetref' in todo_entry:
del todo_entry['targetref']
# (Recursively) resolve references in the todo content # (Recursively) resolve references in the todo content
env.resolve_references(todo_entry, todo_info['docname'], env.resolve_references(todo_entry, todo_info['docname'],
@@ -230,12 +212,7 @@ def depart_todo_node(self, node):
def latex_visit_todo_node(self, node): def latex_visit_todo_node(self, node):
# type: (LaTeXTranslator, todo_node) -> None # type: (LaTeXTranslator, todo_node) -> None
self.body.append('\n\\begin{sphinxadmonition}{note}{') self.body.append('\n\\begin{sphinxadmonition}{note}{')
# If this is the original todo node, emit a label that will be referenced by self.body.append(self.hypertarget_to(node))
# a hyperref in the todolist.
target = node.get('targetref')
if target is not None:
self.body.append('\\label{%s}' % target)
title_node = cast(nodes.title, node[0]) title_node = cast(nodes.title, node[0])
self.body.append('%s:}' % title_node.astext().translate(tex_escape_map)) self.body.append('%s:}' % title_node.astext().translate(tex_escape_map))
node.pop(0) node.pop(0)

View File

@@ -107,14 +107,15 @@ def test_todo_valid_link(app, status, warning):
# Look for the link to foo. Note that there are two of them because the # Look for the link to foo. Note that there are two of them because the
# source document uses todolist twice. We could equally well look for links # source document uses todolist twice. We could equally well look for links
# to bar. # to bar.
link = (r'\{\\hyperref\[\\detokenize\{(.*?foo.*?)}]\{\\sphinxcrossref{' link = (r'{\\hyperref\[\\detokenize{(.*?foo.*?)}]{\\sphinxcrossref{'
r'\\sphinxstyleemphasis{original entry}}}}') r'\\sphinxstyleemphasis{original entry}}}}')
m = re.findall(link, content) m = re.findall(link, content)
assert len(m) == 4 assert len(m) == 4
target = m[0] target = m[0]
# Look for the targets of this link. # Look for the targets of this link.
labels = [m for m in re.findall(r'\\label\{([^}]*)}', content) if m == target] labels = re.findall(r'\\label{\\detokenize{([^}]*)}}', content)
matched = [l for l in labels if l == target]
# If everything is correct we should have exactly one target. # If everything is correct we should have exactly one target.
assert len(labels) == 1 assert len(matched) == 1