* Allow registering arbitrary cross-referencing directives/roles.

* Allow labels anywhere, and allow giving an explicit caption in :ref: links.
* Some fixes to the sphinxdoc style.
* Add an option to show author information in the output.
* Search user-defined templates in the order they occur in the config
  (thanks Nick).
This commit is contained in:
Georg Brandl 2008-03-09 18:18:41 +00:00
parent f60dfdd231
commit 910e488e39
9 changed files with 197 additions and 60 deletions

View File

@ -25,12 +25,19 @@ class SphinxFileSystemLoader(BaseLoader):
paths, or from an absolute path. paths, or from an absolute path.
""" """
def __init__(self, paths): def __init__(self, basepath, extpaths):
self.searchpaths = map(path.abspath, paths) self.basepath = path.abspath(basepath)
self.extpaths = map(path.abspath, extpaths)
self.searchpaths = self.extpaths + [self.basepath]
def get_source(self, environment, name, parent): def get_source(self, environment, name, parent):
name = name.replace('/', path.sep) name = name.replace('/', path.sep)
if path.isabs(name): if name.startswith('!'):
name = name[1:]
if not path.exists(path.join(self.basepath, name)):
raise TemplateNotFound(name)
filename = path.join(self.basepath, name)
elif path.isabs(name):
if not path.exists(name): if not path.exists(name):
raise TemplateNotFound(name) raise TemplateNotFound(name)
filename = name filename = name

View File

@ -18,8 +18,10 @@ from docutils import nodes
from docutils.parsers.rst import directives, roles from docutils.parsers.rst import directives, roles
import sphinx import sphinx
from sphinx.roles import xfileref_role
from sphinx.config import Config from sphinx.config import Config
from sphinx.builder import builtin_builders from sphinx.builder import builtin_builders
from sphinx.directives import desc_directive, additional_xref_types
from sphinx.util.console import bold from sphinx.util.console import bold
@ -185,3 +187,9 @@ class Sphinx(object):
def add_role(self, name, role): def add_role(self, name, role):
roles.register_canonical_role(name, role) roles.register_canonical_role(name, role)
def add_description_unit(self, directivename, rolename, indexdesc='',
parse_node=None):
additional_xref_types[directivename] = (rolename, indexdesc, parse_node)
directives.register_directive(directivename, desc_directive)
roles.register_canonical_role(rolename, xfileref_role)

View File

@ -79,10 +79,9 @@ class Builder(object):
# load templates # load templates
self.templates = {} self.templates = {}
templates_path = [path.join(path.dirname(__file__), 'templates')] base_templates_path = path.join(path.dirname(__file__), 'templates')
templates_path.extend(self.config.templates_path) loader = SphinxFileSystemLoader(base_templates_path, self.config.templates_path)
templates_path.reverse() self.jinja_env = Environment(loader=loader,
self.jinja_env = Environment(loader=SphinxFileSystemLoader(templates_path),
# disable traceback, more likely that something # disable traceback, more likely that something
# in the application is broken than in the templates # in the application is broken than in the templates
friendly_traceback=False) friendly_traceback=False)
@ -438,13 +437,11 @@ class StandaloneHTMLBuilder(Builder):
# additional pages from conf.py # additional pages from conf.py
for pagename, template in self.config.html_additional_pages.items(): for pagename, template in self.config.html_additional_pages.items():
template = path.join(self.srcdir, template)
self.handle_page(pagename, {}, template) self.handle_page(pagename, {}, template)
# the index page # the index page
indextemplate = self.config.html_index indextemplate = self.config.html_index
if indextemplate: if indextemplate:
indextemplate = path.join(self.srcdir, indextemplate)
self.handle_page('index', {'indextemplate': indextemplate}, 'index.html') self.handle_page('index', {'indextemplate': indextemplate}, 'index.html')
# copy static files # copy static files
@ -515,7 +512,7 @@ class StandaloneHTMLBuilder(Builder):
ctx['hasdoc'] = lambda name: name in self.env.all_docs ctx['hasdoc'] = lambda name: name in self.env.all_docs
sidebarfile = self.config.html_sidebars.get(pagename) sidebarfile = self.config.html_sidebars.get(pagename)
if sidebarfile: if sidebarfile:
ctx['customsidebar'] = path.join(self.srcdir, sidebarfile) ctx['customsidebar'] = sidebarfile
ctx.update(addctx) ctx.update(addctx)
output = self.get_template(templatename).render(ctx) output = self.get_template(templatename).render(ctx)

