sphinx-doc / sphinx

The Sphinx documentation generator
https://www.sphinx-doc.org/
Other
6.64k stars 2.13k forks source link

DRY: refactor common functionality in generating index nodes #2914

Open jason-s opened 8 years ago

jason-s commented 8 years ago

I am trying to write a simple extension to automatically add some index nodes. Unfortunately there doesn't seem to be any base function to do that. I found three places where the same essential stuff is being used to add index nodes to the toctree:

So it looks like I have to copy the appropriate implementation pieces of this (add a target node, add an index node, configure each of them).

It would be really useful to refactor the common functionality into a single, simple function that extensions could use.

tk0miya commented 8 years ago

Agreed. PR is always welcome :-)

jason-s commented 8 years ago

I would but I can't figure it out. Ignore the print statements below, this is the best I can get, but I can't seem to add index nodes programmatically via something in app.connect('doctree-read', handler) or doctree-resolved

def create_index(document, entries, indexname='index', targetid = None):
    env = document.settings.env
    if targetid is None:
        targetid = indexname + '-%s'
    targetid = targetid % (env.new_serialno(indexname))
    print "GOT TARGET", targetid
    targetnode = docutils.nodes.target('', '', refid=targetid)
    print targetnode
    document.note_explicit_target(targetnode)
    indexnode = addnodes.index()
    indexnode['entries'] = [(ty, content, targetid, '', None) for ty, content in entries]
    indexnode['inline'] = False
    return [indexnode, targetnode]
jason-s commented 8 years ago

ah -- I got it to work finally, I had to add a call to environment.note_indexentries_from. Latest source code:

def create_index(document, entries, indexname='index', targetid = None):
    """
    Programmatically create an index node in a doctree.

    :param document: doctree of an entire document
    :param entries: a list of tuples (type, entryname)
                    where type is ``single``, ``pair``, or ``triple`` and entryname
                    is the name of the index text
                    (see http://www.sphinx-doc.org/en/stable/markup/misc.html#directive-index)
    :param indexname: name of the index container, useful only for custom indices
    :param targetid: format string for the index target id (e.g. ``index-%s``),
                     ``%s`` automatically gets replaced by a serial number counter
    :return: a tuple (nodes, targetid) where nodes is the list of nodes generated
             that can be added to the doctree, and targetid is the final
             index target id used in these nodes.

    :Example:

                nodes, targetid = create_index(doctree,
                    [('single', u'abcdef'),
                     ('single', u'ghijkl')]
                    )
                nodes += [docutils.nodes.inline('','',ids=[targetid])]
                # add nodes to doctree here #
                doctree.settings.env.note_indexentries_from(doctree.settings.env.docname, doctree)

    .. note:: The call to ``note_indexentries_from`` extracts all the index entries
              from the doctree. If using this function from the doctree-read or
              doctree-resolved event handlers, it appears as though the
              ``note_indexentries_from`` function has already been called automatically
              and will need to be called manually, in order to pick up any new
              index nodes.
    """
    env = document.settings.env
    if targetid is None:
        targetid = indexname + '-%s'
    targetid = targetid % (env.new_serialno(indexname))
    targetnode = docutils.nodes.target('', '', refid=targetid)
    document.note_explicit_target(targetnode)
    indexnode = addnodes.index()
    indexnode['entries'] = [(ty, content, targetid, '', None) for ty, content in entries]
    indexnode['inline'] = False
    return [indexnode, targetnode], targetid
jason-s commented 8 years ago

This kind of thing is really hard to do via UTSL -- there are a whole bunch of interacting Python classes which appear to be working together in a really brittle way, so I can never be sure whether what I'm doing is actually the correct way to do it.

tk0miya commented 8 years ago

Agreed. It is very hard to understand. I don't understand how index works. So we need to refactor codes and improve docs. Please send us your code as a PR. I will take a look. (Sorry I really don't know about index. so I can't comment to your code immediately)