Anonymous avatar Anonymous committed 01bdb46

Add globbing-style toctree entries.

Comments (0)

Files changed (5)

 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.
   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
           
    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
 -------------
   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.

sphinx/directives.py

 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
 
     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)
     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)
 
 

sphinx/static/sphinxdoc.css

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

sphinx/util/__init__.py

 """
 
 import os
+import re
 import sys
 import fnmatch
 import tempfile
 
 
 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):]
     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)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.