refactor: todo: Add TodoDomain to collect todo nodes

This commit is contained in:
Takeshi KOMIYA 2019-06-02 23:14:11 +09:00
parent 0441ab0350
commit 9abb4820b1
3 changed files with 67 additions and 14 deletions

View File

@ -10,6 +10,10 @@ Incompatible changes
Deprecated
----------
* ``sphinx.ext.todo.merge_info()``
* ``sphinx.ext.todo.process_todos()``
* ``sphinx.ext.todo.purge_todos()``
Features added
--------------

View File

@ -26,6 +26,21 @@ The following is a list of deprecated interfaces.
- (will be) Removed
- Alternatives
* - ``sphinx.ext.todo.merge_info()``
- 2.2
- 4.0
- ``sphinx.ext.todo.TodoDomain``
* - ``sphinx.ext.todo.process_todos()``
- 2.2
- 4.0
- ``sphinx.ext.todo.TodoDomain``
* - ``sphinx.ext.todo.purge_todos()``
- 2.2
- 4.0
- ``sphinx.ext.todo.TodoDomain``
* - ``sphinx.builders.latex.LaTeXBuilder.apply_transforms()``
- 2.1
- 4.0

View File

@ -11,6 +11,7 @@
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import cast
from docutils import nodes
@ -18,6 +19,8 @@ from docutils.parsers.rst import directives
from docutils.parsers.rst.directives.admonitions import BaseAdmonition
import sphinx
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.util import logging
@ -69,6 +72,7 @@ class Todo(BaseAdmonition, SphinxDirective):
return [todo]
elif isinstance(todo, todo_node):
todo.insert(0, nodes.title(text=_('Todo')))
todo['docname'] = self.env.docname
self.add_name(todo)
self.set_source_info(todo)
self.state.document.note_explicit_target(todo)
@ -77,8 +81,39 @@ class Todo(BaseAdmonition, SphinxDirective):
raise RuntimeError # never reached here
class TodoDomain(Domain):
name = 'todo'
label = 'todo'
@property
def todos(self):
# type: () -> Dict[str, List[todo_node]]
return self.data.setdefault('todos', {})
def clear_doc(self, docname):
# type: (str) -> None
self.todos.pop(docname, None)
def merge_domaindata(self, docnames, otherdata):
# type: (List[str], Dict) -> None
for docname in docnames:
self.todos[docname] = otherdata['todos'][docname]
def process_doc(self, env, docname, document):
# type: (BuildEnvironment, str, nodes.document) -> None
todos = self.todos.setdefault(docname, [])
for todo in document.traverse(todo_node):
env.app.emit('todo-defined', todo)
todos.append(todo)
if env.config.todo_emit_warnings:
logger.warning(__("TODO entry found: %s"), todo[1].astext(),
location=todo)
def process_todos(app, doctree):
# type: (Sphinx, nodes.document) -> None
warnings.warn('process_todos() is deprecated.', RemovedInSphinx40Warning)
# collect all todos in the environment
# this is not done in the directive itself because it some transformations
# must have already been run, e.g. substitutions
@ -127,10 +162,8 @@ def process_todo_nodes(app, doctree, fromdocname):
"""Replace all todolist nodes with a list of the collected todos.
Augment each todo with a backlink to the original location.
"""
env = app.builder.env
if not hasattr(env, 'todo_all_todos'):
env.todo_all_todos = [] # type: ignore
domain = cast(TodoDomain, app.env.get_domain('todo'))
todos = sum(domain.todos.values(), [])
for node in doctree.traverse(todolist):
if node.get('ids'):
@ -142,14 +175,14 @@ def process_todo_nodes(app, doctree, fromdocname):
node.replace_self(content)
continue
for todo_info in env.todo_all_todos: # type: ignore
for todo_info in todos:
para = nodes.paragraph(classes=['todo-source'])
if app.config['todo_link_only']:
description = _('<<original entry>>')
else:
description = (
_('(The <<original entry>> is located in %s, line %d.)') %
(todo_info['source'], todo_info['lineno'])
(todo_info.source, todo_info.line)
)
desc1 = description[:description.find('<<')]
desc2 = description[description.find('>>') + 2:]
@ -159,16 +192,17 @@ def process_todo_nodes(app, doctree, fromdocname):
innernode = nodes.emphasis(_('original entry'), _('original entry'))
try:
para += make_refnode(app.builder, fromdocname, todo_info['docname'],
todo_info['target'], innernode)
todo_info['ids'][0], innernode)
except NoUri:
# ignore if no URI can be determined, e.g. for LaTeX output
pass
para += nodes.Text(desc2, desc2)
todo_entry = todo_info['todo']
todo_entry = todo_info.deepcopy()
todo_entry['ids'].clear()
# (Recursively) resolve references in the todo content
env.resolve_references(todo_entry, todo_info['docname'],
app.env.resolve_references(todo_entry, todo_info['docname'],
app.builder)
# Insert into the todolist
@ -180,6 +214,7 @@ def process_todo_nodes(app, doctree, fromdocname):
def purge_todos(app, env, docname):
# type: (Sphinx, BuildEnvironment, str) -> None
warnings.warn('purge_todos() is deprecated.', RemovedInSphinx40Warning)
if not hasattr(env, 'todo_all_todos'):
return
env.todo_all_todos = [todo for todo in env.todo_all_todos # type: ignore
@ -188,6 +223,7 @@ def purge_todos(app, env, docname):
def merge_info(app, env, docnames, other):
# type: (Sphinx, BuildEnvironment, Iterable[str], BuildEnvironment) -> None
warnings.warn('merge_info() is deprecated.', RemovedInSphinx40Warning)
if not hasattr(other, 'todo_all_todos'):
return
if not hasattr(env, 'todo_all_todos'):
@ -242,12 +278,10 @@ def setup(app):
app.add_directive('todo', Todo)
app.add_directive('todolist', TodoList)
app.connect('doctree-read', process_todos)
app.add_domain(TodoDomain)
app.connect('doctree-resolved', process_todo_nodes)
app.connect('env-purge-doc', purge_todos)
app.connect('env-merge-info', merge_info)
return {
'version': sphinx.__display_version__,
'env_version': 1,
'env_version': 2,
'parallel_read_safe': True
}