Add globbing-style toctree entries.

This commit is contained in:
Georg Brandl 2008-05-04 16:57:15 +00:00
parent 407a0c47c1
commit 548cdc858a
5 changed files with 134 additions and 19 deletions

View File

@ -4,9 +4,11 @@ Changes in trunk
New features added New features added
------------------ ------------------
* The ``toctree`` directive now supports a ``glob`` option that allows
glob-style entries in the content.
* If the `pygments_style` config value contains a dot it's treated as the * If the `pygments_style` config value contains a dot it's treated as the
import path of a custom Pygments style class. import path of a custom Pygments style class.
* autodoc detects descriptors properly now
* A new config value, `exclude_dirs`, can be used to exclude whole * A new config value, `exclude_dirs`, can be used to exclude whole
directories from the search for source files. directories from the search for source files.
@ -36,6 +38,8 @@ Bugs fixed
and index file. Remove two remaining instances of hard-coded and index file. Remove two remaining instances of hard-coded
"documentation". "documentation".
* sphinx.ext.autodoc: descriptors are detected properly now.
* Lots of little fixes to the LaTeX output and style. * Lots of little fixes to the LaTeX output and style.
* Fix OpenSearch template and make template URL absolute. The * Fix OpenSearch template and make template URL absolute. The

View File

