mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add globbing-style toctree entries.
This commit is contained in:
parent
407a0c47c1
commit
548cdc858a
6
CHANGES
6
CHANGES
@ -4,9 +4,11 @@ Changes in trunk
|
||||
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
|
||||
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
|
||||
directories from the search for source files.
|
||||
@ -36,6 +38,8 @@ Bugs fixed
|
||||
and index file. Remove two remaining instances of hard-coded
|
||||
"documentation".
|
||||
|
||||
* sphinx.ext.autodoc: descriptors are detected properly now.
|
||||
|
||||
* Lots of little fixes to the LaTeX output and style.
|
||||
|
||||
* Fix OpenSearch template and make template URL absolute. The
|
||||
|
@ -71,17 +71,35 @@ tables of contents. The ``toctree`` directive is the central element.
|
||||
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.
|
||||
|
||||
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)
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
as a "full table of contents" if you don't give a ``maxdepth`` option.
|
||||
|
||||
.. versionchanged:: 0.2.1
|
||||
Added "globbing" option.
|
||||
|
||||
|
||||
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
|
||||
documents or document-containing directories with such names. (Using ``_`` as
|
||||
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.
|
||||
|
@ -19,6 +19,7 @@ from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.util import patfilter
|
||||
from sphinx.roles import caption_ref_re
|
||||
from sphinx.util.compat import make_admonition
|
||||
|
||||
@ -650,28 +651,46 @@ def toctree_directive(name, arguments, options, content, lineno,
|
||||
env = state.document.settings.env
|
||||
suffix = env.config.source_suffix
|
||||
dirname = posixpath.dirname(env.docname)
|
||||
glob = 'glob' in options
|
||||
|
||||
ret = []
|
||||
subnode = addnodes.toctree()
|
||||
includefiles = []
|
||||
includetitles = {}
|
||||
for docname in content:
|
||||
if not docname:
|
||||
all_docnames = env.found_docs.copy()
|
||||
# don't add the currently visited file in catch-all patterns
|
||||
all_docnames.remove(env.docname)
|
||||
for entry in content:
|
||||
if not entry:
|
||||
continue
|
||||
# look for explicit titles and documents ("Some Title <document>").
|
||||
m = caption_ref_re.match(docname)
|
||||
if m:
|
||||
docname = m.group(2)
|
||||
includetitles[docname] = m.group(1)
|
||||
# absolutize filenames, remove suffixes
|
||||
if docname.endswith(suffix):
|
||||
docname = docname[:-len(suffix)]
|
||||
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))
|
||||
if not glob:
|
||||
# look for explicit titles and documents ("Some Title <document>").
|
||||
m = caption_ref_re.match(entry)
|
||||
if m:
|
||||
docname = m.group(2)
|
||||
includetitles[docname] = m.group(1)
|
||||
else:
|
||||
docname = entry
|
||||
# remove suffixes (backwards compatibility)
|
||||
if docname.endswith(suffix):
|
||||
docname = docname[:-len(suffix)]
|
||||
# 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:
|
||||
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['includetitles'] = includetitles
|
||||
subnode['maxdepth'] = options.get('maxdepth', -1)
|
||||
@ -679,7 +698,7 @@ def toctree_directive(name, arguments, options, content, lineno,
|
||||
return ret
|
||||
|
||||
toctree_directive.content = 1
|
||||
toctree_directive.options = {'maxdepth': int}
|
||||
toctree_directive.options = {'maxdepth': int, 'glob': directives.flag}
|
||||
directives.register_directive('toctree', toctree_directive)
|
||||
|
||||
|
||||
|
@ -242,6 +242,10 @@ p {
|
||||
margin: 0.8em 0 0.5em 0;
|
||||
}
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 0.7em 0 0.3em 0;
|
||||
|
@ -10,6 +10,7 @@
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import fnmatch
|
||||
import tempfile
|
||||
@ -158,7 +159,7 @@ def fmt_ex(ex):
|
||||
|
||||
|
||||
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)
|
||||
if i != -1:
|
||||
return s[:i], s[i+len(t):]
|
||||
@ -187,3 +188,64 @@ def save_traceback():
|
||||
os.write(fd, exc)
|
||||
os.close(fd)
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user