Merge with http://bitbucket.org/tpowers/sphinx/ (rewriting the external/internal distinction code)

In HTML output, references now get the class ``internal`` if they are internal to the whole project, as opposed to internal to the current page.

The ``menuselection`` and ``guilabel`` roles now support ampersand accelerators.
This commit is contained in:
Georg Brandl
2010-05-24 12:57:07 +02:00
19 changed files with 141 additions and 47 deletions

View File

@@ -26,6 +26,13 @@ Release 1.0 (in development)
allowing styles to customize their appearance. Domain-specific
roles get two classes, ``domain`` and ``domain-rolename``.
* In HTML output, references now get the class ``internal`` if they
are internal to the whole project, as opposed to internal to the
current page.
* The ``menuselection`` and ``guilabel`` roles now support ampersand
accelerators.
* New more compact doc field syntax is now recognized:
``:param type name: description``.

View File

@@ -200,7 +200,7 @@ General configuration
The name of the default :ref:`domain <domains>`. Can also be ``None`` to
disable a default domain. The default is ``'py'``. Those objects in other
domains (whether the domain name is given explicitly, or selected by a
:dir:`default-domain` directive) will have the domain name explicitly
:rst:dir:`default-domain` directive) will have the domain name explicitly
prepended when named (e.g., when the default domain is C, Python functions
will be named "Python function", not just "function").

View File

