mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Convert builtin directives to classes. Refactor desc_directive so that it is easier to subclass.
This commit is contained in:
parent
8bd4e8e398
commit
044d42953d
@ -57,8 +57,7 @@ import sphinx
|
|||||||
from sphinx.roles import xfileref_role, innernodetypes
|
from sphinx.roles import xfileref_role, innernodetypes
|
||||||
from sphinx.config import Config
|
from sphinx.config import Config
|
||||||
from sphinx.builders import BUILTIN_BUILDERS
|
from sphinx.builders import BUILTIN_BUILDERS
|
||||||
from sphinx.directives import desc_directive, target_directive, \
|
from sphinx.directives import GenericDesc, Target, additional_xref_types
|
||||||
additional_xref_types
|
|
||||||
from sphinx.environment import SphinxStandaloneReader
|
from sphinx.environment import SphinxStandaloneReader
|
||||||
from sphinx.util.compat import Directive, directive_dwim
|
from sphinx.util.compat import Directive, directive_dwim
|
||||||
from sphinx.util.console import bold
|
from sphinx.util.console import bold
|
||||||
@ -314,7 +313,7 @@ class Sphinx(object):
|
|||||||
parse_node=None, ref_nodeclass=None):
|
parse_node=None, ref_nodeclass=None):
|
||||||
additional_xref_types[directivename] = (rolename, indextemplate,
|
additional_xref_types[directivename] = (rolename, indextemplate,
|
||||||
parse_node)
|
parse_node)
|
||||||
directives.register_directive(directivename, desc_directive)
|
directives.register_directive(directivename, GenericDesc)
|
||||||
roles.register_canonical_role(rolename, xfileref_role)
|
roles.register_canonical_role(rolename, xfileref_role)
|
||||||
if ref_nodeclass is not None:
|
if ref_nodeclass is not None:
|
||||||
innernodetypes[rolename] = ref_nodeclass
|
innernodetypes[rolename] = ref_nodeclass
|
||||||
@ -322,7 +321,7 @@ class Sphinx(object):
|
|||||||
def add_crossref_type(self, directivename, rolename, indextemplate='',
|
def add_crossref_type(self, directivename, rolename, indextemplate='',
|
||||||
ref_nodeclass=None):
|
ref_nodeclass=None):
|
||||||
additional_xref_types[directivename] = (rolename, indextemplate, None)
|
additional_xref_types[directivename] = (rolename, indextemplate, None)
|
||||||
directives.register_directive(directivename, target_directive)
|
directives.register_directive(directivename, Target)
|
||||||
roles.register_canonical_role(rolename, xfileref_role)
|
roles.register_canonical_role(rolename, xfileref_role)
|
||||||
if ref_nodeclass is not None:
|
if ref_nodeclass is not None:
|
||||||
innernodetypes[rolename] = ref_nodeclass
|
innernodetypes[rolename] = ref_nodeclass
|
||||||
|
@ -16,103 +16,133 @@ from docutils.parsers.rst import directives
|
|||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.util import parselinenos
|
from sphinx.util import parselinenos
|
||||||
|
from sphinx.util.compat import Directive
|
||||||
|
|
||||||
|
|
||||||
# ------ highlight directive ---------------------------------------------------
|
class Highlight(Directive):
|
||||||
|
"""
|
||||||
|
Directive to set the highlighting language for code blocks, as well
|
||||||
|
as the threshold for line numbers.
|
||||||
|
"""
|
||||||
|
|
||||||
def highlightlang_directive(name, arguments, options, content, lineno,
|
has_content = False
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 1
|
||||||
if 'linenothreshold' in options:
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {
|
||||||
|
'linenothreshold': directives.unchanged,
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if 'linenothreshold' in self.options:
|
||||||
try:
|
try:
|
||||||
linenothreshold = int(options['linenothreshold'])
|
linenothreshold = int(self.options['linenothreshold'])
|
||||||
except Exception:
|
except Exception:
|
||||||
linenothreshold = 10
|
linenothreshold = 10
|
||||||
else:
|
else:
|
||||||
linenothreshold = sys.maxint
|
linenothreshold = sys.maxint
|
||||||
return [addnodes.highlightlang(lang=arguments[0].strip(),
|
return [addnodes.highlightlang(lang=self.arguments[0].strip(),
|
||||||
linenothreshold=linenothreshold)]
|
linenothreshold=linenothreshold)]
|
||||||
|
|
||||||
highlightlang_directive.content = 0
|
|
||||||
highlightlang_directive.arguments = (1, 0, 0)
|
|
||||||
highlightlang_directive.options = {'linenothreshold': directives.unchanged}
|
|
||||||
directives.register_directive('highlight', highlightlang_directive)
|
|
||||||
# old name
|
|
||||||
directives.register_directive('highlightlang', highlightlang_directive)
|
|
||||||
|
|
||||||
|
class CodeBlock(Directive):
|
||||||
|
"""
|
||||||
|
Directive for a code block with special highlighting or line numbering
|
||||||
|
settings.
|
||||||
|
"""
|
||||||
|
|
||||||
# ------ code-block directive --------------------------------------------------
|
has_content = True
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {
|
||||||
|
'linenos': directives.flag,
|
||||||
|
}
|
||||||
|
|
||||||
def codeblock_directive(name, arguments, options, content, lineno,
|
def run(self):
|
||||||
content_offset, block_text, state, state_machine):
|
code = u'\n'.join(self.content)
|
||||||
code = u'\n'.join(content)
|
|
||||||
literal = nodes.literal_block(code, code)
|
literal = nodes.literal_block(code, code)
|
||||||
literal['language'] = arguments[0]
|
literal['language'] = self.arguments[0]
|
||||||
literal['linenos'] = 'linenos' in options
|
literal['linenos'] = 'linenos' in self.options
|
||||||
return [literal]
|
return [literal]
|
||||||
|
|
||||||
codeblock_directive.content = 1
|
|
||||||
codeblock_directive.arguments = (1, 0, 0)
|
|
||||||
codeblock_directive.options = {'linenos': directives.flag}
|
|
||||||
directives.register_directive('code-block', codeblock_directive)
|
|
||||||
directives.register_directive('sourcecode', codeblock_directive)
|
|
||||||
|
|
||||||
|
class LiteralInclude(Directive):
|
||||||
|
"""
|
||||||
|
Like ``.. include:: :literal:``, but only warns if the include file is
|
||||||
|
not found, and does not raise errors. Also has several options for
|
||||||
|
selecting what to include.
|
||||||
|
"""
|
||||||
|
|
||||||
# ------ literalinclude directive ----------------------------------------------
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {
|
||||||
|
'linenos': directives.flag,
|
||||||
|
'language': directives.unchanged_required,
|
||||||
|
'encoding': directives.encoding,
|
||||||
|
'pyobject': directives.unchanged_required,
|
||||||
|
'lines': directives.unchanged_required,
|
||||||
|
'start-after': directives.unchanged_required,
|
||||||
|
'end-before': directives.unchanged_required,
|
||||||
|
}
|
||||||
|
|
||||||
def literalinclude_directive(name, arguments, options, content, lineno,
|
def run(self):
|
||||||
content_offset, block_text, state, state_machine):
|
document = self.state.document
|
||||||
"""Like .. include:: :literal:, but only warns if the include file is
|
filename = self.arguments[0]
|
||||||
not found."""
|
if not document.settings.file_insertion_enabled:
|
||||||
if not state.document.settings.file_insertion_enabled:
|
return [document.reporter.warning('File insertion disabled',
|
||||||
return [state.document.reporter.warning('File insertion disabled',
|
line=self.lineno)]
|
||||||
line=lineno)]
|
env = document.settings.env
|
||||||
env = state.document.settings.env
|
rel_fn = filename
|
||||||
rel_fn = arguments[0]
|
sourcename = self.state_machine.input_lines.source(
|
||||||
source_dir = path.dirname(path.abspath(state_machine.input_lines.source(
|
self.lineno - self.state_machine.input_offset - 1)
|
||||||
lineno - state_machine.input_offset - 1)))
|
source_dir = path.dirname(path.abspath(sourcename))
|
||||||
fn = path.normpath(path.join(source_dir, rel_fn))
|
fn = path.normpath(path.join(source_dir, rel_fn))
|
||||||
|
|
||||||
if 'pyobject' in options and 'lines' in options:
|
if 'pyobject' in self.options and 'lines' in self.options:
|
||||||
return [state.document.reporter.warning(
|
return [document.reporter.warning(
|
||||||
'Cannot use both "pyobject" and "lines" options', line=lineno)]
|
'Cannot use both "pyobject" and "lines" options',
|
||||||
|
line=self.lineno)]
|
||||||
|
|
||||||
encoding = options.get('encoding', env.config.source_encoding)
|
encoding = self.options.get('encoding', env.config.source_encoding)
|
||||||
try:
|
try:
|
||||||
f = codecs.open(fn, 'rU', encoding)
|
f = codecs.open(fn, 'rU', encoding)
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
f.close()
|
f.close()
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
return [state.document.reporter.warning(
|
return [document.reporter.warning(
|
||||||
'Include file %r not found or reading it failed' % arguments[0],
|
'Include file %r not found or reading it failed' % filename,
|
||||||
line=lineno)]
|
line=self.lineno)]
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
return [state.document.reporter.warning(
|
return [document.reporter.warning(
|
||||||
'Encoding %r used for reading included file %r seems to '
|
'Encoding %r used for reading included file %r seems to '
|
||||||
'be wrong, try giving an :encoding: option' %
|
'be wrong, try giving an :encoding: option' %
|
||||||
(encoding, arguments[0]))]
|
(encoding, filename))]
|
||||||
|
|
||||||
objectname = options.get('pyobject')
|
objectname = self.options.get('pyobject')
|
||||||
if objectname is not None:
|
if objectname is not None:
|
||||||
from sphinx.pycode import ModuleAnalyzer
|
from sphinx.pycode import ModuleAnalyzer
|
||||||
analyzer = ModuleAnalyzer.for_file(fn, '')
|
analyzer = ModuleAnalyzer.for_file(fn, '')
|
||||||
tags = analyzer.find_tags()
|
tags = analyzer.find_tags()
|
||||||
if objectname not in tags:
|
if objectname not in tags:
|
||||||
return [state.document.reporter.warning(
|
return [document.reporter.warning(
|
||||||
'Object named %r not found in include file %r' %
|
'Object named %r not found in include file %r' %
|
||||||
(objectname, arguments[0]), line=lineno)]
|
(objectname, filename), line=self.lineno)]
|
||||||
else:
|
else:
|
||||||
lines = lines[tags[objectname][1] - 1 : tags[objectname][2] - 1]
|
lines = lines[tags[objectname][1]-1 : tags[objectname][2]-1]
|
||||||
|
|
||||||
linespec = options.get('lines')
|
linespec = self.options.get('lines')
|
||||||
if linespec is not None:
|
if linespec is not None:
|
||||||
try:
|
try:
|
||||||
linelist = parselinenos(linespec, len(lines))
|
linelist = parselinenos(linespec, len(lines))
|
||||||
except ValueError, err:
|
except ValueError, err:
|
||||||
return [state.document.reporter.warning(str(err), line=lineno)]
|
return [document.reporter.warning(str(err), line=self.lineno)]
|
||||||
lines = [lines[i] for i in linelist]
|
lines = [lines[i] for i in linelist]
|
||||||
|
|
||||||
startafter = options.get('start-after')
|
startafter = self.options.get('start-after')
|
||||||
endbefore = options.get('end-before')
|
endbefore = self.options.get('end-before')
|
||||||
if startafter is not None or endbefore is not None:
|
if startafter is not None or endbefore is not None:
|
||||||
use = not startafter
|
use = not startafter
|
||||||
res = []
|
res = []
|
||||||
@ -129,22 +159,16 @@ def literalinclude_directive(name, arguments, options, content, lineno,
|
|||||||
text = ''.join(lines)
|
text = ''.join(lines)
|
||||||
retnode = nodes.literal_block(text, text, source=fn)
|
retnode = nodes.literal_block(text, text, source=fn)
|
||||||
retnode.line = 1
|
retnode.line = 1
|
||||||
if options.get('language', ''):
|
if self.options.get('language', ''):
|
||||||
retnode['language'] = options['language']
|
retnode['language'] = self.options['language']
|
||||||
if 'linenos' in options:
|
if 'linenos' in self.options:
|
||||||
retnode['linenos'] = True
|
retnode['linenos'] = True
|
||||||
state.document.settings.env.note_dependency(rel_fn)
|
document.settings.env.note_dependency(rel_fn)
|
||||||
return [retnode]
|
return [retnode]
|
||||||
|
|
||||||
literalinclude_directive.options = {
|
|
||||||
'linenos': directives.flag,
|
directives.register_directive('highlight', Highlight)
|
||||||
'language': directives.unchanged_required,
|
directives.register_directive('highlightlang', Highlight) # old name
|
||||||
'encoding': directives.encoding,
|
directives.register_directive('code-block', CodeBlock)
|
||||||
'pyobject': directives.unchanged_required,
|
directives.register_directive('sourcecode', CodeBlock)
|
||||||
'lines': directives.unchanged_required,
|
directives.register_directive('literalinclude', LiteralInclude)
|
||||||
'start-after': directives.unchanged_required,
|
|
||||||
'end-before': directives.unchanged_required,
|
|
||||||
}
|
|
||||||
literalinclude_directive.content = 0
|
|
||||||
literalinclude_directive.arguments = (1, 0, 0)
|
|
||||||
directives.register_directive('literalinclude', literalinclude_directive)
|
|
||||||
|
@ -15,92 +15,76 @@ from docutils.parsers.rst import directives
|
|||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.util import ws_re
|
from sphinx.util import ws_re
|
||||||
|
from sphinx.util.compat import Directive
|
||||||
|
|
||||||
|
|
||||||
# ------ information units -----------------------------------------------------
|
def _is_only_paragraph(node):
|
||||||
|
"""True if the node only contains one paragraph (and system messages)."""
|
||||||
def desc_index_text(desctype, module, name, add_modules):
|
if len(node) == 0:
|
||||||
if desctype == 'function':
|
return False
|
||||||
if not module:
|
elif len(node) > 1:
|
||||||
return _('%s() (built-in function)') % name
|
for subnode in node[1:]:
|
||||||
return _('%s() (in module %s)') % (name, module)
|
if not isinstance(subnode, nodes.system_message):
|
||||||
elif desctype == 'data':
|
return False
|
||||||
if not module:
|
if isinstance(node[0], nodes.paragraph):
|
||||||
return _('%s (built-in variable)') % name
|
return True
|
||||||
return _('%s (in module %s)') % (name, module)
|
return False
|
||||||
elif desctype == 'class':
|
|
||||||
if not module:
|
|
||||||
return _('%s (built-in class)') % name
|
|
||||||
return _('%s (class in %s)') % (name, module)
|
|
||||||
elif desctype == 'exception':
|
|
||||||
return name
|
|
||||||
elif desctype == 'method':
|
|
||||||
try:
|
|
||||||
clsname, methname = name.rsplit('.', 1)
|
|
||||||
except ValueError:
|
|
||||||
if module:
|
|
||||||
return _('%s() (in module %s)') % (name, module)
|
|
||||||
else:
|
|
||||||
return '%s()' % name
|
|
||||||
if module and add_modules:
|
|
||||||
return _('%s() (%s.%s method)') % (methname, module, clsname)
|
|
||||||
else:
|
|
||||||
return _('%s() (%s method)') % (methname, clsname)
|
|
||||||
elif desctype == 'staticmethod':
|
|
||||||
try:
|
|
||||||
clsname, methname = name.rsplit('.', 1)
|
|
||||||
except ValueError:
|
|
||||||
if module:
|
|
||||||
return _('%s() (in module %s)') % (name, module)
|
|
||||||
else:
|
|
||||||
return '%s()' % name
|
|
||||||
if module and add_modules:
|
|
||||||
return _('%s() (%s.%s static method)') % (methname, module, clsname)
|
|
||||||
else:
|
|
||||||
return _('%s() (%s static method)') % (methname, clsname)
|
|
||||||
elif desctype == 'classmethod':
|
|
||||||
try:
|
|
||||||
clsname, methname = name.rsplit('.', 1)
|
|
||||||
except ValueError:
|
|
||||||
if module:
|
|
||||||
return '%s() (in module %s)' % (name, module)
|
|
||||||
else:
|
|
||||||
return '%s()' % name
|
|
||||||
if module:
|
|
||||||
return '%s() (%s.%s class method)' % (methname, module, clsname)
|
|
||||||
else:
|
|
||||||
return '%s() (%s class method)' % (methname, clsname)
|
|
||||||
elif desctype == 'attribute':
|
|
||||||
try:
|
|
||||||
clsname, attrname = name.rsplit('.', 1)
|
|
||||||
except ValueError:
|
|
||||||
if module:
|
|
||||||
return _('%s (in module %s)') % (name, module)
|
|
||||||
else:
|
|
||||||
return name
|
|
||||||
if module and add_modules:
|
|
||||||
return _('%s (%s.%s attribute)') % (attrname, module, clsname)
|
|
||||||
else:
|
|
||||||
return _('%s (%s attribute)') % (attrname, clsname)
|
|
||||||
elif desctype == 'cfunction':
|
|
||||||
return _('%s (C function)') % name
|
|
||||||
elif desctype == 'cmember':
|
|
||||||
return _('%s (C member)') % name
|
|
||||||
elif desctype == 'cmacro':
|
|
||||||
return _('%s (C macro)') % name
|
|
||||||
elif desctype == 'ctype':
|
|
||||||
return _('%s (C type)') % name
|
|
||||||
elif desctype == 'cvar':
|
|
||||||
return _('%s (C variable)') % name
|
|
||||||
else:
|
|
||||||
raise ValueError('unhandled descenv: %s' % desctype)
|
|
||||||
|
|
||||||
|
|
||||||
# ------ make field lists (like :param foo:) in desc bodies prettier
|
# REs for Python signatures
|
||||||
|
py_sig_re = re.compile(
|
||||||
|
r'''^ ([\w.]*\.)? # class name(s)
|
||||||
|
(\w+) \s* # thing name
|
||||||
|
(?: \((.*)\) # optional: arguments
|
||||||
|
(?:\s* -> \s* (.*))? # return annotation
|
||||||
|
)? $ # and nothing more
|
||||||
|
''', re.VERBOSE)
|
||||||
|
|
||||||
_ = lambda x: x # make gettext extraction in constants possible
|
py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
|
||||||
|
|
||||||
doc_fields_with_arg = {
|
# REs for C signatures
|
||||||
|
c_sig_re = re.compile(
|
||||||
|
r'''^([^(]*?) # return type
|
||||||
|
([\w:]+) \s* # thing name (colon allowed for C++ class names)
|
||||||
|
(?: \((.*)\) )? # optionally arguments
|
||||||
|
(\s+const)? $ # const specifier
|
||||||
|
''', re.VERBOSE)
|
||||||
|
c_funcptr_sig_re = re.compile(
|
||||||
|
r'''^([^(]+?) # return type
|
||||||
|
(\( [^()]+ \)) \s* # name in parentheses
|
||||||
|
\( (.*) \) # arguments
|
||||||
|
(\s+const)? $ # const specifier
|
||||||
|
''', re.VERBOSE)
|
||||||
|
c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$')
|
||||||
|
|
||||||
|
# RE for option descriptions
|
||||||
|
option_desc_re = re.compile(
|
||||||
|
r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
|
||||||
|
|
||||||
|
# RE to split at word boundaries
|
||||||
|
wsplit_re = re.compile(r'(\W+)')
|
||||||
|
|
||||||
|
# RE to strip backslash escapes
|
||||||
|
strip_backslash_re = re.compile(r'\\(?=[^\\])')
|
||||||
|
|
||||||
|
|
||||||
|
class DescDirective(Directive):
|
||||||
|
"""
|
||||||
|
Directive to describe a class, function or similar object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
has_content = True
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {
|
||||||
|
'noindex': directives.flag,
|
||||||
|
'module': directives.unchanged,
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = lambda x: x # make gettext extraction in constants possible
|
||||||
|
|
||||||
|
doc_fields_with_arg = {
|
||||||
'param': '%param',
|
'param': '%param',
|
||||||
'parameter': '%param',
|
'parameter': '%param',
|
||||||
'arg': '%param',
|
'arg': '%param',
|
||||||
@ -118,33 +102,17 @@ doc_fields_with_arg = {
|
|||||||
'cvar': 'Variable',
|
'cvar': 'Variable',
|
||||||
'returns': _('Returns'),
|
'returns': _('Returns'),
|
||||||
'return': 'Returns',
|
'return': 'Returns',
|
||||||
}
|
}
|
||||||
|
|
||||||
doc_fields_with_linked_arg = ('raises', 'raise', 'exception', 'except')
|
doc_fields_with_linked_arg = ('raises', 'raise', 'exception', 'except')
|
||||||
|
|
||||||
doc_fields_without_arg = {
|
doc_fields_without_arg = {
|
||||||
'returns': 'Returns',
|
'returns': 'Returns',
|
||||||
'return': 'Returns',
|
'return': 'Returns',
|
||||||
'rtype': _('Return type'),
|
'rtype': _('Return type'),
|
||||||
}
|
}
|
||||||
|
|
||||||
del _
|
def handle_doc_fields(self, node):
|
||||||
|
|
||||||
|
|
||||||
def _is_only_paragraph(node):
|
|
||||||
# determine if the node only contains one paragraph (and system messages)
|
|
||||||
if len(node) == 0:
|
|
||||||
return False
|
|
||||||
elif len(node) > 1:
|
|
||||||
for subnode in node[1:]:
|
|
||||||
if not isinstance(subnode, nodes.system_message):
|
|
||||||
return False
|
|
||||||
if isinstance(node[0], nodes.paragraph):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def handle_doc_fields(node, env):
|
|
||||||
# don't traverse, only handle field lists that are immediate children
|
# don't traverse, only handle field lists that are immediate children
|
||||||
for child in node.children:
|
for child in node.children:
|
||||||
if not isinstance(child, nodes.field_list):
|
if not isinstance(child, nodes.field_list):
|
||||||
@ -158,7 +126,7 @@ def handle_doc_fields(node, env):
|
|||||||
fname, fbody = field
|
fname, fbody = field
|
||||||
try:
|
try:
|
||||||
typ, obj = fname.astext().split(None, 1)
|
typ, obj = fname.astext().split(None, 1)
|
||||||
typdesc = _(doc_fields_with_arg[typ])
|
typdesc = _(self.doc_fields_with_arg[typ])
|
||||||
if _is_only_paragraph(fbody):
|
if _is_only_paragraph(fbody):
|
||||||
children = fbody.children[0].children
|
children = fbody.children[0].children
|
||||||
else:
|
else:
|
||||||
@ -179,8 +147,9 @@ def handle_doc_fields(node, env):
|
|||||||
elif typdesc == '%type':
|
elif typdesc == '%type':
|
||||||
typenodes = fbody.children
|
typenodes = fbody.children
|
||||||
if _is_only_paragraph(fbody):
|
if _is_only_paragraph(fbody):
|
||||||
typenodes = [nodes.Text(' (')] + \
|
typenodes = ([nodes.Text(' (')] +
|
||||||
typenodes[0].children + [nodes.Text(')')]
|
typenodes[0].children +
|
||||||
|
[nodes.Text(')')])
|
||||||
param_types[obj] = typenodes
|
param_types[obj] = typenodes
|
||||||
else:
|
else:
|
||||||
fieldname = typdesc + ' '
|
fieldname = typdesc + ' '
|
||||||
@ -188,12 +157,11 @@ def handle_doc_fields(node, env):
|
|||||||
nfieldname = nodes.field_name(fieldname, fieldname)
|
nfieldname = nodes.field_name(fieldname, fieldname)
|
||||||
nfield += nfieldname
|
nfield += nfieldname
|
||||||
node = nfieldname
|
node = nfieldname
|
||||||
if typ in doc_fields_with_linked_arg:
|
if typ in self.doc_fields_with_linked_arg:
|
||||||
node = addnodes.pending_xref(obj, reftype='obj',
|
node = addnodes.pending_xref(
|
||||||
refcaption=False,
|
obj, reftype='obj', refcaption=False,
|
||||||
reftarget=obj,
|
reftarget=obj, modname=self.env.currmodule,
|
||||||
modname=env.currmodule,
|
classname=self.env.currclass)
|
||||||
classname=env.currclass)
|
|
||||||
nfieldname += node
|
nfieldname += node
|
||||||
node += nodes.Text(obj, obj)
|
node += nodes.Text(obj, obj)
|
||||||
nfield += nodes.field_body()
|
nfield += nodes.field_body()
|
||||||
@ -202,7 +170,7 @@ def handle_doc_fields(node, env):
|
|||||||
except (KeyError, ValueError):
|
except (KeyError, ValueError):
|
||||||
fnametext = fname.astext()
|
fnametext = fname.astext()
|
||||||
try:
|
try:
|
||||||
typ = _(doc_fields_without_arg[fnametext])
|
typ = _(self.doc_fields_without_arg[fnametext])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# at least capitalize the field name
|
# at least capitalize the field name
|
||||||
typ = fnametext.capitalize()
|
typ = fnametext.capitalize()
|
||||||
@ -224,20 +192,70 @@ def handle_doc_fields(node, env):
|
|||||||
param_nodes[param][1:1] = type
|
param_nodes[param][1:1] = type
|
||||||
child.replace_self(new_list)
|
child.replace_self(new_list)
|
||||||
|
|
||||||
|
def get_signatures(self):
|
||||||
|
# remove backslashes to support (dummy) escapes; helps Vim highlighting
|
||||||
|
return [strip_backslash_re.sub('', sig.strip())
|
||||||
|
for sig in self.arguments[0].split('\n')]
|
||||||
|
|
||||||
# ------ functions to parse a Python or C signature and create desc_* nodes.
|
def parse_signature(self, sig, signode):
|
||||||
|
raise ValueError # must be implemented in subclasses
|
||||||
|
|
||||||
py_sig_re = re.compile(
|
def add_target_and_index(self, name, sig, signode):
|
||||||
r'''^ ([\w.]*\.)? # class name(s)
|
return # do nothing by default
|
||||||
(\w+) \s* # thing name
|
|
||||||
(?: \((.*)\) # optional: arguments
|
|
||||||
(?:\s* -> \s* (.*))? # return annotation
|
|
||||||
)? $ # and nothing more
|
|
||||||
''', re.VERBOSE)
|
|
||||||
|
|
||||||
py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
|
def before_content(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def parse_py_signature(signode, sig, desctype, module, env):
|
def after_content(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.desctype = self.name
|
||||||
|
self.env = self.state.document.settings.env
|
||||||
|
self.indexnode = addnodes.index(entries=[])
|
||||||
|
|
||||||
|
node = addnodes.desc()
|
||||||
|
node.document = self.state.document
|
||||||
|
node['desctype'] = self.desctype
|
||||||
|
node['noindex'] = noindex = ('noindex' in self.options)
|
||||||
|
|
||||||
|
self.names = []
|
||||||
|
signatures = self.get_signatures()
|
||||||
|
for i, sig in enumerate(signatures):
|
||||||
|
# add a signature node for each signature in the current unit
|
||||||
|
# and add a reference target for it
|
||||||
|
signode = addnodes.desc_signature(sig, '')
|
||||||
|
signode['first'] = False
|
||||||
|
node.append(signode)
|
||||||
|
try:
|
||||||
|
# name can also be a tuple, e.g. (classname, objname)
|
||||||
|
name = self.parse_signature(sig, signode)
|
||||||
|
except ValueError, err:
|
||||||
|
# signature parsing failed
|
||||||
|
signode.clear()
|
||||||
|
signode += addnodes.desc_name(sig, sig)
|
||||||
|
continue # we don't want an index entry here
|
||||||
|
if not noindex and name not in self.names:
|
||||||
|
# only add target and index entry if this is the first
|
||||||
|
# description of the object with this name in this desc block
|
||||||
|
self.names.append(name)
|
||||||
|
self.add_target_and_index(name, sig, signode)
|
||||||
|
|
||||||
|
contentnode = addnodes.desc_content()
|
||||||
|
node.append(contentnode)
|
||||||
|
if self.names:
|
||||||
|
# needed for association of version{added,changed} directives
|
||||||
|
self.env.currdesc = self.names[0]
|
||||||
|
self.before_content()
|
||||||
|
self.state.nested_parse(self.content, self.content_offset, contentnode)
|
||||||
|
self.handle_doc_fields(contentnode)
|
||||||
|
self.env.currdesc = None
|
||||||
|
self.after_content()
|
||||||
|
return [self.indexnode, node]
|
||||||
|
|
||||||
|
|
||||||
|
class PythonDesc(DescDirective):
|
||||||
|
def parse_signature(self, sig, signode):
|
||||||
"""
|
"""
|
||||||
Transform a python signature into RST nodes.
|
Transform a python signature into RST nodes.
|
||||||
Return (fully qualified name of the thing, classname if any).
|
Return (fully qualified name of the thing, classname if any).
|
||||||
@ -251,40 +269,44 @@ def parse_py_signature(signode, sig, desctype, module, env):
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
classname, name, arglist, retann = m.groups()
|
classname, name, arglist, retann = m.groups()
|
||||||
|
|
||||||
if env.currclass:
|
if self.env.currclass:
|
||||||
add_module = False
|
add_module = False
|
||||||
if classname and classname.startswith(env.currclass):
|
if classname and classname.startswith(self.env.currclass):
|
||||||
fullname = classname + name
|
fullname = classname + name
|
||||||
# class name is given again in the signature
|
# class name is given again in the signature
|
||||||
classname = classname[len(env.currclass):].lstrip('.')
|
classname = classname[len(self.env.currclass):].lstrip('.')
|
||||||
elif classname:
|
elif classname:
|
||||||
# class name is given in the signature, but different
|
# class name is given in the signature, but different
|
||||||
# (shouldn't happen)
|
# (shouldn't happen)
|
||||||
fullname = env.currclass + '.' + classname + name
|
fullname = self.env.currclass + '.' + classname + name
|
||||||
else:
|
else:
|
||||||
# class name is not given in the signature
|
# class name is not given in the signature
|
||||||
fullname = env.currclass + '.' + name
|
fullname = self.env.currclass + '.' + name
|
||||||
else:
|
else:
|
||||||
add_module = True
|
add_module = True
|
||||||
fullname = classname and classname + name or name
|
fullname = classname and classname + name or name
|
||||||
|
|
||||||
if desctype == 'staticmethod':
|
# XXX!
|
||||||
|
if self.desctype == 'staticmethod':
|
||||||
signode += addnodes.desc_annotation('static ', 'static ')
|
signode += addnodes.desc_annotation('static ', 'static ')
|
||||||
elif desctype == 'classmethod':
|
elif self.desctype == 'classmethod':
|
||||||
signode += addnodes.desc_annotation('classmethod ', 'classmethod ')
|
signode += addnodes.desc_annotation('classmethod ', 'classmethod ')
|
||||||
|
|
||||||
if classname:
|
if classname:
|
||||||
signode += addnodes.desc_addname(classname, classname)
|
signode += addnodes.desc_addname(classname, classname)
|
||||||
# exceptions are a special case, since they are documented in the
|
# exceptions are a special case, since they are documented in the
|
||||||
# 'exceptions' module.
|
# 'exceptions' module.
|
||||||
elif add_module and env.config.add_module_names and \
|
elif add_module and self.env.config.add_module_names:
|
||||||
module and module != 'exceptions':
|
modname = self.options.get('module', self.env.currmodule)
|
||||||
nodetext = module + '.'
|
if modname and modname != 'exceptions':
|
||||||
|
nodetext = modname + '.'
|
||||||
signode += addnodes.desc_addname(nodetext, nodetext)
|
signode += addnodes.desc_addname(nodetext, nodetext)
|
||||||
|
|
||||||
signode += addnodes.desc_name(name, name)
|
signode += addnodes.desc_name(name, name)
|
||||||
if not arglist:
|
if not arglist:
|
||||||
if desctype in ('function', 'method', 'staticmethod', 'classmethod'):
|
# XXX!
|
||||||
|
if self.desctype in ('function', 'method',
|
||||||
|
'staticmethod', 'classmethod'):
|
||||||
# for callables, add an empty parameter list
|
# for callables, add an empty parameter list
|
||||||
signode += addnodes.desc_parameterlist()
|
signode += addnodes.desc_parameterlist()
|
||||||
if retann:
|
if retann:
|
||||||
@ -314,33 +336,142 @@ def parse_py_signature(signode, sig, desctype, module, env):
|
|||||||
signode += addnodes.desc_returns(retann, retann)
|
signode += addnodes.desc_returns(retann, retann)
|
||||||
return fullname, classname
|
return fullname, classname
|
||||||
|
|
||||||
|
def get_index_text(self, modname, name):
|
||||||
|
raise NotImplementedError('must be implemented in subclasses')
|
||||||
|
|
||||||
c_sig_re = re.compile(
|
def add_target_and_index(self, name_cls, sig, signode):
|
||||||
r'''^([^(]*?) # return type
|
modname = self.options.get('module', self.env.currmodule)
|
||||||
([\w:]+) \s* # thing name (colon allowed for C++ class names)
|
fullname = (modname and modname + '.' or '') + name_cls[0]
|
||||||
(?: \((.*)\) )? # optionally arguments
|
# note target
|
||||||
(\s+const)? $ # const specifier
|
if fullname not in self.state.document.ids:
|
||||||
''', re.VERBOSE)
|
signode['names'].append(fullname)
|
||||||
c_funcptr_sig_re = re.compile(
|
signode['ids'].append(fullname)
|
||||||
r'''^([^(]+?) # return type
|
signode['first'] = (not self.names)
|
||||||
(\( [^()]+ \)) \s* # name in parentheses
|
self.state.document.note_explicit_target(signode)
|
||||||
\( (.*) \) # arguments
|
self.env.note_descref(fullname, self.desctype, self.lineno)
|
||||||
(\s+const)? $ # const specifier
|
|
||||||
''', re.VERBOSE)
|
|
||||||
c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$')
|
|
||||||
|
|
||||||
# RE to split at word boundaries
|
indextext = self.get_index_text(modname, name_cls)
|
||||||
wsplit_re = re.compile(r'(\W+)')
|
if indextext:
|
||||||
|
self.indexnode['entries'].append(('single', indextext,
|
||||||
|
fullname, fullname))
|
||||||
|
|
||||||
# These C types aren't described in the reference, so don't try to create
|
def before_content(self):
|
||||||
# a cross-reference to them
|
# needed for automatic qualification of members (reset in subclasses)
|
||||||
stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct'))
|
self.clsname_set = False
|
||||||
|
|
||||||
def parse_c_type(node, ctype):
|
def after_content(self):
|
||||||
|
if self.clsname_set:
|
||||||
|
self.env.currclass = None
|
||||||
|
|
||||||
|
|
||||||
|
class ModulelevelDesc(PythonDesc):
|
||||||
|
def get_index_text(self, modname, name_cls):
|
||||||
|
if self.desctype == 'function':
|
||||||
|
if not modname:
|
||||||
|
return _('%s() (built-in function)') % name_cls[0]
|
||||||
|
return _('%s() (in module %s)') % (name_cls[0], modname)
|
||||||
|
elif self.desctype == 'data':
|
||||||
|
if not modname:
|
||||||
|
return _('%s (built-in variable)') % name_cls[0]
|
||||||
|
return _('%s (in module %s)') % (name_cls[0], modname)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
class ClasslikeDesc(PythonDesc):
|
||||||
|
def get_index_text(self, modname, name_cls):
|
||||||
|
if self.desctype == 'class':
|
||||||
|
if not modname:
|
||||||
|
return _('%s (built-in class)') % name_cls[0]
|
||||||
|
return _('%s (class in %s)') % (name_cls[0], modname)
|
||||||
|
elif self.desctype == 'exception':
|
||||||
|
return name_cls[0]
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def before_content(self):
|
||||||
|
PythonDesc.before_content(self)
|
||||||
|
if self.names:
|
||||||
|
self.env.currclass = self.names[0][0]
|
||||||
|
self.clsname_set = True
|
||||||
|
|
||||||
|
|
||||||
|
class ClassmemberDesc(PythonDesc):
|
||||||
|
def get_index_text(self, modname, name_cls):
|
||||||
|
name, cls = name_cls
|
||||||
|
add_modules = self.env.config.add_module_names
|
||||||
|
if self.desctype == 'method':
|
||||||
|
try:
|
||||||
|
clsname, methname = name.rsplit('.', 1)
|
||||||
|
except ValueError:
|
||||||
|
if modname:
|
||||||
|
return _('%s() (in module %s)') % (name, modname)
|
||||||
|
else:
|
||||||
|
return '%s()' % name
|
||||||
|
if modname and add_modules:
|
||||||
|
return _('%s() (%s.%s method)') % (methname, modname, clsname)
|
||||||
|
else:
|
||||||
|
return _('%s() (%s method)') % (methname, clsname)
|
||||||
|
elif self.desctype == 'staticmethod':
|
||||||
|
try:
|
||||||
|
clsname, methname = name.rsplit('.', 1)
|
||||||
|
except ValueError:
|
||||||
|
if modname:
|
||||||
|
return _('%s() (in module %s)') % (name, modname)
|
||||||
|
else:
|
||||||
|
return '%s()' % name
|
||||||
|
if modname and add_modules:
|
||||||
|
return _('%s() (%s.%s static method)') % (methname, modname,
|
||||||
|
clsname)
|
||||||
|
else:
|
||||||
|
return _('%s() (%s static method)') % (methname, clsname)
|
||||||
|
elif self.desctype == 'classmethod':
|
||||||
|
try:
|
||||||
|
clsname, methname = name.rsplit('.', 1)
|
||||||
|
except ValueError:
|
||||||
|
if modname:
|
||||||
|
return '%s() (in module %s)' % (name, modname)
|
||||||
|
else:
|
||||||
|
return '%s()' % name
|
||||||
|
if modname:
|
||||||
|
return '%s() (%s.%s class method)' % (methname, modname,
|
||||||
|
clsname)
|
||||||
|
else:
|
||||||
|
return '%s() (%s class method)' % (methname, clsname)
|
||||||
|
elif self.desctype == 'attribute':
|
||||||
|
try:
|
||||||
|
clsname, attrname = name.rsplit('.', 1)
|
||||||
|
except ValueError:
|
||||||
|
if modname:
|
||||||
|
return _('%s (in module %s)') % (name, modname)
|
||||||
|
else:
|
||||||
|
return name
|
||||||
|
if modname and add_modules:
|
||||||
|
return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
|
||||||
|
else:
|
||||||
|
return _('%s (%s attribute)') % (attrname, clsname)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def before_content(self):
|
||||||
|
PythonDesc.before_content(self)
|
||||||
|
if self.names and self.names[-1][1] and not self.env.currclass:
|
||||||
|
self.env.currclass = self.names[-1][1].strip('.')
|
||||||
|
self.clsname_set = True
|
||||||
|
|
||||||
|
|
||||||
|
class CDesc(DescDirective):
|
||||||
|
|
||||||
|
# These C types aren't described anywhere, so don't try to create
|
||||||
|
# a cross-reference to them
|
||||||
|
stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct'))
|
||||||
|
|
||||||
|
def _parse_type(self, node, ctype):
|
||||||
# add cross-ref nodes for all words
|
# add cross-ref nodes for all words
|
||||||
for part in filter(None, wsplit_re.split(ctype)):
|
for part in filter(None, wsplit_re.split(ctype)):
|
||||||
tnode = nodes.Text(part, part)
|
tnode = nodes.Text(part, part)
|
||||||
if part[0] in string.ascii_letters+'_' and part not in stopwords:
|
if part[0] in string.ascii_letters+'_' and \
|
||||||
|
part not in self.stopwords:
|
||||||
pnode = addnodes.pending_xref(
|
pnode = addnodes.pending_xref(
|
||||||
'', reftype='ctype', reftarget=part,
|
'', reftype='ctype', reftarget=part,
|
||||||
modname=None, classname=None)
|
modname=None, classname=None)
|
||||||
@ -349,7 +480,7 @@ def parse_c_type(node, ctype):
|
|||||||
else:
|
else:
|
||||||
node += tnode
|
node += tnode
|
||||||
|
|
||||||
def parse_c_signature(signode, sig, desctype):
|
def parse_signature(self, sig, signode):
|
||||||
"""Transform a C (or C++) signature into RST nodes."""
|
"""Transform a C (or C++) signature into RST nodes."""
|
||||||
# first try the function pointer signature regex, it's more specific
|
# first try the function pointer signature regex, it's more specific
|
||||||
m = c_funcptr_sig_re.match(sig)
|
m = c_funcptr_sig_re.match(sig)
|
||||||
@ -360,7 +491,7 @@ def parse_c_signature(signode, sig, desctype):
|
|||||||
rettype, name, arglist, const = m.groups()
|
rettype, name, arglist, const = m.groups()
|
||||||
|
|
||||||
signode += addnodes.desc_type('', '')
|
signode += addnodes.desc_type('', '')
|
||||||
parse_c_type(signode[-1], rettype)
|
self._parse_type(signode[-1], rettype)
|
||||||
try:
|
try:
|
||||||
classname, funcname = name.split('::', 1)
|
classname, funcname = name.split('::', 1)
|
||||||
classname += '::'
|
classname += '::'
|
||||||
@ -374,7 +505,7 @@ def parse_c_signature(signode, sig, desctype):
|
|||||||
if m:
|
if m:
|
||||||
name = m.group(1)
|
name = m.group(1)
|
||||||
if not arglist:
|
if not arglist:
|
||||||
if desctype == 'cfunction':
|
if self.desctype == 'cfunction':
|
||||||
# for functions, add an empty parameter list
|
# for functions, add an empty parameter list
|
||||||
signode += addnodes.desc_parameterlist()
|
signode += addnodes.desc_parameterlist()
|
||||||
return name
|
return name
|
||||||
@ -390,9 +521,9 @@ def parse_c_signature(signode, sig, desctype):
|
|||||||
ctype, argname = arg.rsplit(' ', 1)
|
ctype, argname = arg.rsplit(' ', 1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# no argument name given, only the type
|
# no argument name given, only the type
|
||||||
parse_c_type(param, arg)
|
self._parse_type(param, arg)
|
||||||
else:
|
else:
|
||||||
parse_c_type(param, ctype)
|
self._parse_type(param, ctype)
|
||||||
param += nodes.emphasis(' '+argname, ' '+argname)
|
param += nodes.emphasis(' '+argname, ' '+argname)
|
||||||
paramlist += param
|
paramlist += param
|
||||||
signode += paramlist
|
signode += paramlist
|
||||||
@ -400,11 +531,30 @@ def parse_c_signature(signode, sig, desctype):
|
|||||||
signode += addnodes.desc_addname(const, const)
|
signode += addnodes.desc_addname(const, const)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def get_index_text(self, modname, name):
|
||||||
|
if self.desctype == 'cfunction':
|
||||||
|
return _('%s (C function)') % name
|
||||||
|
elif self.desctype == 'cmember':
|
||||||
|
return _('%s (C member)') % name
|
||||||
|
elif self.desctype == 'cmacro':
|
||||||
|
return _('%s (C macro)') % name
|
||||||
|
elif self.desctype == 'ctype':
|
||||||
|
return _('%s (C type)') % name
|
||||||
|
elif self.desctype == 'cvar':
|
||||||
|
return _('%s (C variable)') % name
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
option_desc_re = re.compile(
|
# just copy that one
|
||||||
r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
|
add_target_and_index = PythonDesc.__dict__['add_target_and_index']
|
||||||
|
|
||||||
def parse_option_desc(signode, sig):
|
|
||||||
|
class CmdoptionDesc(DescDirective):
|
||||||
|
"""
|
||||||
|
A command-line option (.. cmdoption).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def parse_signature(self, sig, signode):
|
||||||
"""Transform an option description into RST nodes."""
|
"""Transform an option description into RST nodes."""
|
||||||
count = 0
|
count = 0
|
||||||
firstname = ''
|
firstname = ''
|
||||||
@ -421,186 +571,73 @@ def parse_option_desc(signode, sig):
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
return firstname
|
return firstname
|
||||||
|
|
||||||
|
def add_target_and_index(self, name, sig, signode):
|
||||||
strip_backslash_re = re.compile(r'\\(?=[^\\])')
|
targetname = name.replace('/', '-')
|
||||||
|
if self.env.currprogram:
|
||||||
def desc_directive(desctype, arguments, options, content, lineno,
|
targetname = '-' + self.env.currprogram + targetname
|
||||||
content_offset, block_text, state, state_machine):
|
|
||||||
env = state.document.settings.env
|
|
||||||
inode = addnodes.index(entries=[])
|
|
||||||
node = addnodes.desc()
|
|
||||||
node.document = state.document
|
|
||||||
node['desctype'] = desctype
|
|
||||||
|
|
||||||
noindex = ('noindex' in options)
|
|
||||||
node['noindex'] = noindex
|
|
||||||
# remove backslashes to support (dummy) escapes; helps Vim's highlighting
|
|
||||||
signatures = map(lambda s: strip_backslash_re.sub('', s.strip()),
|
|
||||||
arguments[0].split('\n'))
|
|
||||||
names = []
|
|
||||||
clsname = None
|
|
||||||
module = options.get('module', env.currmodule)
|
|
||||||
for i, sig in enumerate(signatures):
|
|
||||||
# add a signature node for each signature in the current unit
|
|
||||||
# and add a reference target for it
|
|
||||||
sig = sig.strip()
|
|
||||||
signode = addnodes.desc_signature(sig, '')
|
|
||||||
signode['first'] = False
|
|
||||||
node.append(signode)
|
|
||||||
try:
|
|
||||||
if desctype in ('function', 'data', 'class', 'exception',
|
|
||||||
'method', 'staticmethod', 'classmethod',
|
|
||||||
'attribute'):
|
|
||||||
name, clsname = parse_py_signature(signode, sig,
|
|
||||||
desctype, module, env)
|
|
||||||
elif desctype in ('cfunction', 'cmember', 'cmacro',
|
|
||||||
'ctype', 'cvar'):
|
|
||||||
name = parse_c_signature(signode, sig, desctype)
|
|
||||||
elif desctype == 'cmdoption':
|
|
||||||
optname = parse_option_desc(signode, sig)
|
|
||||||
if not noindex:
|
|
||||||
targetname = optname.replace('/', '-')
|
|
||||||
if env.currprogram:
|
|
||||||
targetname = '-' + env.currprogram + targetname
|
|
||||||
targetname = 'cmdoption' + targetname
|
targetname = 'cmdoption' + targetname
|
||||||
signode['ids'].append(targetname)
|
signode['ids'].append(targetname)
|
||||||
state.document.note_explicit_target(signode)
|
self.state.document.note_explicit_target(signode)
|
||||||
inode['entries'].append(
|
self.indexnode['entries'].append(
|
||||||
('pair', _('%scommand line option; %s') %
|
('pair', _('%scommand line option; %s') %
|
||||||
((env.currprogram and env.currprogram + ' ' or ''),
|
((self.env.currprogram and
|
||||||
sig),
|
self.env.currprogram + ' ' or ''), sig),
|
||||||
targetname, targetname))
|
targetname, targetname))
|
||||||
env.note_progoption(optname, targetname)
|
self.env.note_progoption(name, targetname)
|
||||||
continue
|
|
||||||
elif desctype == 'describe':
|
|
||||||
signode.clear()
|
class GenericDesc(DescDirective):
|
||||||
signode += addnodes.desc_name(sig, sig)
|
"""
|
||||||
continue
|
A generic x-ref directive registered with Sphinx.add_description_unit().
|
||||||
else:
|
"""
|
||||||
# another registered generic x-ref directive
|
|
||||||
rolename, indextemplate, parse_node = \
|
def parse_signature(self, sig, signode):
|
||||||
additional_xref_types[desctype]
|
parse_node = additional_xref_types[self.desctype][2]
|
||||||
if parse_node:
|
if parse_node:
|
||||||
fullname = parse_node(env, sig, signode)
|
name = parse_node(self.env, sig, signode)
|
||||||
else:
|
else:
|
||||||
signode.clear()
|
signode.clear()
|
||||||
signode += addnodes.desc_name(sig, sig)
|
signode += addnodes.desc_name(sig, sig)
|
||||||
# normalize whitespace like xfileref_role does
|
# normalize whitespace like xfileref_role does
|
||||||
fullname = ws_re.sub('', sig)
|
name = ws_re.sub('', sig)
|
||||||
if not noindex:
|
return name
|
||||||
targetname = '%s-%s' % (rolename, fullname)
|
|
||||||
|
def add_target_and_index(self, name, sig, signode):
|
||||||
|
rolename, indextemplate, _ = additional_xref_types[self.desctype]
|
||||||
|
targetname = '%s-%s' % (rolename, name)
|
||||||
signode['ids'].append(targetname)
|
signode['ids'].append(targetname)
|
||||||
state.document.note_explicit_target(signode)
|
self.state.document.note_explicit_target(signode)
|
||||||
if indextemplate:
|
if indextemplate:
|
||||||
indexentry = _(indextemplate) % (fullname,)
|
indexentry = _(indextemplate) % (name,)
|
||||||
indextype = 'single'
|
indextype = 'single'
|
||||||
colon = indexentry.find(':')
|
colon = indexentry.find(':')
|
||||||
if colon != -1:
|
if colon != -1:
|
||||||
indextype = indexentry[:colon].strip()
|
indextype = indexentry[:colon].strip()
|
||||||
indexentry = indexentry[colon+1:].strip()
|
indexentry = indexentry[colon+1:].strip()
|
||||||
inode['entries'].append((indextype, indexentry,
|
self.indexnode['entries'].append((indextype, indexentry,
|
||||||
targetname, targetname))
|
targetname, targetname))
|
||||||
env.note_reftarget(rolename, fullname, targetname)
|
self.env.note_reftarget(rolename, name, 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
|
|
||||||
# only add target and index entry if this is the first description
|
|
||||||
# of the function name in this desc block
|
|
||||||
if not noindex and name not in names:
|
|
||||||
fullname = (module and module + '.' or '') + name
|
|
||||||
# note target
|
|
||||||
if fullname not in state.document.ids:
|
|
||||||
signode['names'].append(fullname)
|
|
||||||
signode['ids'].append(fullname)
|
|
||||||
signode['first'] = (not names)
|
|
||||||
state.document.note_explicit_target(signode)
|
|
||||||
env.note_descref(fullname, desctype, lineno)
|
|
||||||
names.append(name)
|
|
||||||
|
|
||||||
indextext = desc_index_text(desctype, module, name,
|
|
||||||
env.config.add_module_names)
|
|
||||||
inode['entries'].append(('single', indextext, fullname, fullname))
|
|
||||||
|
|
||||||
subnode = addnodes.desc_content()
|
|
||||||
node.append(subnode)
|
|
||||||
# needed for automatic qualification of members
|
|
||||||
clsname_set = False
|
|
||||||
if desctype in ('class', 'exception') and names:
|
|
||||||
env.currclass = names[0]
|
|
||||||
clsname_set = True
|
|
||||||
elif desctype in ('method', 'staticmethod', 'classmethod',
|
|
||||||
'attribute') and clsname and not env.currclass:
|
|
||||||
env.currclass = clsname.strip('.')
|
|
||||||
clsname_set = True
|
|
||||||
# needed for association of version{added,changed} directives
|
|
||||||
if names:
|
|
||||||
env.currdesc = names[0]
|
|
||||||
state.nested_parse(content, content_offset, subnode)
|
|
||||||
handle_doc_fields(subnode, env)
|
|
||||||
if clsname_set:
|
|
||||||
env.currclass = None
|
|
||||||
env.currdesc = None
|
|
||||||
return [inode, node]
|
|
||||||
|
|
||||||
desc_directive.content = 1
|
|
||||||
desc_directive.arguments = (1, 0, 1)
|
|
||||||
desc_directive.options = {'noindex': directives.flag,
|
|
||||||
'module': directives.unchanged}
|
|
||||||
|
|
||||||
desctypes = [
|
|
||||||
# the Python ones
|
|
||||||
'function',
|
|
||||||
'data',
|
|
||||||
'class',
|
|
||||||
'method',
|
|
||||||
'classmethod',
|
|
||||||
'staticmethod',
|
|
||||||
'attribute',
|
|
||||||
'exception',
|
|
||||||
# the C ones
|
|
||||||
'cfunction',
|
|
||||||
'cmember',
|
|
||||||
'cmacro',
|
|
||||||
'ctype',
|
|
||||||
'cvar',
|
|
||||||
# for command line options
|
|
||||||
'cmdoption',
|
|
||||||
# the generic one
|
|
||||||
'describe',
|
|
||||||
'envvar',
|
|
||||||
]
|
|
||||||
|
|
||||||
for _name in desctypes:
|
|
||||||
directives.register_directive(_name, desc_directive)
|
|
||||||
|
|
||||||
_ = lambda x: x
|
|
||||||
|
|
||||||
# Generic cross-reference types; they can be registered in the application;
|
|
||||||
# the directives are either desc_directive or target_directive
|
|
||||||
additional_xref_types = {
|
|
||||||
# directive name: (role name, index text, function to parse the desc node)
|
|
||||||
'envvar': ('envvar', _('environment variable; %s'), None),
|
|
||||||
}
|
|
||||||
|
|
||||||
del _
|
|
||||||
|
|
||||||
|
|
||||||
# ------ target ----------------------------------------------------------------
|
class Target(Directive):
|
||||||
|
"""
|
||||||
|
Generic target for user-defined cross-reference types.
|
||||||
|
"""
|
||||||
|
|
||||||
def target_directive(targettype, arguments, options, content, lineno,
|
has_content = False
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 1
|
||||||
"""Generic target for user-defined cross-reference types."""
|
optional_arguments = 0
|
||||||
env = state.document.settings.env
|
final_argument_whitespace = True
|
||||||
rolename, indextemplate, foo = additional_xref_types[targettype]
|
option_spec = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
env = self.state.document.settings.env
|
||||||
|
rolename, indextemplate, foo = additional_xref_types[self.name]
|
||||||
# normalize whitespace in fullname like xfileref_role does
|
# normalize whitespace in fullname like xfileref_role does
|
||||||
fullname = ws_re.sub('', arguments[0].strip())
|
fullname = ws_re.sub('', self.arguments[0].strip())
|
||||||
targetname = '%s-%s' % (rolename, fullname)
|
targetname = '%s-%s' % (rolename, fullname)
|
||||||
node = nodes.target('', '', ids=[targetname])
|
node = nodes.target('', '', ids=[targetname])
|
||||||
state.document.note_explicit_target(node)
|
self.state.document.note_explicit_target(node)
|
||||||
ret = [node]
|
ret = [node]
|
||||||
if indextemplate:
|
if indextemplate:
|
||||||
indexentry = indextemplate % (fullname,)
|
indexentry = indextemplate % (fullname,)
|
||||||
@ -615,8 +652,37 @@ def target_directive(targettype, arguments, options, content, lineno,
|
|||||||
env.note_reftarget(rolename, fullname, targetname)
|
env.note_reftarget(rolename, fullname, targetname)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
target_directive.content = 0
|
# Note: the target directive is not registered here, it is used by the
|
||||||
target_directive.arguments = (1, 0, 1)
|
# application when registering additional xref types.
|
||||||
|
|
||||||
# note, the target directive is not registered here, it is used by the
|
_ = lambda x: x
|
||||||
# application when registering additional xref types
|
|
||||||
|
# Generic cross-reference types; they can be registered in the application;
|
||||||
|
# the directives are either desc_directive or target_directive.
|
||||||
|
additional_xref_types = {
|
||||||
|
# directive name: (role name, index text, function to parse the desc node)
|
||||||
|
'envvar': ('envvar', _('environment variable; %s'), None),
|
||||||
|
}
|
||||||
|
|
||||||
|
del _
|
||||||
|
|
||||||
|
|
||||||
|
directives.register_directive('describe', DescDirective)
|
||||||
|
|
||||||
|
directives.register_directive('function', ModulelevelDesc)
|
||||||
|
directives.register_directive('data', ModulelevelDesc)
|
||||||
|
directives.register_directive('class', ClasslikeDesc)
|
||||||
|
directives.register_directive('exception', ClasslikeDesc)
|
||||||
|
directives.register_directive('method', ClassmemberDesc)
|
||||||
|
directives.register_directive('classmethod', ClassmemberDesc)
|
||||||
|
directives.register_directive('staticmethod', ClassmemberDesc)
|
||||||
|
directives.register_directive('attribute', ClassmemberDesc)
|
||||||
|
|
||||||
|
directives.register_directive('cfunction', CDesc)
|
||||||
|
directives.register_directive('cmember', CDesc)
|
||||||
|
directives.register_directive('cmacro', CDesc)
|
||||||
|
directives.register_directive('ctype', CDesc)
|
||||||
|
directives.register_directive('cvar', CDesc)
|
||||||
|
|
||||||
|
directives.register_directive('cmdoption', CmdoptionDesc)
|
||||||
|
directives.register_directive('envvar', GenericDesc)
|
||||||
|
@ -15,16 +15,29 @@ from docutils.parsers.rst import directives
|
|||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.locale import pairindextypes
|
from sphinx.locale import pairindextypes
|
||||||
from sphinx.util import patfilter, ws_re, caption_ref_re, url_re, docname_join
|
from sphinx.util import patfilter, ws_re, caption_ref_re, url_re, docname_join
|
||||||
from sphinx.util.compat import make_admonition
|
from sphinx.util.compat import Directive, make_admonition
|
||||||
|
|
||||||
|
|
||||||
# ------ the TOC tree ----------------------------------------------------------
|
class TocTree(Directive):
|
||||||
|
"""
|
||||||
|
Directive to notify Sphinx about the hierarchical structure of the docs,
|
||||||
|
and to include a table-of-contents like tree in the current document.
|
||||||
|
"""
|
||||||
|
|
||||||
def toctree_directive(name, arguments, options, content, lineno,
|
has_content = True
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 0
|
||||||
env = state.document.settings.env
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {
|
||||||
|
'maxdepth': int,
|
||||||
|
'glob': directives.flag,
|
||||||
|
'hidden': directives.flag,
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
env = self.state.document.settings.env
|
||||||
suffix = env.config.source_suffix
|
suffix = env.config.source_suffix
|
||||||
glob = 'glob' in options
|
glob = 'glob' in self.options
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
# (title, ref) pairs, where ref may be a document, or an external link,
|
# (title, ref) pairs, where ref may be a document, or an external link,
|
||||||
@ -35,11 +48,11 @@ def toctree_directive(name, arguments, options, content, lineno,
|
|||||||
all_docnames = env.found_docs.copy()
|
all_docnames = env.found_docs.copy()
|
||||||
# don't add the currently visited file in catch-all patterns
|
# don't add the currently visited file in catch-all patterns
|
||||||
all_docnames.remove(env.docname)
|
all_docnames.remove(env.docname)
|
||||||
for entry in content:
|
for entry in self.content:
|
||||||
if not entry:
|
if not entry:
|
||||||
continue
|
continue
|
||||||
if not glob:
|
if not glob:
|
||||||
# look for explicit titles and documents ("Some Title <document>").
|
# look for explicit titles ("Some Title <document>")
|
||||||
m = caption_ref_re.match(entry)
|
m = caption_ref_re.match(entry)
|
||||||
if m:
|
if m:
|
||||||
ref = m.group(2)
|
ref = m.group(2)
|
||||||
@ -56,9 +69,9 @@ def toctree_directive(name, arguments, options, content, lineno,
|
|||||||
if url_re.match(ref) or ref == 'self':
|
if url_re.match(ref) or ref == 'self':
|
||||||
entries.append((title, ref))
|
entries.append((title, ref))
|
||||||
elif docname not in env.found_docs:
|
elif docname not in env.found_docs:
|
||||||
ret.append(state.document.reporter.warning(
|
ret.append(self.state.document.reporter.warning(
|
||||||
'toctree references unknown document %r' % docname,
|
'toctree references unknown document %r' % docname,
|
||||||
line=lineno))
|
line=self.lineno))
|
||||||
else:
|
else:
|
||||||
entries.append((title, docname))
|
entries.append((title, docname))
|
||||||
includefiles.append(docname)
|
includefiles.append(docname)
|
||||||
@ -70,47 +83,56 @@ def toctree_directive(name, arguments, options, content, lineno,
|
|||||||
entries.append((None, docname))
|
entries.append((None, docname))
|
||||||
includefiles.append(docname)
|
includefiles.append(docname)
|
||||||
if not docnames:
|
if not docnames:
|
||||||
ret.append(state.document.reporter.warning(
|
ret.append(self.state.document.reporter.warning(
|
||||||
'toctree glob pattern %r didn\'t match any documents'
|
'toctree glob pattern %r didn\'t match any documents'
|
||||||
% entry, line=lineno))
|
% entry, line=self.lineno))
|
||||||
subnode = addnodes.toctree()
|
subnode = addnodes.toctree()
|
||||||
subnode['parent'] = env.docname
|
subnode['parent'] = env.docname
|
||||||
subnode['entries'] = entries
|
subnode['entries'] = entries
|
||||||
subnode['includefiles'] = includefiles
|
subnode['includefiles'] = includefiles
|
||||||
subnode['maxdepth'] = options.get('maxdepth', -1)
|
subnode['maxdepth'] = self.options.get('maxdepth', -1)
|
||||||
subnode['glob'] = glob
|
subnode['glob'] = glob
|
||||||
subnode['hidden'] = 'hidden' in options
|
subnode['hidden'] = 'hidden' in self.options
|
||||||
ret.append(subnode)
|
ret.append(subnode)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
toctree_directive.content = 1
|
|
||||||
toctree_directive.options = {'maxdepth': int, 'glob': directives.flag,
|
|
||||||
'hidden': directives.flag}
|
|
||||||
directives.register_directive('toctree', toctree_directive)
|
|
||||||
|
|
||||||
|
class Module(Directive):
|
||||||
|
"""
|
||||||
|
Directive to mark description of a new module.
|
||||||
|
"""
|
||||||
|
|
||||||
# ------ section metadata ------------------------------------------------------
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {
|
||||||
|
'platform': lambda x: x,
|
||||||
|
'synopsis': lambda x: x,
|
||||||
|
'noindex': directives.flag,
|
||||||
|
'deprecated': directives.flag,
|
||||||
|
}
|
||||||
|
|
||||||
def module_directive(name, arguments, options, content, lineno,
|
def run(self):
|
||||||
content_offset, block_text, state, state_machine):
|
env = self.state.document.settings.env
|
||||||
env = state.document.settings.env
|
modname = self.arguments[0].strip()
|
||||||
modname = arguments[0].strip()
|
noindex = 'noindex' in self.options
|
||||||
noindex = 'noindex' in options
|
|
||||||
env.currmodule = modname
|
env.currmodule = modname
|
||||||
env.note_module(modname, options.get('synopsis', ''),
|
env.note_module(modname, self.options.get('synopsis', ''),
|
||||||
options.get('platform', ''),
|
self.options.get('platform', ''),
|
||||||
'deprecated' in options)
|
'deprecated' in self.options)
|
||||||
modulenode = addnodes.module()
|
modulenode = addnodes.module()
|
||||||
modulenode['modname'] = modname
|
modulenode['modname'] = modname
|
||||||
modulenode['synopsis'] = options.get('synopsis', '')
|
modulenode['synopsis'] = self.options.get('synopsis', '')
|
||||||
targetnode = nodes.target('', '', ids=['module-' + modname])
|
targetnode = nodes.target('', '', ids=['module-' + modname])
|
||||||
state.document.note_explicit_target(targetnode)
|
self.state.document.note_explicit_target(targetnode)
|
||||||
ret = [modulenode, targetnode]
|
ret = [modulenode, targetnode]
|
||||||
if 'platform' in options:
|
if 'platform' in self.options:
|
||||||
modulenode['platform'] = options['platform']
|
platform = self.options['platform']
|
||||||
|
modulenode['platform'] = platform
|
||||||
node = nodes.paragraph()
|
node = nodes.paragraph()
|
||||||
node += nodes.emphasis('', _('Platforms: '))
|
node += nodes.emphasis('', _('Platforms: '))
|
||||||
node += nodes.Text(options['platform'], options['platform'])
|
node += nodes.Text(platform, platform)
|
||||||
ret.append(node)
|
ret.append(node)
|
||||||
# the synopsis isn't printed; in fact, it is only used in the
|
# the synopsis isn't printed; in fact, it is only used in the
|
||||||
# modindex currently
|
# modindex currently
|
||||||
@ -121,83 +143,104 @@ def module_directive(name, arguments, options, content, lineno,
|
|||||||
ret.insert(0, inode)
|
ret.insert(0, inode)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
module_directive.arguments = (1, 0, 0)
|
|
||||||
module_directive.options = {'platform': lambda x: x,
|
|
||||||
'synopsis': lambda x: x,
|
|
||||||
'noindex': directives.flag,
|
|
||||||
'deprecated': directives.flag}
|
|
||||||
directives.register_directive('module', module_directive)
|
|
||||||
|
|
||||||
|
class CurrentModule(Directive):
|
||||||
|
"""
|
||||||
|
This directive is just to tell Sphinx that we're documenting
|
||||||
|
stuff in module foo, but links to module foo won't lead here.
|
||||||
|
"""
|
||||||
|
|
||||||
def currentmodule_directive(name, arguments, options, content, lineno,
|
has_content = False
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 1
|
||||||
# This directive is just to tell people that we're documenting
|
optional_arguments = 0
|
||||||
# stuff in module foo, but links to module foo won't lead here.
|
final_argument_whitespace = False
|
||||||
env = state.document.settings.env
|
option_spec = {}
|
||||||
modname = arguments[0].strip()
|
|
||||||
|
def run(self):
|
||||||
|
env = self.state.document.settings.env
|
||||||
|
modname = self.arguments[0].strip()
|
||||||
if modname == 'None':
|
if modname == 'None':
|
||||||
env.currmodule = None
|
env.currmodule = None
|
||||||
else:
|
else:
|
||||||
env.currmodule = modname
|
env.currmodule = modname
|
||||||
return []
|
return []
|
||||||
|
|
||||||
currentmodule_directive.arguments = (1, 0, 0)
|
|
||||||
directives.register_directive('currentmodule', currentmodule_directive)
|
|
||||||
|
|
||||||
|
class Author(Directive):
|
||||||
|
"""
|
||||||
|
Directive to give the name of the author of the current document
|
||||||
|
or section. Shown in the output only if the show_authors option is on.
|
||||||
|
"""
|
||||||
|
|
||||||
def author_directive(name, arguments, options, content, lineno,
|
has_content = False
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 1
|
||||||
# Show authors only if the show_authors option is on
|
optional_arguments = 0
|
||||||
env = state.document.settings.env
|
final_argument_whitespace = True
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
env = self.state.document.settings.env
|
||||||
if not env.config.show_authors:
|
if not env.config.show_authors:
|
||||||
return []
|
return []
|
||||||
para = nodes.paragraph()
|
para = nodes.paragraph()
|
||||||
emph = nodes.emphasis()
|
emph = nodes.emphasis()
|
||||||
para += emph
|
para += emph
|
||||||
if name == 'sectionauthor':
|
if self.name == 'sectionauthor':
|
||||||
text = _('Section author: ')
|
text = _('Section author: ')
|
||||||
elif name == 'moduleauthor':
|
elif self.name == 'moduleauthor':
|
||||||
text = _('Module author: ')
|
text = _('Module author: ')
|
||||||
else:
|
else:
|
||||||
text = _('Author: ')
|
text = _('Author: ')
|
||||||
emph += nodes.Text(text, text)
|
emph += nodes.Text(text, text)
|
||||||
inodes, messages = state.inline_text(arguments[0], lineno)
|
inodes, messages = self.state.inline_text(self.arguments[0],
|
||||||
|
self.lineno)
|
||||||
emph.extend(inodes)
|
emph.extend(inodes)
|
||||||
return [para] + messages
|
return [para] + messages
|
||||||
|
|
||||||
author_directive.arguments = (1, 0, 1)
|
|
||||||
directives.register_directive('sectionauthor', author_directive)
|
|
||||||
directives.register_directive('moduleauthor', author_directive)
|
|
||||||
|
|
||||||
|
class Program(Directive):
|
||||||
|
"""
|
||||||
|
Directive to name the program for which options are documented.
|
||||||
|
"""
|
||||||
|
|
||||||
def program_directive(name, arguments, options, content, lineno,
|
has_content = False
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 1
|
||||||
env = state.document.settings.env
|
optional_arguments = 0
|
||||||
program = ws_re.sub('-', arguments[0].strip())
|
final_argument_whitespace = True
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
env = self.state.document.settings.env
|
||||||
|
program = ws_re.sub('-', self.arguments[0].strip())
|
||||||
if program == 'None':
|
if program == 'None':
|
||||||
env.currprogram = None
|
env.currprogram = None
|
||||||
else:
|
else:
|
||||||
env.currprogram = program
|
env.currprogram = program
|
||||||
return []
|
return []
|
||||||
|
|
||||||
program_directive.arguments = (1, 0, 1)
|
|
||||||
directives.register_directive('program', program_directive)
|
|
||||||
|
|
||||||
|
class Index(Directive):
|
||||||
|
"""
|
||||||
|
Directive to add entries to the index.
|
||||||
|
"""
|
||||||
|
|
||||||
# ------ index markup ----------------------------------------------------------
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
indextypes = [
|
indextypes = [
|
||||||
'single', 'pair', 'triple',
|
'single', 'pair', 'triple',
|
||||||
]
|
]
|
||||||
|
|
||||||
def index_directive(name, arguments, options, content, lineno,
|
def run(self):
|
||||||
content_offset, block_text, state, state_machine):
|
arguments = self.arguments[0].split('\n')
|
||||||
arguments = arguments[0].split('\n')
|
env = self.state.document.settings.env
|
||||||
env = state.document.settings.env
|
|
||||||
targetid = 'index-%s' % env.index_num
|
targetid = 'index-%s' % env.index_num
|
||||||
env.index_num += 1
|
env.index_num += 1
|
||||||
targetnode = nodes.target('', '', ids=[targetid])
|
targetnode = nodes.target('', '', ids=[targetid])
|
||||||
state.document.note_explicit_target(targetnode)
|
self.state.document.note_explicit_target(targetnode)
|
||||||
indexnode = addnodes.index()
|
indexnode = addnodes.index()
|
||||||
indexnode['entries'] = ne = []
|
indexnode['entries'] = ne = []
|
||||||
for entry in arguments:
|
for entry in arguments:
|
||||||
@ -209,7 +252,7 @@ def index_directive(name, arguments, options, content, lineno,
|
|||||||
ne.append(('pair', value, targetid, value))
|
ne.append(('pair', value, targetid, value))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
for type in indextypes:
|
for type in self.indextypes:
|
||||||
if entry.startswith(type+':'):
|
if entry.startswith(type+':'):
|
||||||
value = entry[len(type)+1:].strip()
|
value = entry[len(type)+1:].strip()
|
||||||
ne.append((type, value, targetid, value))
|
ne.append((type, value, targetid, value))
|
||||||
@ -223,58 +266,62 @@ def index_directive(name, arguments, options, content, lineno,
|
|||||||
ne.append(('single', value, targetid, value))
|
ne.append(('single', value, targetid, value))
|
||||||
return [indexnode, targetnode]
|
return [indexnode, targetnode]
|
||||||
|
|
||||||
index_directive.arguments = (1, 0, 1)
|
|
||||||
directives.register_directive('index', index_directive)
|
|
||||||
|
|
||||||
# ------ versionadded/versionchanged -------------------------------------------
|
class VersionChange(Directive):
|
||||||
|
"""
|
||||||
|
Directive to describe a change/addition/deprecation in a specific version.
|
||||||
|
"""
|
||||||
|
|
||||||
def version_directive(name, arguments, options, content, lineno,
|
has_content = True
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 1
|
||||||
|
optional_arguments = 1
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
node = addnodes.versionmodified()
|
node = addnodes.versionmodified()
|
||||||
node.document = state.document
|
node.document = self.state.document
|
||||||
node['type'] = name
|
node['type'] = self.name
|
||||||
node['version'] = arguments[0]
|
node['version'] = self.arguments[0]
|
||||||
if len(arguments) == 2:
|
if len(self.arguments) == 2:
|
||||||
inodes, messages = state.inline_text(arguments[1], lineno+1)
|
inodes, messages = self.state.inline_text(self.arguments[1],
|
||||||
|
self.lineno+1)
|
||||||
node.extend(inodes)
|
node.extend(inodes)
|
||||||
if content:
|
if self.content:
|
||||||
state.nested_parse(content, content_offset, node)
|
self.state.nested_parse(self.content, self.content_offset, node)
|
||||||
ret = [node] + messages
|
ret = [node] + messages
|
||||||
else:
|
else:
|
||||||
ret = [node]
|
ret = [node]
|
||||||
env = state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
env.note_versionchange(node['type'], node['version'], node, lineno)
|
env.note_versionchange(node['type'], node['version'], node, self.lineno)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
version_directive.arguments = (1, 1, 1)
|
|
||||||
version_directive.content = 1
|
|
||||||
|
|
||||||
directives.register_directive('deprecated', version_directive)
|
class SeeAlso(Directive):
|
||||||
directives.register_directive('versionadded', version_directive)
|
"""
|
||||||
directives.register_directive('versionchanged', version_directive)
|
An admonition mentioning things to look at as reference.
|
||||||
|
"""
|
||||||
|
|
||||||
|
has_content = True
|
||||||
|
required_arguments = 0
|
||||||
|
optional_arguments = 1
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
# ------ see also --------------------------------------------------------------
|
def run(self):
|
||||||
|
|
||||||
def seealso_directive(name, arguments, options, content, lineno,
|
|
||||||
content_offset, block_text, state, state_machine):
|
|
||||||
ret = make_admonition(
|
ret = make_admonition(
|
||||||
addnodes.seealso, name, [_('See also')], options, content,
|
addnodes.seealso, self.name, [_('See also')], self.options,
|
||||||
lineno, content_offset, block_text, state, state_machine)
|
self.content, self.lineno, self.content_offset, self.block_text,
|
||||||
if arguments:
|
self.state, self.state_machine)
|
||||||
argnodes, msgs = state.inline_text(arguments[0], lineno)
|
if self.arguments:
|
||||||
|
argnodes, msgs = self.state.inline_text(self.arguments[0],
|
||||||
|
self.lineno)
|
||||||
para = nodes.paragraph()
|
para = nodes.paragraph()
|
||||||
para += argnodes
|
para += argnodes
|
||||||
para += msgs
|
para += msgs
|
||||||
ret[0].insert(1, para)
|
ret[0].insert(1, para)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
seealso_directive.content = 1
|
|
||||||
seealso_directive.arguments = (0, 1, 1)
|
|
||||||
directives.register_directive('seealso', seealso_directive)
|
|
||||||
|
|
||||||
|
|
||||||
# ------ production list (for the reference) -----------------------------------
|
|
||||||
|
|
||||||
token_re = re.compile('`([a-z_]+)`')
|
token_re = re.compile('`([a-z_]+)`')
|
||||||
|
|
||||||
@ -297,14 +344,24 @@ def token_xrefs(text, env):
|
|||||||
retnodes.append(nodes.Text(text[pos:], text[pos:]))
|
retnodes.append(nodes.Text(text[pos:], text[pos:]))
|
||||||
return retnodes
|
return retnodes
|
||||||
|
|
||||||
def productionlist_directive(name, arguments, options, content, lineno,
|
class ProductionList(Directive):
|
||||||
content_offset, block_text, state, state_machine):
|
"""
|
||||||
env = state.document.settings.env
|
Directive to list grammar productions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
env = self.state.document.settings.env
|
||||||
node = addnodes.productionlist()
|
node = addnodes.productionlist()
|
||||||
messages = []
|
messages = []
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
for rule in arguments[0].split('\n'):
|
for rule in self.arguments[0].split('\n'):
|
||||||
if i == 0 and ':' not in rule:
|
if i == 0 and ':' not in rule:
|
||||||
# production group
|
# production group
|
||||||
continue
|
continue
|
||||||
@ -317,31 +374,36 @@ def productionlist_directive(name, arguments, options, content, lineno,
|
|||||||
subnode['tokenname'] = name.strip()
|
subnode['tokenname'] = name.strip()
|
||||||
if subnode['tokenname']:
|
if subnode['tokenname']:
|
||||||
idname = 'grammar-token-%s' % subnode['tokenname']
|
idname = 'grammar-token-%s' % subnode['tokenname']
|
||||||
if idname not in state.document.ids:
|
if idname not in self.state.document.ids:
|
||||||
subnode['ids'].append(idname)
|
subnode['ids'].append(idname)
|
||||||
state.document.note_implicit_target(subnode, subnode)
|
self.state.document.note_implicit_target(subnode, subnode)
|
||||||
env.note_reftarget('token', subnode['tokenname'], idname)
|
env.note_reftarget('token', subnode['tokenname'], idname)
|
||||||
subnode.extend(token_xrefs(tokens, env))
|
subnode.extend(token_xrefs(tokens, env))
|
||||||
node.append(subnode)
|
node.append(subnode)
|
||||||
return [node] + messages
|
return [node] + messages
|
||||||
|
|
||||||
productionlist_directive.content = 0
|
|
||||||
productionlist_directive.arguments = (1, 0, 1)
|
|
||||||
directives.register_directive('productionlist', productionlist_directive)
|
|
||||||
|
|
||||||
|
class Glossary(Directive):
|
||||||
|
"""
|
||||||
|
Directive to create a glossary with cross-reference targets
|
||||||
|
for :term: roles.
|
||||||
|
"""
|
||||||
|
|
||||||
# ------ glossary directive ----------------------------------------------------
|
has_content = True
|
||||||
|
required_arguments = 0
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
def glossary_directive(name, arguments, options, content, lineno,
|
def run(self):
|
||||||
content_offset, block_text, state, state_machine):
|
env = self.state.document.settings.env
|
||||||
"""Glossary with cross-reference targets for :term: roles."""
|
|
||||||
env = state.document.settings.env
|
|
||||||
node = addnodes.glossary()
|
node = addnodes.glossary()
|
||||||
node.document = state.document
|
node.document = self.state.document
|
||||||
state.nested_parse(content, content_offset, node)
|
self.state.nested_parse(self.content, self.content_offset, node)
|
||||||
|
|
||||||
# the content should be definition lists
|
# the content should be definition lists
|
||||||
dls = [child for child in node if isinstance(child, nodes.definition_list)]
|
dls = [child for child in node
|
||||||
|
if isinstance(child, nodes.definition_list)]
|
||||||
# now, extract definition terms to enable cross-reference creation
|
# now, extract definition terms to enable cross-reference creation
|
||||||
for dl in dls:
|
for dl in dls:
|
||||||
dl['classes'].append('glossary')
|
dl['classes'].append('glossary')
|
||||||
@ -355,60 +417,80 @@ def glossary_directive(name, arguments, options, content, lineno,
|
|||||||
env.gloss_entries.add(new_id)
|
env.gloss_entries.add(new_id)
|
||||||
li[0]['names'].append(new_id)
|
li[0]['names'].append(new_id)
|
||||||
li[0]['ids'].append(new_id)
|
li[0]['ids'].append(new_id)
|
||||||
state.document.settings.env.note_reftarget('term', termtext.lower(),
|
env.note_reftarget('term', termtext.lower(), new_id)
|
||||||
new_id)
|
|
||||||
# add an index entry too
|
# add an index entry too
|
||||||
indexnode = addnodes.index()
|
indexnode = addnodes.index()
|
||||||
indexnode['entries'] = [('single', termtext, new_id, termtext)]
|
indexnode['entries'] = [('single', termtext, new_id, termtext)]
|
||||||
li.insert(0, indexnode)
|
li.insert(0, indexnode)
|
||||||
return [node]
|
return [node]
|
||||||
|
|
||||||
glossary_directive.content = 1
|
|
||||||
glossary_directive.arguments = (0, 0, 0)
|
|
||||||
directives.register_directive('glossary', glossary_directive)
|
|
||||||
|
|
||||||
|
class Centered(Directive):
|
||||||
|
"""
|
||||||
|
Directive to create a centered line of bold text.
|
||||||
|
"""
|
||||||
|
|
||||||
# ------ miscellaneous markup --------------------------------------------------
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
def centered_directive(name, arguments, options, content, lineno,
|
def run(self):
|
||||||
content_offset, block_text, state, state_machine):
|
if not self.arguments:
|
||||||
if not arguments:
|
|
||||||
return []
|
return []
|
||||||
subnode = addnodes.centered()
|
subnode = addnodes.centered()
|
||||||
inodes, messages = state.inline_text(arguments[0], lineno)
|
inodes, messages = self.state.inline_text(self.arguments[0],
|
||||||
|
self.lineno)
|
||||||
subnode.extend(inodes)
|
subnode.extend(inodes)
|
||||||
return [subnode] + messages
|
return [subnode] + messages
|
||||||
|
|
||||||
centered_directive.arguments = (1, 0, 1)
|
|
||||||
directives.register_directive('centered', centered_directive)
|
|
||||||
|
|
||||||
|
|
||||||
def acks_directive(name, arguments, options, content, lineno,
|
class Acks(Directive):
|
||||||
content_offset, block_text, state, state_machine):
|
"""
|
||||||
|
Directive for a list of names.
|
||||||
|
"""
|
||||||
|
|
||||||
|
has_content = True
|
||||||
|
required_arguments = 0
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
node = addnodes.acks()
|
node = addnodes.acks()
|
||||||
node.document = state.document
|
node.document = self.state.document
|
||||||
state.nested_parse(content, content_offset, node)
|
self.state.nested_parse(self.content, self.content_offset, node)
|
||||||
if len(node.children) != 1 or not isinstance(node.children[0],
|
if len(node.children) != 1 or not isinstance(node.children[0],
|
||||||
nodes.bullet_list):
|
nodes.bullet_list):
|
||||||
return [state.document.reporter.warning('.. acks content is not a list',
|
return [self.state.document.reporter.warning(
|
||||||
line=lineno)]
|
'.. acks content is not a list', line=self.lineno)]
|
||||||
return [node]
|
return [node]
|
||||||
|
|
||||||
acks_directive.content = 1
|
|
||||||
acks_directive.arguments = (0, 0, 0)
|
|
||||||
directives.register_directive('acks', acks_directive)
|
|
||||||
|
|
||||||
|
class HList(Directive):
|
||||||
|
"""
|
||||||
|
Directive for a list that gets compacted horizontally.
|
||||||
|
"""
|
||||||
|
|
||||||
def hlist_directive(name, arguments, options, content, lineno,
|
has_content = True
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 0
|
||||||
ncolumns = options.get('columns', 2)
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {
|
||||||
|
'columns': int,
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
ncolumns = self.options.get('columns', 2)
|
||||||
node = nodes.paragraph()
|
node = nodes.paragraph()
|
||||||
node.document = state.document
|
node.document = self.state.document
|
||||||
state.nested_parse(content, content_offset, node)
|
self.state.nested_parse(self.content, self.content_offset, node)
|
||||||
if len(node.children) != 1 or not isinstance(node.children[0],
|
if len(node.children) != 1 or not isinstance(node.children[0],
|
||||||
nodes.bullet_list):
|
nodes.bullet_list):
|
||||||
return [state.document.reporter.warning(
|
return [self.state.document.reporter.warning(
|
||||||
'.. hlist content is not a list', line=lineno)]
|
'.. hlist content is not a list', line=self.lineno)]
|
||||||
fulllist = node.children[0]
|
fulllist = node.children[0]
|
||||||
# create a hlist node where the items are distributed
|
# create a hlist node where the items are distributed
|
||||||
npercol, nmore = divmod(len(fulllist), ncolumns)
|
npercol, nmore = divmod(len(fulllist), ncolumns)
|
||||||
@ -423,23 +505,41 @@ def hlist_directive(name, arguments, options, content, lineno,
|
|||||||
newnode += col
|
newnode += col
|
||||||
return [newnode]
|
return [newnode]
|
||||||
|
|
||||||
hlist_directive.content = 1
|
|
||||||
hlist_directive.arguments = (0, 0, 0)
|
|
||||||
hlist_directive.options = {'columns': int}
|
|
||||||
directives.register_directive('hlist', hlist_directive)
|
|
||||||
|
|
||||||
|
class TabularColumns(Directive):
|
||||||
|
"""
|
||||||
|
Directive to give an explicit tabulary column definition to LaTeX.
|
||||||
|
"""
|
||||||
|
|
||||||
def tabularcolumns_directive(name, arguments, options, content, lineno,
|
has_content = False
|
||||||
content_offset, block_text, state, state_machine):
|
required_arguments = 1
|
||||||
# support giving explicit tabulary column definition to latex
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
node = addnodes.tabular_col_spec()
|
node = addnodes.tabular_col_spec()
|
||||||
node['spec'] = arguments[0]
|
node['spec'] = self.arguments[0]
|
||||||
return [node]
|
return [node]
|
||||||
|
|
||||||
tabularcolumns_directive.content = 0
|
|
||||||
tabularcolumns_directive.arguments = (1, 0, 1)
|
|
||||||
directives.register_directive('tabularcolumns', tabularcolumns_directive)
|
|
||||||
|
|
||||||
|
directives.register_directive('toctree', TocTree)
|
||||||
|
directives.register_directive('module', Module)
|
||||||
|
directives.register_directive('currentmodule', CurrentModule)
|
||||||
|
directives.register_directive('sectionauthor', Author)
|
||||||
|
directives.register_directive('moduleauthor', Author)
|
||||||
|
directives.register_directive('program', Program)
|
||||||
|
directives.register_directive('index', Index)
|
||||||
|
directives.register_directive('deprecated', VersionChange)
|
||||||
|
directives.register_directive('versionadded', VersionChange)
|
||||||
|
directives.register_directive('versionchanged', VersionChange)
|
||||||
|
directives.register_directive('seealso', SeeAlso)
|
||||||
|
directives.register_directive('productionlist', ProductionList)
|
||||||
|
directives.register_directive('glossary', Glossary)
|
||||||
|
directives.register_directive('centered', Centered)
|
||||||
|
directives.register_directive('acks', Acks)
|
||||||
|
directives.register_directive('hlist', HList)
|
||||||
|
directives.register_directive('tabularcolumns', TabularColumns)
|
||||||
|
|
||||||
# register the standard rst class directive under a different name
|
# register the standard rst class directive under a different name
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user