Generating labels for nodes of a custom directive

360 views Asked by At

Using the Sphinx "TODO" Directive example I would like to reference the todo instances embedded within a .rst file. For example, if the .rst file content contains:

.. todo:: foo

.. todo:: bar

I can see that the following code (taken from the Sphinx TODO example page)

class TodoDirective(SphinxDirective):

    # this enables content in the directive
    has_content = True

    def run(self):
        targetid = 'todo-%d' % self.env.new_serialno('todo')
        targetnode = nodes.target('', '', ids=[targetid])

        todo_node = todo('\n'.join(self.content))
        todo_node += nodes.title(_('Todo'), _('Todo'))
        self.state.nested_parse(self.content, self.content_offset, todo_node)

        if not hasattr(self.env, 'todo_all_todos'):
            self.env.todo_all_todos = []

        self.env.todo_all_todos.append({
            'docname': self.env.docname,
            'lineno': self.lineno,
            'todo': todo_node.deepcopy(),
            'target': targetnode,
        })

        return [targetnode, todo_node]

Creates target nodes with ids: todo-0 and todo-1. That are successfully referenced by embedding the directive in a .rst file as:

.. todolist::

What I would like to do is reference the todo items within a .rst file like this:

:ref:`todo-0`
:ref:`todo-1`

This would require getting the TodoDirective to generate a label for each target node. I have not been able to figure out how to do so.

This simple project is posted here: https://github.com/natersoz/sphinx_sandbox

2

There are 2 answers

1
Woltan On BEST ANSWER

I had the same problem as you and was able to resolve the issue by looking at the autosectionlabel extension.

What they do there is to add the reference to the labels domain data. I got it working inside a custom directive like so:

nodeId = nodes.make_id("some-id")
self.env.app.env.domaindata["std"]["labels"][nodeId] = self.env.docname, nodeId, "Title"
section = nodes.section(ids=[nodeId])
section.append(nodes.title(text="Title"))

Key is the second line of the code above.

Also you want to add the label to the anonlabels to be able to reference it via

:ref:`foo <nodeId>`

like so:

self.env.app.env.domaindata["std"]["anonlabels"][nodeId] = self.env.docname, nodeId
0
natersoz On

I found one way to perform this operation. It likely only works for HTML output:

`Text related to todo-0 <introduction.html#todo-0>`_

This reference link will successfully link to the TODO. All other reference attempts fail. i.e. These all fail:

Note: these syntaxes do not work::

    `introduction.html#todo-1`_

    :ref:`introduction.html#todo-1`

    :ref:`todo-1`