View File

@ -20,7 +20,7 @@ class Config(object):
# the values are: (default, needs fresh doctrees if changed) # the values are: (default, needs fresh doctrees if changed)
# If you add a value here, don't forget to include it in the # If you add a value here, don't forget to include it in the
# quickstart.py file template as well! # quickstart.py file template as well as in the docs!
config_values = dict( config_values = dict(
# general substitutions # general substitutions
@ -41,6 +41,7 @@ class Config(object):
unused_docs = ([], True), unused_docs = ([], True),
add_function_parentheses = (True, True), add_function_parentheses = (True, True),
add_module_names = (True, True), add_module_names = (True, True),
show_authors = (False, True),
pygments_style = ('sphinx', False), pygments_style = ('sphinx', False),
# HTML options # HTML options

View File

@ -310,21 +310,30 @@ def desc_directive(desctype, arguments, options, content, lineno,
targetname, targetname) targetname, targetname)
env.note_reftarget('option', optname, targetname) env.note_reftarget('option', optname, targetname)
continue continue
elif desctype == 'envvar': elif desctype == 'describe':
signode.clear() signode.clear()
signode += addnodes.desc_name(sig, sig) signode += addnodes.desc_name(sig, sig)
if not noindex:
targetname = 'envvar-' + sig
signode['ids'].append(targetname)
state.document.note_explicit_target(signode)
env.note_index_entry('pair', 'environment variable; %s' % sig,
targetname, targetname)
env.note_reftarget('envvar', sig, targetname)
continue continue
else: else:
# for "describe": use generic fallback # another registered generic x-ref directive
raise ValueError rolename, indextext, parse_node = additional_xref_types[desctype]
if parse_node:
parse_node(sig, signode)
else:
signode.clear()
signode += addnodes.desc_name(sig, sig)
if not noindex:
targetname = '%s-%s' % (rolename, sig)
signode['ids'].append(targetname)
state.document.note_explicit_target(signode)
if indextext:
env.note_index_entry('pair', '%s; %s' % (indextext, sig),
targetname, targetname)
env.note_reftarget(rolename, sig, targetname)
# don't use object indexing below
continue
except ValueError, err: except ValueError, err:
# signature parsing failed
signode.clear() signode.clear()
signode += addnodes.desc_name(sig, sig) signode += addnodes.desc_name(sig, sig)
continue # we don't want an index entry here continue # we don't want an index entry here
@ -384,15 +393,22 @@ desctypes = [
'cvar', 'cvar',
# the odd one # the odd one
'opcode', 'opcode',
# the generic ones # for command line options
'cmdoption', # for command line options 'cmdoption',
'envvar', # for environment variables # the generic one
'describe', 'describe',
'envvar',
] ]
for _name in desctypes: for _name in desctypes:
directives.register_directive(_name, desc_directive) directives.register_directive(_name, desc_directive)
# Generic cross-reference types; they can be registered in the application
additional_xref_types = {
# directive name: (role name, index text)
'envvar': ('envvar', 'environment variable', None),
}
# ------ versionadded/versionchanged ----------------------------------------------- # ------ versionadded/versionchanged -----------------------------------------------
@ -526,8 +542,23 @@ directives.register_directive('module', module_directive)
def author_directive(name, arguments, options, content, lineno, def author_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine): content_offset, block_text, state, state_machine):
# The author directives aren't included in the built document # Show authors only if the show_authors option is on
return [] env = state.document.settings.env
if not env.config.show_authors:
return []
para = nodes.paragraph()
emph = nodes.emphasis()
para += emph
if name == 'sectionauthor':
text = 'Section author: '
elif name == 'moduleauthor':
text = 'Module author: '
else:
text = 'Author: '
emph += nodes.Text(text, text)
inodes, messages = state.inline_text(arguments[0], lineno)
emph.extend(inodes)
return [para] + messages
author_directive.arguments = (1, 0, 1) author_directive.arguments = (1, 0, 1)
directives.register_directive('sectionauthor', author_directive) directives.register_directive('sectionauthor', author_directive)

View File

