/documentor/libraries/Sphinx-1.1.3-py3.2/sphinx/roles.py
Python | 324 lines | 276 code | 12 blank | 36 comment | 12 complexity | e4b8a9e1983c6da081be737466ab98b5 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0, GPL-2.0
- # -*- coding: utf-8 -*-
- """
- sphinx.roles
- ~~~~~~~~~~~~
- Handlers for additional ReST roles.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
- """
- import re
- import warnings
- from docutils import nodes, utils
- from docutils.parsers.rst import roles
- from sphinx import addnodes
- from sphinx.locale import _
- from sphinx.util import ws_re
- from sphinx.util.nodes import split_explicit_title, process_index_entry, \
- set_role_source_info
- generic_docroles = {
- 'command' : nodes.strong,
- 'dfn' : nodes.emphasis,
- 'kbd' : nodes.literal,
- 'mailheader' : addnodes.literal_emphasis,
- 'makevar' : nodes.strong,
- 'manpage' : addnodes.literal_emphasis,
- 'mimetype' : addnodes.literal_emphasis,
- 'newsgroup' : addnodes.literal_emphasis,
- 'program' : nodes.strong, # XXX should be an x-ref
- 'regexp' : nodes.literal,
- }
- for rolename, nodeclass in generic_docroles.items():
- generic = roles.GenericRole(rolename, nodeclass)
- role = roles.CustomRole(rolename, generic, {'classes': [rolename]})
- roles.register_local_role(rolename, role)
- # -- generic cross-reference role ----------------------------------------------
- class XRefRole(object):
- """
- A generic cross-referencing role. To create a callable that can be used as
- a role function, create an instance of this class.
- The general features of this role are:
- * Automatic creation of a reference and a content node.
- * Optional separation of title and target with `title <target>`.
- * The implementation is a class rather than a function to make
- customization easier.
- Customization can be done in two ways:
- * Supplying constructor parameters:
- * `fix_parens` to normalize parentheses (strip from target, and add to
- title if configured)
- * `lowercase` to lowercase the target
- * `nodeclass` and `innernodeclass` select the node classes for
- the reference and the content node
- * Subclassing and overwriting `process_link()` and/or `result_nodes()`.
- """
- nodeclass = addnodes.pending_xref
- innernodeclass = nodes.literal
- def __init__(self, fix_parens=False, lowercase=False,
- nodeclass=None, innernodeclass=None, warn_dangling=False):
- self.fix_parens = fix_parens
- self.lowercase = lowercase
- self.warn_dangling = warn_dangling
- if nodeclass is not None:
- self.nodeclass = nodeclass
- if innernodeclass is not None:
- self.innernodeclass = innernodeclass
- def _fix_parens(self, env, has_explicit_title, title, target):
- if not has_explicit_title:
- if title.endswith('()'):
- # remove parentheses
- title = title[:-2]
- if env.config.add_function_parentheses:
- # add them back to all occurrences if configured
- title += '()'
- # remove parentheses from the target too
- if target.endswith('()'):
- target = target[:-2]
- return title, target
- def __call__(self, typ, rawtext, text, lineno, inliner,
- options={}, content=[]):
- env = inliner.document.settings.env
- if not typ:
- typ = env.config.default_role
- else:
- typ = typ.lower()
- if ':' not in typ:
- domain, role = '', typ
- classes = ['xref', role]
- else:
- domain, role = typ.split(':', 1)
- classes = ['xref', domain, '%s-%s' % (domain, role)]
- # if the first character is a bang, don't cross-reference at all
- if text[0:1] == '!':
- text = utils.unescape(text)[1:]
- if self.fix_parens:
- text, tgt = self._fix_parens(env, False, text, "")
- innernode = self.innernodeclass(rawtext, text, classes=classes)
- return self.result_nodes(inliner.document, env, innernode,
- is_ref=False)
- # split title and target in role content
- has_explicit_title, title, target = split_explicit_title(text)
- title = utils.unescape(title)
- target = utils.unescape(target)
- # fix-up title and target
- if self.lowercase:
- target = target.lower()
- if self.fix_parens:
- title, target = self._fix_parens(
- env, has_explicit_title, title, target)
- # create the reference node
- refnode = self.nodeclass(rawtext, reftype=role, refdomain=domain,
- refexplicit=has_explicit_title)
- # we may need the line number for warnings
- set_role_source_info(inliner, lineno, refnode)
- title, target = self.process_link(
- env, refnode, has_explicit_title, title, target)
- # now that the target and title are finally determined, set them
- refnode['reftarget'] = target
- refnode += self.innernodeclass(rawtext, title, classes=classes)
- # we also need the source document
- refnode['refdoc'] = env.docname
- refnode['refwarn'] = self.warn_dangling
- # result_nodes allow further modification of return values
- return self.result_nodes(inliner.document, env, refnode, is_ref=True)
- # methods that can be overwritten
- def process_link(self, env, refnode, has_explicit_title, title, target):
- """Called after parsing title and target text, and creating the
- reference node (given in *refnode*). This method can alter the
- reference node and must return a new (or the same) ``(title, target)``
- tuple.
- """
- return title, ws_re.sub(' ', target)
- def result_nodes(self, document, env, node, is_ref):
- """Called before returning the finished nodes. *node* is the reference
- node if one was created (*is_ref* is then true), else the content node.
- This method can add other nodes and must return a ``(nodes, messages)``
- tuple (the usual return value of a role function).
- """
- return [node], []
- def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
- options={}, content=[]):
- """Role for PEP/RFC references that generate an index entry."""
- env = inliner.document.settings.env
- if not typ:
- typ = env.config.default_role
- else:
- typ = typ.lower()
- text = utils.unescape(etext)
- targetid = 'index-%s' % env.new_serialno('index')
- indexnode = addnodes.index()
- targetnode = nodes.target('', '', ids=[targetid])
- inliner.document.note_explicit_target(targetnode)
- if typ == 'pep':
- indexnode['entries'] = [
- ('single', _('Python Enhancement Proposals; PEP %s') % text,
- targetid, '')]
- anchor = ''
- anchorindex = text.find('#')
- if anchorindex > 0:
- text, anchor = text[:anchorindex], text[anchorindex:]
- try:
- pepnum = int(text)
- except ValueError:
- msg = inliner.reporter.error('invalid PEP number %s' % text,
- line=lineno)
- prb = inliner.problematic(rawtext, rawtext, msg)
- return [prb], [msg]
- ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum
- sn = nodes.strong('PEP '+text, 'PEP '+text)
- rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
- classes=[typ])
- rn += sn
- return [indexnode, targetnode, rn], []
- elif typ == 'rfc':
- indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, '')]
- anchor = ''
- anchorindex = text.find('#')
- if anchorindex > 0:
- text, anchor = text[:anchorindex], text[anchorindex:]
- try:
- rfcnum = int(text)
- except ValueError:
- msg = inliner.reporter.error('invalid RFC number %s' % text,
- line=lineno)
- prb = inliner.problematic(rawtext, rawtext, msg)
- return [prb], [msg]
- ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
- sn = nodes.strong('RFC '+text, 'RFC '+text)
- rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
- classes=[typ])
- rn += sn
- return [indexnode, targetnode, rn], []
- _amp_re = re.compile(r'(?<!&)&(?![&\s])')
- def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
- text = utils.unescape(text)
- if typ == 'menuselection':
- text = text.replace('-->', '\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('{([^}]+)}')
- def emph_literal_role(typ, rawtext, text, lineno, inliner,
- options={}, content=[]):
- text = utils.unescape(text)
- pos = 0
- retnode = nodes.literal(role=typ.lower(), classes=[typ])
- for m in _litvar_re.finditer(text):
- if m.start() > pos:
- txt = text[pos:m.start()]
- retnode += nodes.Text(txt, txt)
- retnode += nodes.emphasis(m.group(1), m.group(1))
- pos = m.end()
- if pos < len(text):
- retnode += nodes.Text(text[pos:], text[pos:])
- return [retnode], []
- _abbr_re = re.compile('\((.*)\)$', re.S)
- def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
- text = utils.unescape(text)
- m = _abbr_re.search(text)
- if m is None:
- return [addnodes.abbreviation(text, text)], []
- abbr = text[:m.start()].strip()
- expl = m.group(1)
- return [addnodes.abbreviation(abbr, abbr, explanation=expl)], []
- def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
- # create new reference target
- env = inliner.document.settings.env
- targetid = 'index-%s' % env.new_serialno('index')
- targetnode = nodes.target('', '', ids=[targetid])
- # split text and target in role content
- has_explicit_title, title, target = split_explicit_title(text)
- title = utils.unescape(title)
- target = utils.unescape(target)
- # if an explicit target is given, we can process it as a full entry
- if has_explicit_title:
- entries = process_index_entry(target, targetid)
- # otherwise we just create a "single" entry
- else:
- # but allow giving main entry
- main = ''
- if target.startswith('!'):
- target = target[1:]
- title = title[1:]
- main = 'main'
- entries = [('single', target, targetid, main)]
- indexnode = addnodes.index()
- indexnode['entries'] = entries
- textnode = nodes.Text(title, title)
- return [indexnode, targetnode, textnode], []
- specific_docroles = {
- # links to download references
- 'download': XRefRole(nodeclass=addnodes.download_reference),
- # links to documents
- 'doc': XRefRole(warn_dangling=True),
- 'pep': indexmarkup_role,
- 'rfc': indexmarkup_role,
- 'guilabel': menusel_role,
- 'menuselection': menusel_role,
- 'file': emph_literal_role,
- 'samp': emph_literal_role,
- 'abbr': abbr_role,
- 'index': index_role,
- }
- for rolename, func in specific_docroles.items():
- roles.register_local_role(rolename, func)
- # backwards compatibility alias
- def xfileref_role(*args, **kwds):
- warnings.warn('xfileref_role is deprecated, use XRefRole',
- DeprecationWarning, stacklevel=2)
- return XRefRole()(*args, **kwds)