Commits

Georg Brandl committed 99e97f4

Add todo extension.

Comments (0)

Files changed (9)

     creates links to Sphinx documentation of Python objects in other
     projects.
 
+  - The new extension ``sphinx.ext.todo`` allows the insertion of
+    "To do" directives whose visibility in the output can be toggled.
+    It also adds a directive to compile a list of all todo items.
+
   - sphinx.ext.autodoc has a new event ``autodoc-process-signature``
     that allows tuning function signature introspection.
 
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 #latex_appendices = []
 
 
+# Extension options
+# -----------------
+
+todo_include_todos = True
+
+
 # Extension interface
 # -------------------
 

doc/ext/builderapi.rst

 Writing new builders
 ====================
 
-XXX to be expanded.
+.. todo:: Expand this.
 
 .. currentmodule:: sphinx.builder
 

doc/ext/coverage.rst

    To use this builder, activate the coverage extension in your configuration
    file and give ``-b coverage`` on the command line.
 
-XXX to be expanded.
+.. todo:: Write this section.
 
 Several new configuration values can be used to specify what the builder
 should check:

doc/ext/refcounting.rst

 .. module:: sphinx.ext.refcounting
    :synopsis: Keep track of reference counting behavior.
 
-XXX to be written.
+.. todo:: Write this section.
+:mod:`sphinx.ext.todo` -- Support for todo items
+================================================
+
+.. module:: sphinx.ext.todo
+   :synopsis: Allow inserting todo items into documents.
+.. moduleauthor:: Daniel Bültmann
+
+.. versionadded:: 0.5
+
+There are two additional directives when using this extension:
+
+.. directive:: todo
+
+   Use this directive like, for example, :dir:`note`.
+
+   It will only show up in the output if :confval:`todo_include_todos` is true.
+
+
+.. directive:: todolist
+
+   This directive is replaced by a list of all todo directives in the whole
+   documentation, if :confval:`todo_include_todos` is true.
+
+   
+There is also an additional config value:
+   
+.. confval:: todo_include_todos
+
+   If this is ``True``, :dir:`todo` and :dir:`todolist` produce output, else
+   they produce nothing.  The default is ``False``.

doc/extensions.rst

    ext/refcounting
    ext/ifconfig
    ext/coverage
+   ext/todo

sphinx/ext/todo.py

+# -*- coding: utf-8 -*-
+"""
+    sphinx.ext.todo
+    ~~~~~~~~~~~~~~~
+
+    Allow todos to be inserted into your documentation.  Inclusion of todos can
+    be switched of by a configuration variable.  The todolist directive collects
+    all todos of your project and lists them along with a backlink to the
+    original location.
+
+    :copyright: 2008 Daniel Bültmann.
+    :license: BSD.
+"""
+
+from docutils import nodes
+from docutils.parsers.rst.directives import admonitions
+
+from sphinx.util.compat import make_admonition
+
+class todo_node(nodes.Admonition, nodes.Element): pass
+class todolist(nodes.General, nodes.Element): pass
+
+
+def todo_directive(name, arguments, options, content, lineno,
+                   content_offset, block_text, state, state_machine):
+    env = state.document.settings.env
+
+    targetid = "todo-%s" % env.index_num
+    env.index_num += 1
+    targetnode = nodes.target('', '', ids=[targetid])
+
+    ad = make_admonition(todo_node, name, [_('Todo')], options, content, lineno,
+                         content_offset, block_text, state, state_machine)
+
+    # Attach a list of all todos to the environment,
+    # the todolist works with the collected todo nodes
+    if not hasattr(env, 'todo_all_todos'):
+        env.todo_all_todos = []
+    env.todo_all_todos.append({
+        'docname': env.docname,
+        'lineno': lineno,
+        'todo': ad[0].deepcopy(),
+        'target': targetnode,
+    })
+
+    return [targetnode] + ad
+
+
+def todolist_directive(name, arguments, options, content, lineno,
+                       content_offset, block_text, state, state_machine):
+    # Simply insert an empty todolist node which will be replaced later
+    # when process_todolist is called
+    return [todolist('')]
+
+
+def process_todo_nodes(app, doctree, fromdocname):
+    if not app.config['todo_include_todos']:
+        for node in doctree.traverse(todo_node):
+            node.parent.remove(node)
+
+
+def process_todolist(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
+
+    for node in doctree.traverse(todolist):
+        if not app.config['todo_include_todos']:
+            node.replace_self([])
+            continue
+
+        content = []
+
+        for todo_info in env.todo_all_todos:
+            para = nodes.paragraph()
+            filename = env.doc2path(todo_info['docname'], base=None)
+            description = (
+                _('(The original entry is located in %s, line %d and can be found ') %
+                (filename, todo_info['lineno']))
+            para += nodes.Text(description, description)
+
+            # Create a reference
+            newnode = nodes.reference('', '')
+            innernode = nodes.emphasis(_('here'), _('here'))
+            newnode['refdocname'] = todo_info['docname']
+            newnode['refuri'] = app.builder.get_relative_uri(
+                fromdocname, todo_info['docname'])
+            newnode['refuri'] += '#' + todo_info['target']['refid']
+            newnode.append(innernode)
+            para += newnode
+            para += nodes.Text('.)', '.)')
+
+            # Insert into the todolist
+            content.append(todo_info['todo'])
+            content.append(para)
+
+        node.replace_self(content)
+
+
+def purge_todos(app, env, docname):
+    if not hasattr(env, 'todo_all_todos'):
+        return
+    env.todo_all_todos = [todo for todo in env.todo_all_todos
+                          if todo['docname'] != docname]
+
+
+def visit_todo_node(self, node):
+    self.visit_admonition(node)
+
+def depart_todo_node(self, node):
+    self.depart_admonition(node)
+
+def setup(app):
+    app.add_config_value('todo_include_todos', False, False)
+
+    app.add_node(todolist)
+    app.add_node(todo_node,
+                 html=(visit_todo_node, depart_todo_node),
+                 latex=(visit_todo_node, depart_todo_node),
+                 text=(visit_todo_node, depart_todo_node))
+
+    app.add_directive('todo', todo_directive, 1, (0, 0, 1))
+    app.add_directive('todolist', todolist_directive, 1, (0, 0, 1))
+    app.connect('doctree-resolved', process_todolist)
+    app.connect('doctree-resolved', process_todo_nodes)
+    app.connect('env-purge-doc', purge_todos)
+

tests/root/conf.py

 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['ext', 'sphinx.ext.autodoc', 'sphinx.ext.jsmath', 'sphinx.ext.coverage']
+extensions = ['ext', 'sphinx.ext.autodoc', 'sphinx.ext.jsmath',
+              'sphinx.ext.coverage', 'sphinx.ext.todo']
 jsmath_path = 'dummy.js'
 
 # Add any paths that contain templates here, relative to this directory.