More logical "next"/"previous" links.

This commit is contained in:
Georg Brandl
2008-05-31 22:52:42 +00:00
parent c8668ca498
commit c7110a4199
5 changed files with 67 additions and 26 deletions

View File

@@ -33,6 +33,9 @@ New features added
* Add document metadata to the values in the default template context.
* Let the "previous" and "next" to more logical documents, so that always
selecting "next" lets you visit every document in the tree.
Bugs fixed
----------

View File

@@ -135,6 +135,11 @@ def main(argv=sys.argv):
app.builder.build_update()
except KeyboardInterrupt:
# catches BaseExceptions in 2.5 -- SystemExit, KeyboardInterrupt
if use_pdb:
import pdb
print >>sys.stderr, darkred('Interrupted while building, starting debugger:')
traceback.print_exc()
pdb.post_mortem(sys.exc_info()[2])
return 1
except SystemExit:
return 0

View File

@@ -302,9 +302,6 @@ class StandaloneHTMLBuilder(Builder):
def prepare_writing(self, docnames):
from sphinx.search import IndexBuilder
self.info(bold('creating index...'))
self.env.create_index(self)
self.indexer = IndexBuilder()
self.load_indexer(docnames)
self.docwriter = HTMLWriter(self)
@@ -326,6 +323,8 @@ class StandaloneHTMLBuilder(Builder):
if not isinstance(self.config.html_use_opensearch, basestring):
self.warn('html_use_opensearch config value must now be a string')
self.relations = self.env.collect_relations()
self.globalcontext = dict(
project = self.config.project,
release = self.config.release,
@@ -350,28 +349,29 @@ class StandaloneHTMLBuilder(Builder):
# find out relations
prev = next = None
parents = []
related = self.env.toctree_relations.get(docname)
related = self.relations.get(docname)
titles = self.env.titles
if related:
if related and related[1]:
try:
prev = {'link': self.get_relative_uri(docname, related[1]),
'title': self.render_partial(titles[related[1]])['title']}
except KeyError:
# the relation is (somehow) not in the TOC tree, handle that gracefully
prev = None
if related and related[2]:
try:
next = {'link': self.get_relative_uri(docname, related[2]),
'title': self.render_partial(titles[related[2]])['title']}
except KeyError:
next = None
while related:
while related and related[0]:
try:
parents.append(
{'link': self.get_relative_uri(docname, related[0]),
'title': self.render_partial(titles[related[0]])['title']})
except KeyError:
pass
related = self.env.toctree_relations.get(related[0])
related = self.relations.get(related[0])
if parents:
parents.pop() # remove link to the master file; we have a generic
# "back to index" link already
@@ -420,13 +420,14 @@ class StandaloneHTMLBuilder(Builder):
if self.config.html_use_index:
# the total count of lines for each index letter, used to distribute
# the entries into two columns
genindex = self.env.create_index(self)
indexcounts = []
for _, entries in self.env.index:
for _, entries in genindex:
indexcounts.append(sum(1 + len(subitems)
for _, (_, subitems) in entries))
genindexcontext = dict(
genindexentries = self.env.index,
genindexentries = genindex,
genindexcounts = indexcounts,
)
self.info(' genindex', nonl=1)

View File

@@ -15,10 +15,10 @@ import time
import heapq
import types
import difflib
import itertools
import cPickle as pickle
from os import path
from string import uppercase
from itertools import izip, groupby
try:
import hashlib
md5 = hashlib.md5
@@ -58,7 +58,7 @@ default_settings = {
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
ENV_VERSION = 21
ENV_VERSION = 22
default_substitutions = set([
@@ -225,8 +225,7 @@ class BuildEnvironment:
self.toc_num_entries = {} # docname -> number of real entries
# used to determine when to show the TOC in a sidebar
# (don't show if it's only one item)
self.toctree_relations = {} # docname -> ["parent", "previous", "next"] docname
# for navigating in the toctree
self.toctree_includes = {} # docname -> list of toctree includefiles
self.files_to_rebuild = {} # docname -> set of files (containing its TOCs)
# to rebuild too
@@ -280,11 +279,14 @@ class BuildEnvironment:
self.titles.pop(docname, None)
self.tocs.pop(docname, None)
self.toc_num_entries.pop(docname, None)
self.toctree_includes.pop(docname, None)
self.filemodules.pop(docname, None)
self.indexentries.pop(docname, None)
for subfn, fnset in self.files_to_rebuild.iteritems():
for subfn, fnset in self.files_to_rebuild.items():
fnset.discard(docname)
if not fnset:
del self.files_to_rebuild[subfn]
for fullname, (fn, _) in self.descrefs.items():
if fn == docname:
del self.descrefs[fullname]
@@ -590,17 +592,11 @@ class BuildEnvironment:
"""Note a TOC tree directive in a document and gather information about
file relations from it."""
includefiles = toctreenode['includefiles']
includefiles_len = len(includefiles)
for i, includefile in enumerate(includefiles):
# the "previous" file for the first toctree item is the parent
previous = i > 0 and includefiles[i-1] or docname
# the "next" file for the last toctree item is the parent again
next = i < includefiles_len-1 and includefiles[i+1] or docname
self.toctree_relations[includefile] = [docname, previous, next]
for includefile in includefiles:
# note that if the included file is rebuilt, this one must be
# too (since the TOC of the included file could have changed)
self.files_to_rebuild.setdefault(includefile, set()).add(docname)
self.toctree_includes.setdefault(docname, []).extend(includefiles)
def build_toc_from(self, docname, document):
"""Build a TOC from the doctree and store it in the inventory."""
@@ -998,14 +994,49 @@ class BuildEnvironment:
else:
# get all other symbols under one heading
return 'Symbols'
self.index = [(key, list(group)) for (key, group) in
itertools.groupby(newlist, keyfunc)]
return [(key, list(group)) for (key, group) in groupby(newlist, keyfunc)]
def collect_relations(self):
relations = {}
getinc = self.toctree_includes.get
def collect(parents, docname, previous, next):
includes = getinc(docname)
# previous
if not previous:
previous = parents[0][0]
else:
while 1:
previncs = getinc(previous)
if previncs:
previous = previncs[-1]
else:
break
# next
if includes:
next = includes[0]
elif next:
pass
else:
for parname, parindex in parents:
parincs = getinc(parname)
if parincs and parindex + 1 < len(parincs):
next = parincs[parindex+1]
break
# else it will stay None
# same for children
if includes:
for subindex, args in enumerate(izip(includes, [None] + includes,
includes[1:] + [None])):
collect([(docname, subindex)] + parents, *args)
relations[docname] = [parents[0][0], previous, next]
collect([(None, 0)], self.config.master_doc, None, None)
return relations
def check_consistency(self):
"""Do consistency checks."""
for docname in self.all_docs:
if docname not in self.toctree_relations:
if docname not in self.files_to_rebuild:
if docname == self.config.master_doc:
# the master file is not included anywhere ;)
continue

View File

@@ -184,6 +184,7 @@ def build_hhx(builder, outdir, outname):
f.close()
builder.info('writing index file...')
index = builder.env.create_index(builder)
f = open(path.join(outdir, outname+'.hhk'), 'w')
try:
f.write('<UL>\n')
@@ -199,7 +200,7 @@ def build_hhx(builder, outdir, outname):
for subitem in subitems:
write_index(subitem[0], subitem[1], [])
f.write('</UL>')
for (key, group) in builder.env.index:
for (key, group) in index:
for title, (refs, subitems) in group:
write_index(title, refs, subitems)
f.write('</UL>\n')