@ -44,6 +44,7 @@ Body.enum.converters['loweralpha'] = \
from sphinx import addnodes from sphinx import addnodes
from sphinx.util import get_matching_docs, SEP from sphinx.util import get_matching_docs, SEP
from sphinx.directives import additional_xref_types
default_settings = { default_settings = {
'embed_stylesheet': False, 'embed_stylesheet': False,
@ -56,7 +57,7 @@ default_settings = {
# This is increased every time a new environment attribute is added # This is increased every time a new environment attribute is added
# to properly invalidate pickle files. # to properly invalidate pickle files.
ENV_VERSION = 16 ENV_VERSION = 17
def walk_depth(node, depth, maxdepth): def walk_depth(node, depth, maxdepth):
@ -226,6 +227,7 @@ class BuildEnvironment:
self.filemodules = {} # docname -> [modules] self.filemodules = {} # docname -> [modules]
self.modules = {} # modname -> docname, synopsis, platform, deprecated self.modules = {} # modname -> docname, synopsis, platform, deprecated
self.labels = {} # labelname -> docname, labelid, sectionname self.labels = {} # labelname -> docname, labelid, sectionname
self.anonlabels = {} # labelname -> docname, labelid
self.reftargets = {} # (type, name) -> docname, labelid self.reftargets = {} # (type, name) -> docname, labelid
# where type is term, token, option, envvar # where type is term, token, option, envvar
@ -471,13 +473,19 @@ class BuildEnvironment:
continue continue
labelid = document.nameids[name] labelid = document.nameids[name]
node = document.ids[labelid] node = document.ids[labelid]
if not isinstance(node, nodes.section): if name.isdigit() or node.has_key('refuri') or \
# e.g. desc-signatures node.tagname.startswith('desc_'):
# ignore footnote labels, labels automatically generated from a
# link and description units
continue continue
sectname = node[0].astext() # node[0] == title node
if name in self.labels: if name in self.labels:
self.warn(docname, 'duplicate label %s, ' % name + self.warn(docname, 'duplicate label %s, ' % name +
'other instance in %s' % self.doc2path(self.labels[name][0])) 'other instance in %s' % self.doc2path(self.labels[name][0]))
self.anonlabels[name] = docname, labelid
if not isinstance(node, nodes.section):
# anonymous-only labels
continue
sectname = node[0].astext() # node[0] == title node
self.labels[name] = docname, labelid, sectname self.labels[name] = docname, labelid, sectname
def note_toctree(self, docname, toctreenode): def note_toctree(self, docname, toctreenode):
@ -654,23 +662,37 @@ class BuildEnvironment:
typ = node['reftype'] typ = node['reftype']
target = node['reftarget'] target = node['reftarget']
reftarget_roles = set(('token', 'term', 'option'))
# add all custom xref types too
reftarget_roles.update(i[0] for i in additional_xref_types.values())
try: try:
if typ == 'ref': if typ == 'ref':
# reference to the named label; the final node will contain the if node['refcaption']:
# section name after the label # reference to anonymous label; the reference uses the supplied
docname, labelid, sectname = self.labels.get(target, ('','','')) # link caption
if not docname: docname, labelid = self.anonlabels.get(target, ('',''))
newnode = doctree.reporter.system_message( sectname = node.astext()
2, 'undefined label: %s' % target) if not docname:
#self.warn(fromdocname, 'undefined label: %s' % target) newnode = doctree.reporter.system_message(
2, 'undefined label: %s' % target)
else: else:
# reference to the named label; the final node will contain the
# section name after the label
docname, labelid, sectname = self.labels.get(target, ('','',''))
if not docname:
newnode = doctree.reporter.system_message(
2, 'undefined label: %s -- if you don\'t ' % target +
'give a link caption the label must precede a section '
'header.')
if docname:
newnode = nodes.reference('', '') newnode = nodes.reference('', '')
innernode = nodes.emphasis(sectname, sectname) innernode = nodes.emphasis(sectname, sectname)
if docname == fromdocname: if docname == fromdocname:
newnode['refid'] = labelid newnode['refid'] = labelid
else: else:
# set more info in contnode in case the following call # set more info in contnode in case the get_relative_uri call
# raises NoUri, the builder will have to resolve these # raises NoUri, the builder will then have to resolve these
contnode = addnodes.pending_xref('') contnode = addnodes.pending_xref('')
contnode['refdocname'] = docname contnode['refdocname'] = docname
contnode['refsectname'] = sectname contnode['refsectname'] = sectname
@ -693,7 +715,7 @@ class BuildEnvironment:
newnode['refuri'] = builder.get_relative_uri( newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname) + '#' + labelid fromdocname, docname) + '#' + labelid
newnode.append(contnode) newnode.append(contnode)
elif typ in ('token', 'term', 'envvar', 'option'): elif typ in reftarget_roles:
docname, labelid = self.reftargets.get((typ, target), ('', '')) docname, labelid = self.reftargets.get((typ, target), ('', ''))
if not docname: if not docname:
if typ == 'term': if typ == 'term':

View File

@ -78,6 +78,10 @@ today_fmt = '%%B %%d, %%Y'
# unit titles (such as .. function::). # unit titles (such as .. function::).
#add_module_names = True #add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = 'sphinx'
@ -103,14 +107,14 @@ html_last_updated_fmt = '%%b %%d, %%Y'
# typographically correct entities. # typographically correct entities.
#html_use_smartypants = True #html_use_smartypants = True
# Content template for the index page, filename relative to this file. # Content template for the index page.
#html_index = '' #html_index = ''
# Custom sidebar templates, maps page names to filenames relative to this file. # Custom sidebar templates, maps document names to template names.
#html_sidebars = {} #html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to # Additional templates that should be rendered to pages, maps page names to
# filenames relative to this file. # template names.
#html_additional_pages = {} #html_additional_pages = {}
# If true, the reST sources are included in the HTML build as _sources/<name>. # If true, the reST sources are included in the HTML build as _sources/<name>.

View File

@ -17,6 +17,7 @@ from docutils.parsers.rst import roles
from sphinx import addnodes from sphinx import addnodes
ws_re = re.compile(r'\s+') ws_re = re.compile(r'\s+')
caption_ref_re = re.compile(r'^([^<]+?)\s*<(.+)>$')
generic_docroles = { generic_docroles = {
'command' : nodes.strong, 'command' : nodes.strong,
@ -127,6 +128,21 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
pnode['refspecific'] = True pnode['refspecific'] = True
if typ == 'term': if typ == 'term':
pnode['reftarget'] = ws_re.sub(' ', text).lower() pnode['reftarget'] = ws_re.sub(' ', text).lower()
elif typ == 'ref':
brace = text.find('<')
if brace != -1:
pnode['refcaption'] = True
m = caption_ref_re.match(text)
if not m:
# fallback
pnode['reftarget'] = text[brace+1:]
text = text[:brace]
else:
pnode['reftarget'] = m.group(2)
text = m.group(1)
else:
pnode['refcaption'] = False
pnode['reftarget'] = ws_re.sub('', text)
elif typ == 'option': elif typ == 'option':
if text[0] in '-/': if text[0] in '-/':
pnode['reftarget'] = text[1:] pnode['reftarget'] = text[1:]

View File

@ -64,11 +64,24 @@ tt.descname {
tt.descclassname { tt.descclassname {
background-color: transparent; background-color: transparent;
border: 0;
} }
tt.xref, a tt { tt.xref {
background-color: transparent; background-color: transparent;
font-weight: bold; font-weight: bold;
border: 0;
}
a tt {
background-color: transparent;
font-weight: bold;
border: 0;
color: #CA7900;
}
a tt:hover {
color: #2491CF;
} }
dl { dl {
@ -99,20 +112,11 @@ dt:target,
background-color: #fbe54e; background-color: #fbe54e;
} }
/* dl.glossary dt {
dt { font-weight: bold;
margin-top: 0.8em; font-size: 1.1em;
} }
dd p.first {
margin-top: 0;
}
dd p.last {
margin-bottom: 0;
}
*/
pre { pre {
line-height: 120%; line-height: 120%;
} }
@ -122,10 +126,6 @@ pre a {
text-decoration: underline; text-decoration: underline;
} }
div.syntax {
background-color: transparent;
}
div.document { div.document {
background-color: white; background-color: white;
text-align: left; text-align: left;
@ -226,6 +226,10 @@ div.bodywrapper {
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
} }
div.body a {
text-decoration: underline;
}
div.sidebar { div.sidebar {
margin: 0; margin: 0;
padding: 0.5em 15px 15px 0; padding: 0.5em 15px 15px 0;
@ -447,3 +451,50 @@ a.headerlink:hover {
background-color: #ccc; background-color: #ccc;
color: white!important; color: white!important;
} }
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable dl, table.indextable dd {
margin-top: 0;
margin-bottom: 0;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
form.pfform {
margin: 10px 0 20px 0;
}
table.contentstable {
width: 90%;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}