@@ -182,6 +182,11 @@ in a different style:
labels, window titles, field names, menu and menu selection names, and even
values in selection lists.
.. versionchanged:: 1.0
An accelerator key for the GUI label can be included using an ampersand;
this will be stripped and displayed underlined in the output (example:
``:guilabel:`&Cancel```). To include a literal ampersand, double it.
.. rst:role:: kbd
Mark a sequence of keystrokes. What form the key sequence takes may depend
@@ -227,6 +232,9 @@ in a different style:
ellipsis some operating systems use to indicate that the command opens a
dialog, the indicator should be omitted from the selection name.
``menuselection`` also supports ampersand accelerators just like
:rst:role:`guilabel`.
.. rst:role:: mimetype
The name of a MIME type, or a component of a MIME type (the major or minor

View File

@@ -105,6 +105,9 @@ These themes are:
doesn't scroll out of view for long body content. This may not work well
with all browsers. Defaults to false.
- **externalrefs** (true or false): Display external links differently from
internal links. Defaults to false.
There are also various color and font options that can change the color scheme
without having to write a custom stylesheet:

View File

@@ -607,7 +607,7 @@ class StandaloneHTMLBuilder(Builder):
# the parent node here.
continue
uri = node['uri']
reference = nodes.reference()
reference = nodes.reference('', '', internal=True)
if uri in self.images:
reference['refuri'] = posixpath.join(self.imgpath,
self.images[uri])

View File

@@ -441,7 +441,7 @@ class StandardDomain(Domain):
# 'precede a section header.', node.line)
if not docname:
return None
newnode = nodes.reference('', '')
newnode = nodes.reference('', '', internal=True)
innernode = nodes.emphasis(sectname, sectname)
if docname == fromdocname:
newnode['refid'] = labelid

View File

@@ -1008,9 +1008,9 @@ class BuildEnvironment:
else:
anchorname = '#' + sectionnode['ids'][0]
numentries[0] += 1
reference = nodes.reference('', '', refuri=docname,
anchorname=anchorname,
*nodetext)
reference = nodes.reference(
'', '', internal=True, refuri=docname,
anchorname=anchorname, *nodetext)
para = addnodes.compact_paragraph('', '', reference)
item = nodes.list_item('', para)
if maxdepth == 0 or depth < maxdepth:
@@ -1148,7 +1148,7 @@ class BuildEnvironment:
for (title, ref) in refs:
try:
if url_re.match(ref):
reference = nodes.reference('', '',
reference = nodes.reference('', '', internal=False,
refuri=ref, anchorname='',
*[nodes.Text(title)])
para = addnodes.compact_paragraph('', '', reference)
@@ -1160,7 +1160,7 @@ class BuildEnvironment:
ref = toctreenode['parent']
if not title:
title = clean_astext(self.titles[ref])
reference = nodes.reference('', '',
reference = nodes.reference('', '', internal=True,
refuri=ref,
anchorname='',
*[nodes.Text(title)])
@@ -1275,7 +1275,7 @@ class BuildEnvironment:
else:
caption = clean_astext(self.titles[docname])
innernode = nodes.emphasis(caption, caption)
newnode = nodes.reference('', '')
newnode = nodes.reference('', '', internal=True)
newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname)
newnode.append(innernode)

View File

@@ -46,7 +46,7 @@ def make_link_role(base_url, prefix):
title = full_url
else:
title = prefix + part
pnode = nodes.reference(title, title, refuri=full_url)
pnode = nodes.reference(title, title, internal=False, refuri=full_url)
return [pnode], []
return role

View File

@@ -201,10 +201,8 @@ def missing_reference(app, env, node, contnode):
if objtype not in inventory or target not in inventory[objtype]:
continue
proj, version, uri, dispname = inventory[objtype][target]
newnode = nodes.reference('', '')
newnode['refuri'] = uri
newnode['reftitle'] = '(in %s v%s)' % (proj, version)
newnode['class'] = 'external-xref'
newnode = nodes.reference('', '', internal=False, refuri=uri,
reftitle='(in %s v%s)' % (proj, version))
if dispname == '-':
newnode.append(contnode)
else:

View File

@@ -113,9 +113,8 @@ def process_todo_nodes(app, doctree, fromdocname):
para += nodes.Text(desc1, desc1)
# Create a reference
newnode = nodes.reference('', '')
newnode = nodes.reference('', '', internal=True)
innernode = nodes.emphasis(_('original entry'), _('original entry'))
newnode['refdocname'] = todo_info['docname']
try:
newnode['refuri'] = app.builder.get_relative_uri(
fromdocname, todo_info['docname'])

View File

@@ -24,7 +24,6 @@ from sphinx.util.nodes import split_explicit_title
generic_docroles = {
'command' : nodes.strong,
'dfn' : nodes.emphasis,
'guilabel' : nodes.strong,
'kbd' : nodes.literal,
'mailheader' : addnodes.literal_emphasis,
'makevar' : nodes.strong,
@@ -40,7 +39,6 @@ for rolename, nodeclass in generic_docroles.iteritems():
role = roles.CustomRole(rolename, generic, {'classes': [rolename]})
roles.register_local_role(rolename, role)
# -- generic cross-reference role ----------------------------------------------
class XRefRole(object):
@@ -184,7 +182,7 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
return [prb], [msg]
ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum
sn = nodes.strong('PEP '+text, 'PEP '+text)
rn = nodes.reference('', '', refuri=ref, classes=[typ])
rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ])
rn += sn
return [indexnode, targetnode, rn], []
elif typ == 'rfc':
@@ -199,17 +197,36 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
return [prb], [msg]
ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
sn = nodes.strong('RFC '+text, 'RFC '+text)
rn = nodes.reference('', '', refuri=ref, classes=[typ])
rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ])
rn += sn
return [indexnode, targetnode, rn], []
def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
return [nodes.emphasis(
rawtext, utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'),
classes=[typ])], []
return role
_amp_re = re.compile(r'(?<!&)&(?![&\s])')
def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
if typ == 'menuselection':
text = utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}')
spans = _amp_re.split(text)
node = nodes.emphasis(rawtext=rawtext)
for i, span in enumerate(spans):
span = span.replace('&&', '&')
if i == 0:
if len(span) > 0:
textnode = nodes.Text(span)
node += textnode
continue
accel_node = nodes.inline()
letter_node = nodes.Text(span[0])
accel_node += letter_node
accel_node['classes'].append('accelerator')
node += accel_node
textnode = nodes.Text(span[1:])
node += textnode
node['classes'].append(typ)
return [node], []
_litvar_re = re.compile('{([^}]+)}')
@@ -249,6 +266,7 @@ specific_docroles = {
'pep': indexmarkup_role,
'rfc': indexmarkup_role,
'guilabel': menusel_role,
'menuselection': menusel_role,
'file': emph_literal_role,
'samp': emph_literal_role,

View File

@@ -368,6 +368,14 @@ dl.glossary dt {
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
/* -- code displays --------------------------------------------------------- */
pre {

View File

@@ -147,7 +147,8 @@ div.sphinxsidebar input {
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
/* -- hyperlink styles ------------------------------------------------------ */
a {
color: {{ theme_linkcolor }};
@@ -163,6 +164,20 @@ a:hover {
text-decoration: underline;
}
{% if theme_externalrefs %}
a.external {
text-decoration: none;
border-bottom: 1px dashed {{ theme_linkcolor }};
}
a.external:hover {
text-decoration: none;
border-bottom: none;
}
{% endif %}
/* -- body styles ----------------------------------------------------------- */
div.body h1,
div.body h2,
div.body h3,

View File

@@ -7,6 +7,8 @@ pygments_style = sphinx
rightsidebar = false
stickysidebar = false
externalrefs = false
footerbgcolor = #11303d
footertextcolor = #ffffff
sidebarbgcolor = #1c4e63

View File

@@ -80,7 +80,7 @@ def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc):
def make_refnode(builder, fromdocname, todocname, targetid, child, title=None):
"""Shortcut to create a reference node."""
node = nodes.reference('', '')
node = nodes.reference('', '', internal=True)
if fromdocname == todocname:
node['refid'] = targetid
else:

View File

@@ -157,14 +157,28 @@ class HTMLTranslator(BaseTranslator):
# overwritten
def visit_reference(self, node):
BaseTranslator.visit_reference(self, node)
if node.hasattr('reftitle'):
# ugly hack to add a title attribute
starttag = self.body[-1]
if not starttag.startswith('<a '):
return
self.body[-1] = '<a title="%s"' % self.attval(node['reftitle']) + \
starttag[2:]
atts = {'class': 'reference'}
if node.get('internal'):
atts['class'] += ' internal'
else:
atts['class'] += ' external'
if 'refuri' in node:
atts['href'] = node['refuri']
if self.settings.cloak_email_addresses and \
atts['href'].startswith('mailto:'):
atts['href'] = self.cloak_mailto(atts['href'])
self.in_mailto = 1
else:
assert 'refid' in node, \
'References must have "refuri" or "refid" attribute.'
atts['href'] = '#' + node['refid']
if not isinstance(node.parent, nodes.TextElement):
assert len(node) == 1 and isinstance(node[0], nodes.image)
atts['class'] += ' image-reference'
if 'reftitle' in node:
atts['title'] = node['reftitle']
self.body.append(self.starttag(node, 'a', '', **atts))
if node.hasattr('secnumber'):
self.body.append(('%s' + self.secnumber_suffix) %
'.'.join(map(str, node['secnumber'])))
@@ -283,8 +297,9 @@ class HTMLTranslator(BaseTranslator):
def visit_download_reference(self, node):
if node.hasattr('filename'):
self.body.append('<a href="%s">' % posixpath.join(
self.builder.dlpath, node['filename']))
self.body.append(
'<a class="reference download internal" href="%s">' %
posixpath.join(self.builder.dlpath, node['filename']))
self.context.append('</a>')
else:
self.context.append('')

View File

@@ -70,6 +70,8 @@ Body directives
b
.. _admonition-section:
Admonitions
^^^^^^^^^^^
@@ -84,6 +86,8 @@ Admonitions
Warning text.
.. _some-label:
.. tip::
Tip text.
@@ -95,7 +99,7 @@ Inline markup
* :command:`command`
* :dfn:`dfn`
* :guilabel:`guilabel`
* :guilabel:`guilabel with &accelerator`
* :kbd:`kbd`
* :mailheader:`mailheader`
* :makevar:`makevar`
@@ -105,6 +109,7 @@ Inline markup
* :program:`program`
* :regexp:`regexp`
* :menuselection:`File --> Close`
* :menuselection:`&File --> &Print`
* :file:`a/{varpart}/b`
* :samp:`print {i}`
@@ -115,6 +120,8 @@ Inline markup
* :envvar:`HOME`
* :keyword:`with`
* :token:`try statement <try_stmt>`
* :ref:`admonition-section`
* :ref:`here <some-label>`
* :doc:`subdir/includes`
* ``:download:`` is tested in includes.txt
* :option:`Python -c option <python -c>`

View File

@@ -104,13 +104,20 @@ HTML_XPATH = {
".//li/tt/span[@class='pre']": '^a/$',
".//li/tt/em/span[@class='pre']": '^varpart$',
".//li/tt/em/span[@class='pre']": '^i$',
".//a[@href='http://www.python.org/dev/peps/pep-0008']/strong": 'PEP 8',
".//a[@href='http://tools.ietf.org/html/rfc1.html']/strong": 'RFC 1',
".//a[@href='objects.html#envvar-HOME']/tt/span[@class='pre']": 'HOME',
".//a[@href='#with']/tt/span[@class='pre']": '^with$',
".//a[@href='#grammar-token-try_stmt']/tt/span": '^statement$',
".//a[@href='subdir/includes.html']/em": 'Including in subdir',
".//a[@href='objects.html#cmdoption-python-c']/em": 'Python -c option',
".//a[@href='http://www.python.org/dev/peps/pep-0008']"
"[@class='pep reference external']/strong": 'PEP 8',
".//a[@href='http://tools.ietf.org/html/rfc1.html']"
"[@class='rfc reference external']/strong": 'RFC 1',
".//a[@href='objects.html#envvar-HOME']"
"[@class='reference internal']/tt/span[@class='pre']": 'HOME',
".//a[@href='#with']"
"[@class='reference internal']/tt/span[@class='pre']": '^with$',
".//a[@href='#grammar-token-try_stmt']"
"[@class='reference internal']/tt/span": '^statement$',
".//a[@href='subdir/includes.html']"
"[@class='reference internal']/em": 'Including in subdir',
".//a[@href='objects.html#cmdoption-python-c']"
"[@class='reference internal']/em": 'Python -c option',
# abbreviations
".//abbr[@title='abbreviation']": '^abbr$',
# version stuff
@@ -139,7 +146,7 @@ HTML_XPATH = {
'objects.html': {
".//dt[@id='mod.Cls.meth1']": '',
".//dt[@id='errmod.Error']": '',
".//a[@href='#mod.Cls']": '',
".//a[@href='#mod.Cls'][@class='reference internal']": '',
".//dl[@class='userdesc']": '',
".//dt[@id='userdesc-myobj']": '',
".//a[@href='#userdesc-myobj']": '',
@@ -168,7 +175,8 @@ HTML_XPATH = {
".//li[@class='toctree-l2']/a": 'Inline markup',
".//title": 'Sphinx <Tests>',
".//div[@class='footer']": 'Georg Brandl & Team',
".//a[@href='http://python.org/']": '',
".//a[@href='http://python.org/']"
"[@class='reference external']": '',
".//li/a[@href='genindex.html']/em": 'Index',
".//li/a[@href='py-modindex.html']/em": 'Module Index',
".//li/a[@href='search.html']/em": 'Search Page',

View File

@@ -97,6 +97,12 @@ def test_inline():
u'<p><em class="menuselection">a \N{TRIANGULAR BULLET} b</em></p>',
'\\emph{a \\(\\rightarrow\\) b}')
# interpolation of ampersands in guilabel/menuselection
yield (verify, ':guilabel:`&Foo -&&- &Bar`',
u'<p><em class="guilabel"><span class="accelerator">F</span>oo '
'-&amp;- <span class="accelerator">B</span>ar</em></p>',
'\\emph{\\DUspan{accelerator}{F}oo -\\&- \\DUspan{accelerator}{B}ar}')
# non-interpolation of dashes in option role
yield (verify_re, ':option:`--with-option`',
'<p><em( class="xref std std-option")?>--with-option</em></p>$',