mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
* 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:
parent
f60dfdd231
commit
910e488e39
@ -25,12 +25,19 @@ class SphinxFileSystemLoader(BaseLoader):
|
||||
paths, or from an absolute path.
|
||||
"""
|
||||
|
||||
def __init__(self, paths):
|
||||
self.searchpaths = map(path.abspath, paths)
|
||||
def __init__(self, basepath, extpaths):
|
||||
self.basepath = path.abspath(basepath)
|
||||
self.extpaths = map(path.abspath, extpaths)
|
||||
self.searchpaths = self.extpaths + [self.basepath]
|
||||
|
||||
def get_source(self, environment, name, parent):
|
||||
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):
|
||||
raise TemplateNotFound(name)
|
||||
filename = name
|
||||
|
@ -18,8 +18,10 @@ from docutils import nodes
|
||||
from docutils.parsers.rst import directives, roles
|
||||
|
||||
import sphinx
|
||||
from sphinx.roles import xfileref_role
|
||||
from sphinx.config import Config
|
||||
from sphinx.builder import builtin_builders
|
||||
from sphinx.directives import desc_directive, additional_xref_types
|
||||
from sphinx.util.console import bold
|
||||
|
||||
|
||||
@ -185,3 +187,9 @@ class Sphinx(object):
|
||||
|
||||
def add_role(self, 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)
|
||||
|
@ -79,10 +79,9 @@ class Builder(object):
|
||||
|
||||
# load templates
|
||||
self.templates = {}
|
||||
templates_path = [path.join(path.dirname(__file__), 'templates')]
|
||||
templates_path.extend(self.config.templates_path)
|
||||
templates_path.reverse()
|
||||
self.jinja_env = Environment(loader=SphinxFileSystemLoader(templates_path),
|
||||
base_templates_path = path.join(path.dirname(__file__), 'templates')
|
||||
loader = SphinxFileSystemLoader(base_templates_path, self.config.templates_path)
|
||||
self.jinja_env = Environment(loader=loader,
|
||||
# disable traceback, more likely that something
|
||||
# in the application is broken than in the templates
|
||||
friendly_traceback=False)
|
||||
@ -438,13 +437,11 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
# additional pages from conf.py
|
||||
for pagename, template in self.config.html_additional_pages.items():
|
||||
template = path.join(self.srcdir, template)
|
||||
self.handle_page(pagename, {}, template)
|
||||
|
||||
# the index page
|
||||
indextemplate = self.config.html_index
|
||||
if indextemplate:
|
||||
indextemplate = path.join(self.srcdir, indextemplate)
|
||||
self.handle_page('index', {'indextemplate': indextemplate}, 'index.html')
|
||||
|
||||
# copy static files
|
||||
@ -515,7 +512,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
ctx['hasdoc'] = lambda name: name in self.env.all_docs
|
||||
sidebarfile = self.config.html_sidebars.get(pagename)
|
||||
if sidebarfile:
|
||||
ctx['customsidebar'] = path.join(self.srcdir, sidebarfile)
|
||||
ctx['customsidebar'] = sidebarfile
|
||||
ctx.update(addctx)
|
||||
|
||||
output = self.get_template(templatename).render(ctx)
|
||||
|
@ -20,7 +20,7 @@ class Config(object):
|
||||
# the values are: (default, needs fresh doctrees if changed)
|
||||
|
||||
# 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(
|
||||
# general substitutions
|
||||
@ -41,6 +41,7 @@ class Config(object):
|
||||
unused_docs = ([], True),
|
||||
add_function_parentheses = (True, True),
|
||||
add_module_names = (True, True),
|
||||
show_authors = (False, True),
|
||||
pygments_style = ('sphinx', False),
|
||||
|
||||
# HTML options
|
||||
|
@ -310,21 +310,30 @@ def desc_directive(desctype, arguments, options, content, lineno,
|
||||
targetname, targetname)
|
||||
env.note_reftarget('option', optname, targetname)
|
||||
continue
|
||||
elif desctype == 'envvar':
|
||||
elif desctype == 'describe':
|
||||
signode.clear()
|
||||
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
|
||||
else:
|
||||
# for "describe": use generic fallback
|
||||
raise ValueError
|
||||
# another registered generic x-ref directive
|
||||
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:
|
||||
# signature parsing failed
|
||||
signode.clear()
|
||||
signode += addnodes.desc_name(sig, sig)
|
||||
continue # we don't want an index entry here
|
||||
@ -384,15 +393,22 @@ desctypes = [
|
||||
'cvar',
|
||||
# the odd one
|
||||
'opcode',
|
||||
# the generic ones
|
||||
'cmdoption', # for command line options
|
||||
'envvar', # for environment variables
|
||||
# for command line options
|
||||
'cmdoption',
|
||||
# the generic one
|
||||
'describe',
|
||||
'envvar',
|
||||
]
|
||||
|
||||
for _name in desctypes:
|
||||
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 -----------------------------------------------
|
||||
|
||||
@ -526,8 +542,23 @@ directives.register_directive('module', module_directive)
|
||||
|
||||
def author_directive(name, arguments, options, content, lineno,
|
||||
content_offset, block_text, state, state_machine):
|
||||
# The author directives aren't included in the built document
|
||||
return []
|
||||
# Show authors only if the show_authors option is on
|
||||
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)
|
||||
directives.register_directive('sectionauthor', author_directive)
|
||||
|
@ -44,6 +44,7 @@ Body.enum.converters['loweralpha'] = \
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.util import get_matching_docs, SEP
|
||||
from sphinx.directives import additional_xref_types
|
||||
|
||||
default_settings = {
|
||||
'embed_stylesheet': False,
|
||||
@ -56,7 +57,7 @@ default_settings = {
|
||||
|
||||
# This is increased every time a new environment attribute is added
|
||||
# to properly invalidate pickle files.
|
||||
ENV_VERSION = 16
|
||||
ENV_VERSION = 17
|
||||
|
||||
|
||||
def walk_depth(node, depth, maxdepth):
|
||||
@ -226,6 +227,7 @@ class BuildEnvironment:
|
||||
self.filemodules = {} # docname -> [modules]
|
||||
self.modules = {} # modname -> docname, synopsis, platform, deprecated
|
||||
self.labels = {} # labelname -> docname, labelid, sectionname
|
||||
self.anonlabels = {} # labelname -> docname, labelid
|
||||
self.reftargets = {} # (type, name) -> docname, labelid
|
||||
# where type is term, token, option, envvar
|
||||
|
||||
@ -471,13 +473,19 @@ class BuildEnvironment:
|
||||
continue
|
||||
labelid = document.nameids[name]
|
||||
node = document.ids[labelid]
|
||||
if not isinstance(node, nodes.section):
|
||||
# e.g. desc-signatures
|
||||
if name.isdigit() or node.has_key('refuri') or \
|
||||
node.tagname.startswith('desc_'):
|
||||
# ignore footnote labels, labels automatically generated from a
|
||||
# link and description units
|
||||
continue
|
||||
sectname = node[0].astext() # node[0] == title node
|
||||
if name in self.labels:
|
||||
self.warn(docname, 'duplicate label %s, ' % name +
|
||||
'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
|
||||
|
||||
def note_toctree(self, docname, toctreenode):
|
||||
@ -654,23 +662,37 @@ class BuildEnvironment:
|
||||
typ = node['reftype']
|
||||
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:
|
||||
if typ == 'ref':
|
||||
# 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' % target)
|
||||
#self.warn(fromdocname, 'undefined label: %s' % target)
|
||||
if node['refcaption']:
|
||||
# reference to anonymous label; the reference uses the supplied
|
||||
# link caption
|
||||
docname, labelid = self.anonlabels.get(target, ('',''))
|
||||
sectname = node.astext()
|
||||
if not docname:
|
||||
newnode = doctree.reporter.system_message(
|
||||
2, 'undefined label: %s' % target)
|
||||
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('', '')
|
||||
innernode = nodes.emphasis(sectname, sectname)
|
||||
if docname == fromdocname:
|
||||
newnode['refid'] = labelid
|
||||
else:
|
||||
# set more info in contnode in case the following call
|
||||
# raises NoUri, the builder will have to resolve these
|
||||
# set more info in contnode in case the get_relative_uri call
|
||||
# raises NoUri, the builder will then have to resolve these
|
||||
contnode = addnodes.pending_xref('')
|
||||
contnode['refdocname'] = docname
|
||||
contnode['refsectname'] = sectname
|
||||
@ -693,7 +715,7 @@ class BuildEnvironment:
|
||||
newnode['refuri'] = builder.get_relative_uri(
|
||||
fromdocname, docname) + '#' + labelid
|
||||
newnode.append(contnode)
|
||||
elif typ in ('token', 'term', 'envvar', 'option'):
|
||||
elif typ in reftarget_roles:
|
||||
docname, labelid = self.reftargets.get((typ, target), ('', ''))
|
||||
if not docname:
|
||||
if typ == 'term':
|
||||
|
@ -78,6 +78,10 @@ today_fmt = '%%B %%d, %%Y'
|
||||
# unit titles (such as .. function::).
|
||||
#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.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
@ -103,14 +107,14 @@ html_last_updated_fmt = '%%b %%d, %%Y'
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Content template for the index page, filename relative to this file.
|
||||
# Content template for the index page.
|
||||
#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 = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# filenames relative to this file.
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If true, the reST sources are included in the HTML build as _sources/<name>.
|
||||
|
@ -17,6 +17,7 @@ from docutils.parsers.rst import roles
|
||||
from sphinx import addnodes
|
||||
|
||||
ws_re = re.compile(r'\s+')
|
||||
caption_ref_re = re.compile(r'^([^<]+?)\s*<(.+)>$')
|
||||
|
||||
generic_docroles = {
|
||||
'command' : nodes.strong,
|
||||
@ -127,6 +128,21 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
pnode['refspecific'] = True
|
||||
if typ == 'term':
|
||||
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':
|
||||
if text[0] in '-/':
|
||||
pnode['reftarget'] = text[1:]
|
||||
|
@ -64,11 +64,24 @@ tt.descname {
|
||||
|
||||
tt.descclassname {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
tt.xref {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
a tt {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
border: 0;
|
||||
color: #CA7900;
|
||||
}
|
||||
|
||||
a tt:hover {
|
||||
color: #2491CF;
|
||||
}
|
||||
|
||||
dl {
|
||||
@ -99,20 +112,11 @@ dt:target,
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
/*
|
||||
dt {
|
||||
margin-top: 0.8em;
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
dd p.first {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
dd p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
*/
|
||||
|
||||
pre {
|
||||
line-height: 120%;
|
||||
}
|
||||
@ -122,10 +126,6 @@ pre a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.syntax {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
div.document {
|
||||
background-color: white;
|
||||
text-align: left;
|
||||
@ -226,6 +226,10 @@ div.bodywrapper {
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.body a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.sidebar {
|
||||
margin: 0;
|
||||
padding: 0.5em 15px 15px 0;
|
||||
@ -447,3 +451,50 @@ a.headerlink:hover {
|
||||
background-color: #ccc;
|
||||
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%;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user