@ -70,18 +70,36 @@ tables of contents. The ``toctree`` directive is the central element.
The second line above will link to the ``strings`` document, but will use the The second line above will link to the ``strings`` document, but will use the
title "All about strings" instead of the title of the ``strings`` document. title "All about strings" instead of the title of the ``strings`` document.
You can use "globbing" in toctree directives, by giving the ``glob`` flag
option. All entries are then matched against the list of available
documents, and matches are inserted into the list alphabetically. Example::
.. toctree::
:glob:
intro*
recipe/*
*
This includes first all documents whose names start with ``intro``, then all
documents in the ``recipe`` folder, then all remaining documents (except the
one containing the directive, of course.) [#]_
In the end, all documents in the :term:`source directory` (or subdirectories) In the end, all documents in the :term:`source directory` (or subdirectories)
must occur in some ``toctree`` directive; Sphinx will emit a warning if it must occur in some ``toctree`` directive; Sphinx will emit a warning if it
finds a file that is not included, because that means that this file will not finds a file that is not included, because that means that this file will not
be reachable through standard navigation. Use :confval:`unused_documents` to be reachable through standard navigation. Use :confval:`unused_documents` to
explicitly exclude documents from this check, and :confval:`exclude_dirs` to explicitly exclude documents from building, and :confval:`exclude_dirs` to
exclude whole directories. exclude whole directories.
The "master document" (selected by :confval:`master_doc`) is the "root" of The "master document" (selected by :confval:`master_doc`) is the "root" of
the TOC tree hierarchy. It can be used as the documentation's main page, or the TOC tree hierarchy. It can be used as the documentation's main page, or
as a "full table of contents" if you don't give a ``maxdepth`` option. as a "full table of contents" if you don't give a ``maxdepth`` option.
.. versionchanged:: 0.2.1
Added "globbing" option.
Special names Special names
------------- -------------
@ -110,3 +128,11 @@ The special document names (and pages generated for them) are:
Though only few such names are currently used by Sphinx, you should not create Though only few such names are currently used by Sphinx, you should not create
documents or document-containing directories with such names. (Using ``_`` as documents or document-containing directories with such names. (Using ``_`` as
a prefix for a custom template directory is fine.) a prefix for a custom template directory is fine.)
.. rubric:: Footnotes
.. [#] A note on available globbing syntax: you can use the standard shell
constructs ``*``, ``?``, ``[...]`` and ``[!...]`` with the feature that
these all don't match slashes. A double star ``**`` can be used to match
any sequence of characters *including* slashes.

View File

@ -19,6 +19,7 @@ from docutils import nodes
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.util import patfilter
from sphinx.roles import caption_ref_re from sphinx.roles import caption_ref_re
from sphinx.util.compat import make_admonition from sphinx.util.compat import make_admonition
@ -650,28 +651,46 @@ def toctree_directive(name, arguments, options, content, lineno,
env = state.document.settings.env env = state.document.settings.env
suffix = env.config.source_suffix suffix = env.config.source_suffix
dirname = posixpath.dirname(env.docname) dirname = posixpath.dirname(env.docname)
glob = 'glob' in options
ret = [] ret = []
subnode = addnodes.toctree() subnode = addnodes.toctree()
includefiles = [] includefiles = []
includetitles = {} includetitles = {}
for docname in content: all_docnames = env.found_docs.copy()
if not docname: # don't add the currently visited file in catch-all patterns
all_docnames.remove(env.docname)
for entry in content:
if not entry:
continue continue
# look for explicit titles and documents ("Some Title <document>"). if not glob:
m = caption_ref_re.match(docname) # look for explicit titles and documents ("Some Title <document>").
if m: m = caption_ref_re.match(entry)
docname = m.group(2) if m:
includetitles[docname] = m.group(1) docname = m.group(2)
# absolutize filenames, remove suffixes includetitles[docname] = m.group(1)
if docname.endswith(suffix): else:
docname = docname[:-len(suffix)] docname = entry
docname = posixpath.normpath(posixpath.join(dirname, docname)) # remove suffixes (backwards compatibility)
if docname not in env.found_docs: if docname.endswith(suffix):
ret.append(state.document.reporter.warning( docname = docname[:-len(suffix)]
'toctree references unknown document %r' % docname, line=lineno)) # absolutize filenames
docname = posixpath.normpath(posixpath.join(dirname, docname))
if docname not in env.found_docs:
ret.append(state.document.reporter.warning(
'toctree references unknown document %r' % docname, line=lineno))
else:
includefiles.append(docname)
else: else:
includefiles.append(docname) patname = posixpath.normpath(posixpath.join(dirname, entry))
docnames = sorted(patfilter(all_docnames, patname))
for docname in docnames:
all_docnames.remove(docname) # don't include it again
includefiles.append(docname)
if not docnames:
ret.append(state.document.reporter.warning(
'toctree glob pattern %r didn\'t match any documents' % entry,
line=lineno))
subnode['includefiles'] = includefiles subnode['includefiles'] = includefiles
subnode['includetitles'] = includetitles subnode['includetitles'] = includetitles
subnode['maxdepth'] = options.get('maxdepth', -1) subnode['maxdepth'] = options.get('maxdepth', -1)
@ -679,7 +698,7 @@ def toctree_directive(name, arguments, options, content, lineno,
return ret return ret
toctree_directive.content = 1 toctree_directive.content = 1
toctree_directive.options = {'maxdepth': int} toctree_directive.options = {'maxdepth': int, 'glob': directives.flag}
directives.register_directive('toctree', toctree_directive) directives.register_directive('toctree', toctree_directive)

View File

@ -242,6 +242,10 @@ p {
margin: 0.8em 0 0.5em 0; margin: 0.8em 0 0.5em 0;
} }
p.rubric {
font-weight: bold;
}
h1 { h1 {
margin: 0; margin: 0;
padding: 0.7em 0 0.3em 0; padding: 0.7em 0 0.3em 0;

View File

@ -10,6 +10,7 @@
""" """
import os import os
import re
import sys import sys
import fnmatch import fnmatch
import tempfile import tempfile
@ -158,7 +159,7 @@ def fmt_ex(ex):
def rpartition(s, t): def rpartition(s, t):
"""Similar to str.rpartition from 2.5.""" """Similar to str.rpartition from 2.5, but doesn't return the separator."""
i = s.rfind(t) i = s.rfind(t)
if i != -1: if i != -1:
return s[:i], s[i+len(t):] return s[:i], s[i+len(t):]
@ -187,3 +188,64 @@ def save_traceback():
os.write(fd, exc) os.write(fd, exc)
os.close(fd) os.close(fd)
return path return path
def _translate_pattern(pat):
"""
Translate a shell-style glob pattern to a regular expression.
Adapted from the fnmatch module, but enhanced so that single stars don't
match slashes.
"""
i, n = 0, len(pat)
res = ''
while i < n:
c = pat[i]
i += 1
if c == '*':
if i < n and pat[i] == '*':
# double star matches slashes too
i += 1
res = res + '.*'
else:
# single star doesn't match slashes
res = res + '[^/]*'
elif c == '?':
# question mark doesn't match slashes too
res = res + '[^/]'
elif c == '[':
j = i
if j < n and pat[j] == '!':
j += 1
if j < n and pat[j] == ']':
j += 1
while j < n and pat[j] != ']':
j += 1
if j >= n:
res = res + '\\['
else:
stuff = pat[i:j].replace('\\', '\\\\')
i = j + 1
if stuff[0] == '!':
# negative pattern mustn't match slashes too
stuff = '^/' + stuff[1:]
elif stuff[0] == '^':
stuff = '\\' + stuff
res = '%s[%s]' % (res, stuff)
else:
res += re.escape(c)
return res + '$'
_pat_cache = {}
def patfilter(names, pat):
"""
Return the subset of the list NAMES that match PAT.
Adapted from fnmatch module.
"""
result = []
if not pat in _pat_cache:
_pat_cache[pat] = re.compile(_translate_pattern(pat))
match = _pat_cache[pat].match
return filter(match, names)