diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 660e63ca6..0b2946ae5 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -164,6 +164,10 @@ class StandaloneHTMLBuilder(Builder): ) self.globalcontext.update(self.config.html_context) + def _get_local_toctree(self, docname): + return self.render_partial(self.env.get_toctree_for( + docname, self, self.config.html_collapse_toctree))['fragment'] + def get_doc_context(self, docname, body, metatags): """Collect items for the template context of a page.""" # find out relations @@ -219,8 +223,6 @@ class StandaloneHTMLBuilder(Builder): metatags = metatags, rellinks = rellinks, sourcename = sourcename, - toctree = self.render_partial(self.env.get_toctree_for( - docname, self))['fragment'], toc = self.render_partial(self.env.get_toc_for(docname))['fragment'], # only display a TOC if there's more than one item to show display_toc = (self.env.toc_num_entries[docname] > 1), @@ -474,6 +476,7 @@ class StandaloneHTMLBuilder(Builder): ctx['pathto'] = pathto ctx['hasdoc'] = lambda name: name in self.env.all_docs ctx['customsidebar'] = self.config.html_sidebars.get(pagename) + ctx['toctree'] = self._get_local_toctree(pagename) ctx.update(addctx) self.app.emit('html-page-context', pagename, templatename, ctx, event_arg) diff --git a/sphinx/config.py b/sphinx/config.py index a3de68f5a..24c3e83f1 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -66,6 +66,7 @@ class Config(object): html_use_smartypants = (True, False), html_translator_class = (None, False), html_sidebars = ({}, False), + html_collapse_toctree = (False, False), html_additional_pages = ({}, False), html_use_modindex = (True, False), html_add_permalinks = (True, False), diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index a7cdd9047..ab98ee616 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -14,7 +14,7 @@ from docutils.parsers.rst import directives from sphinx import addnodes from sphinx.locale import pairindextypes -from sphinx.util import patfilter, ws_re, caption_ref_re, docname_join +from sphinx.util import patfilter, ws_re, caption_ref_re, url_re, docname_join from sphinx.util.compat import make_admonition @@ -53,7 +53,7 @@ def toctree_directive(name, arguments, options, content, lineno, docname = docname[:-len(suffix)] # absolutize filenames docname = docname_join(env.docname, docname) - if ref.startswith('http://'): # FIXME: generalize to arbitrary xrefs + if url_re.match(ref): entries.append((title, ref)) elif docname not in env.found_docs: ret.append(state.document.reporter.warning( diff --git a/sphinx/environment.py b/sphinx/environment.py index 4aa32ed08..aa5eede6c 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -44,7 +44,7 @@ from docutils.transforms.parts import ContentsFilter from sphinx import addnodes from sphinx.util import movefile, get_matching_docs, SEP, ustrftime, \ - docname_join, FilenameUniqDict + docname_join, FilenameUniqDict, url_re from sphinx.directives import additional_xref_types default_settings = { @@ -830,12 +830,12 @@ class BuildEnvironment: node['refuri'] = node['anchorname'] return toc - def get_toctree_for(self, docname, builder): + def get_toctree_for(self, docname, builder, collapse): """Return the global TOC nodetree.""" doctree = self.get_doctree(self.config.master_doc) for toctreenode in doctree.traverse(addnodes.toctree): result = self.resolve_toctree(docname, builder, toctreenode, - prune=True) + prune=True, collapse=collapse) if result is not None: return result @@ -909,7 +909,7 @@ class BuildEnvironment: return doctree def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0, - titles_only=False): + titles_only=False, collapse=False): """ Resolve a *toctree* node into individual bullet lists with titles as items, returning None (if no containing titles are found) or @@ -919,6 +919,8 @@ class BuildEnvironment: to the value of the *maxdepth* option on the *toctree* node. If *titles_only* is True, only toplevel document titles will be in the resulting tree. + If *collapse* is True, all branches not containing docname will + be collapsed. """ if toctree.get('hidden', False): return None @@ -935,13 +937,30 @@ class BuildEnvironment: else: _walk_depth(subnode, depth+1, maxdepth) + # cull sub-entries whose parents aren't 'current' + if (collapse and + depth > 1 and + 'current' not in subnode.parent['classes']): + subnode.parent.remove(subnode) + + elif isinstance(subnode, nodes.reference): + # Identify the toc entry pointing to the current document. + if subnode['refuri'] == docname and not subnode['anchorname']: + # tag the whole branch as 'current' + # (We can't use traverse here as 'ascend' un-intuitively + # implies 'siblings'.) + p = subnode + while p: + p['classes'].append('current') + p = p.parent + def _entries_from_toctree(toctreenode, separate=False, subtree=False): """Return TOC entries for a toctree node.""" refs = [(e[0], str(e[1])) for e in toctreenode['entries']] entries = [] for (title, ref) in refs: try: - if ref.startswith('http://'): # FIXME: (see directives/other.py) + if url_re.match(ref): reference = nodes.reference('', '', refuri=ref, anchorname='', *[nodes.Text(title)]) para = addnodes.compact_paragraph('', '', reference) @@ -1001,11 +1020,13 @@ class BuildEnvironment: newnode = addnodes.compact_paragraph('', '', *tocentries) newnode['toctree'] = True + # prune the tree to maxdepth and replace titles, also set level classes _walk_depth(newnode, 1, prune and maxdepth or 0) + # set the target paths in the toctrees (they are not known at TOC generation time) for refnode in newnode.traverse(nodes.reference): - if not refnode['refuri'].startswith('http://'): # FIXME: see above + if not url_re.match(refnode['refuri']): refnode['refuri'] = builder.get_relative_uri( docname, refnode['refuri']) + refnode['anchorname'] return newnode diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index edfb31d8a..6b64483da 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -23,7 +23,7 @@ from os import path # Generally useful regular expressions. ws_re = re.compile(r'\s+') caption_ref_re = re.compile(r'^([^<]+?)\s*<(.+)>$') - +url_re = re.compile(r'(?P.+)://.*') # SEP separates path elements in the canonical file names #