mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
1519 lines
46 KiB
Python
1519 lines
46 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
sphinx.writers.texinfo
|
||
~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Custom docutils writer for Texinfo.
|
||
|
||
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||
:license: BSD, see LICENSE for details.
|
||
"""
|
||
|
||
import re
|
||
import textwrap
|
||
from os import path
|
||
import warnings
|
||
|
||
from six import itervalues
|
||
from six.moves import range
|
||
from docutils import nodes, writers
|
||
|
||
from sphinx import addnodes, __display_version__
|
||
from sphinx.locale import admonitionlabels, _
|
||
from sphinx.util import ustrftime
|
||
from sphinx.writers.latex import collected_footnote
|
||
|
||
|
||
COPYING = """\
|
||
@quotation
|
||
%(project)s %(release)s, %(date)s
|
||
|
||
%(author)s
|
||
|
||
Copyright @copyright{} %(copyright)s
|
||
@end quotation
|
||
"""
|
||
|
||
TEMPLATE = """\
|
||
\\input texinfo @c -*-texinfo-*-
|
||
@c %%**start of header
|
||
@setfilename %(filename)s
|
||
@documentencoding UTF-8
|
||
@ifinfo
|
||
@*Generated by Sphinx """ + __display_version__ + """.@*
|
||
@end ifinfo
|
||
@settitle %(title)s
|
||
@defindex ge
|
||
@paragraphindent %(paragraphindent)s
|
||
@exampleindent %(exampleindent)s
|
||
@finalout
|
||
%(direntry)s
|
||
@definfoenclose strong,`,'
|
||
@definfoenclose emph,`,'
|
||
@c %%**end of header
|
||
|
||
@copying
|
||
%(copying)s
|
||
@end copying
|
||
|
||
@titlepage
|
||
@title %(title)s
|
||
@insertcopying
|
||
@end titlepage
|
||
@contents
|
||
|
||
@c %%** start of user preamble
|
||
%(preamble)s
|
||
@c %%** end of user preamble
|
||
|
||
@ifnottex
|
||
@node Top
|
||
@top %(title)s
|
||
@insertcopying
|
||
@end ifnottex
|
||
|
||
@c %%**start of body
|
||
%(body)s
|
||
@c %%**end of body
|
||
@bye
|
||
"""
|
||
|
||
|
||
def find_subsections(section):
|
||
"""Return a list of subsections for the given ``section``."""
|
||
result = []
|
||
for child in section.children:
|
||
if isinstance(child, nodes.section):
|
||
result.append(child)
|
||
continue
|
||
result.extend(find_subsections(child))
|
||
return result
|
||
|
||
|
||
def smart_capwords(s, sep=None):
|
||
"""Like string.capwords() but does not capitalize words that already
|
||
contain a capital letter."""
|
||
words = s.split(sep)
|
||
for i, word in enumerate(words):
|
||
if all(x.islower() for x in word):
|
||
words[i] = word.capitalize()
|
||
return (sep or ' ').join(words)
|
||
|
||
|
||
class TexinfoWriter(writers.Writer):
|
||
"""Texinfo writer for generating Texinfo documents."""
|
||
supported = ('texinfo', 'texi')
|
||
|
||
settings_spec = (
|
||
'Texinfo Specific Options', None, (
|
||
("Name of the Info file", ['--texinfo-filename'], {'default': ''}),
|
||
('Dir entry', ['--texinfo-dir-entry'], {'default': ''}),
|
||
('Description', ['--texinfo-dir-description'], {'default': ''}),
|
||
('Category', ['--texinfo-dir-category'], {'default':
|
||
'Miscellaneous'})))
|
||
|
||
settings_defaults = {}
|
||
|
||
output = None
|
||
|
||
visitor_attributes = ('output', 'fragment')
|
||
|
||
def __init__(self, builder):
|
||
writers.Writer.__init__(self)
|
||
self.builder = builder
|
||
self.translator_class = (
|
||
self.builder.translator_class or TexinfoTranslator)
|
||
|
||
def translate(self):
|
||
self.visitor = visitor = self.translator_class(
|
||
self.document, self.builder)
|
||
self.document.walkabout(visitor)
|
||
visitor.finish()
|
||
for attr in self.visitor_attributes:
|
||
setattr(self, attr, getattr(visitor, attr))
|
||
|
||
|
||
class TexinfoTranslator(nodes.NodeVisitor):
|
||
|
||
ignore_missing_images = False
|
||
|
||
default_elements = {
|
||
'author': '',
|
||
'body': '',
|
||
'copying': '',
|
||
'date': '',
|
||
'direntry': '',
|
||
'exampleindent': 4,
|
||
'filename': '',
|
||
'paragraphindent': 0,
|
||
'preamble': '',
|
||
'project': '',
|
||
'release': '',
|
||
'title': '',
|
||
}
|
||
|
||
def __init__(self, document, builder):
|
||
nodes.NodeVisitor.__init__(self, document)
|
||
self.builder = builder
|
||
self.init_settings()
|
||
|
||
self.written_ids = set() # node names and anchors in output
|
||
# node names and anchors that should be in output
|
||
self.referenced_ids = set()
|
||
self.indices = [] # (node name, content)
|
||
self.short_ids = {} # anchors --> short ids
|
||
self.node_names = {} # node name --> node's name to display
|
||
self.node_menus = {} # node name --> node's menu entries
|
||
self.rellinks = {} # node name --> (next, previous, up)
|
||
|
||
self.collect_indices()
|
||
self.collect_node_names()
|
||
self.collect_node_menus()
|
||
self.collect_rellinks()
|
||
|
||
self.body = []
|
||
self.context = []
|
||
self.previous_section = None
|
||
self.section_level = 0
|
||
self.seen_title = False
|
||
self.next_section_ids = set()
|
||
self.escape_newlines = 0
|
||
self.escape_hyphens = 0
|
||
self.curfilestack = []
|
||
self.footnotestack = []
|
||
self.in_footnote = 0
|
||
self.handled_abbrs = set()
|
||
|
||
def finish(self):
|
||
if self.previous_section is None:
|
||
self.add_menu('Top')
|
||
for index in self.indices:
|
||
name, content = index
|
||
pointers = tuple([name] + self.rellinks[name])
|
||
self.body.append('\n@node %s,%s,%s,%s\n' % pointers)
|
||
self.body.append('@unnumbered %s\n\n%s\n' % (name, content))
|
||
|
||
while self.referenced_ids:
|
||
# handle xrefs with missing anchors
|
||
r = self.referenced_ids.pop()
|
||
if r not in self.written_ids:
|
||
self.body.append('@anchor{%s}@w{%s}\n' % (r, ' ' * 30))
|
||
self.ensure_eol()
|
||
self.fragment = ''.join(self.body)
|
||
self.elements['body'] = self.fragment
|
||
self.output = TEMPLATE % self.elements
|
||
|
||
# -- Helper routines
|
||
|
||
def init_settings(self):
|
||
settings = self.settings = self.document.settings
|
||
elements = self.elements = self.default_elements.copy()
|
||
elements.update({
|
||
# if empty, the title is set to the first section title
|
||
'title': settings.title,
|
||
'author': settings.author,
|
||
# if empty, use basename of input file
|
||
'filename': settings.texinfo_filename,
|
||
'release': self.escape(self.builder.config.release),
|
||
'project': self.escape(self.builder.config.project),
|
||
'copyright': self.escape(self.builder.config.copyright),
|
||
'date': self.escape(self.builder.config.today or
|
||
ustrftime(self.builder.config.today_fmt or
|
||
_('%B %d, %Y')))
|
||
})
|
||
# title
|
||
title = elements['title']
|
||
if not title:
|
||
title = self.document.next_node(nodes.title)
|
||
title = (title and title.astext()) or '<untitled>'
|
||
elements['title'] = self.escape_id(title) or '<untitled>'
|
||
# filename
|
||
if not elements['filename']:
|
||
elements['filename'] = self.document.get('source') or 'untitled'
|
||
if elements['filename'][-4:] in ('.txt', '.rst'):
|
||
elements['filename'] = elements['filename'][:-4]
|
||
elements['filename'] += '.info'
|
||
# direntry
|
||
if settings.texinfo_dir_entry:
|
||
entry = self.format_menu_entry(
|
||
self.escape_menu(settings.texinfo_dir_entry),
|
||
'(%s)' % elements['filename'],
|
||
self.escape_arg(settings.texinfo_dir_description))
|
||
elements['direntry'] = ('@dircategory %s\n'
|
||
'@direntry\n'
|
||
'%s'
|
||
'@end direntry\n') % (
|
||
self.escape_id(settings.texinfo_dir_category), entry)
|
||
elements['copying'] = COPYING % elements
|
||
# allow the user to override them all
|
||
elements.update(settings.texinfo_elements)
|
||
|
||
def collect_node_names(self):
|
||
"""Generates a unique id for each section.
|
||
|
||
Assigns the attribute ``node_name`` to each section."""
|
||
|
||
def add_node_name(name):
|
||
node_id = self.escape_id(name)
|
||
nth, suffix = 1, ''
|
||
while node_id + suffix in self.written_ids or \
|
||
node_id + suffix in self.node_names:
|
||
nth += 1
|
||
suffix = '<%s>' % nth
|
||
node_id += suffix
|
||
self.written_ids.add(node_id)
|
||
self.node_names[node_id] = name
|
||
return node_id
|
||
|
||
# must have a "Top" node
|
||
self.document['node_name'] = 'Top'
|
||
add_node_name('Top')
|
||
add_node_name('top')
|
||
# each index is a node
|
||
self.indices = [(add_node_name(name), content)
|
||
for name, content in self.indices]
|
||
# each section is also a node
|
||
for section in self.document.traverse(nodes.section):
|
||
title = section.next_node(nodes.Titular)
|
||
name = (title and title.astext()) or '<untitled>'
|
||
section['node_name'] = add_node_name(name)
|
||
|
||
def collect_node_menus(self):
|
||
"""Collect the menu entries for each "node" section."""
|
||
node_menus = self.node_menus
|
||
for node in ([self.document] +
|
||
self.document.traverse(nodes.section)):
|
||
assert 'node_name' in node and node['node_name']
|
||
entries = [s['node_name'] for s in find_subsections(node)]
|
||
node_menus[node['node_name']] = entries
|
||
# try to find a suitable "Top" node
|
||
title = self.document.next_node(nodes.title)
|
||
top = (title and title.parent) or self.document
|
||
if not isinstance(top, (nodes.document, nodes.section)):
|
||
top = self.document
|
||
if top is not self.document:
|
||
entries = node_menus[top['node_name']]
|
||
entries += node_menus['Top'][1:]
|
||
node_menus['Top'] = entries
|
||
del node_menus[top['node_name']]
|
||
top['node_name'] = 'Top'
|
||
# handle the indices
|
||
for name, content in self.indices:
|
||
node_menus[name] = ()
|
||
node_menus['Top'].append(name)
|
||
|
||
def collect_rellinks(self):
|
||
"""Collect the relative links (next, previous, up) for each "node"."""
|
||
rellinks = self.rellinks
|
||
node_menus = self.node_menus
|
||
for id, entries in node_menus.items():
|
||
rellinks[id] = ['', '', '']
|
||
# up's
|
||
for id, entries in node_menus.items():
|
||
for e in entries:
|
||
rellinks[e][2] = id
|
||
# next's and prev's
|
||
for id, entries in node_menus.items():
|
||
for i, id in enumerate(entries):
|
||
# First child's prev is empty
|
||
if i != 0:
|
||
rellinks[id][1] = entries[i-1]
|
||
# Last child's next is empty
|
||
if i != len(entries) - 1:
|
||
rellinks[id][0] = entries[i+1]
|
||
# top's next is its first child
|
||
try:
|
||
first = node_menus['Top'][0]
|
||
except IndexError:
|
||
pass
|
||
else:
|
||
rellinks['Top'][0] = first
|
||
rellinks[first][1] = 'Top'
|
||
|
||
# -- Escaping
|
||
# Which characters to escape depends on the context. In some cases,
|
||
# namely menus and node names, it's not possible to escape certain
|
||
# characters.
|
||
|
||
def escape(self, s):
|
||
"""Return a string with Texinfo command characters escaped."""
|
||
s = s.replace('@', '@@')
|
||
s = s.replace('{', '@{')
|
||
s = s.replace('}', '@}')
|
||
# prevent `` and '' quote conversion
|
||
s = s.replace('``', "`@w{`}")
|
||
s = s.replace("''", "'@w{'}")
|
||
return s
|
||
|
||
def escape_arg(self, s):
|
||
"""Return an escaped string suitable for use as an argument
|
||
to a Texinfo command."""
|
||
s = self.escape(s)
|
||
# commas are the argument delimeters
|
||
s = s.replace(',', '@comma{}')
|
||
# normalize white space
|
||
s = ' '.join(s.split()).strip()
|
||
return s
|
||
|
||
def escape_id(self, s):
|
||
"""Return an escaped string suitable for node names and anchors."""
|
||
bad_chars = ',:.()'
|
||
for bc in bad_chars:
|
||
s = s.replace(bc, ' ')
|
||
s = ' '.join(s.split()).strip()
|
||
return self.escape(s)
|
||
|
||
def escape_menu(self, s):
|
||
"""Return an escaped string suitable for menu entries."""
|
||
s = self.escape_arg(s)
|
||
s = s.replace(':', ';')
|
||
s = ' '.join(s.split()).strip()
|
||
return s
|
||
|
||
def ensure_eol(self):
|
||
"""Ensure the last line in body is terminated by new line."""
|
||
if self.body and self.body[-1][-1:] != '\n':
|
||
self.body.append('\n')
|
||
|
||
def format_menu_entry(self, name, node_name, desc):
|
||
if name == node_name:
|
||
s = '* %s:: ' % (name,)
|
||
else:
|
||
s = '* %s: %s. ' % (name, node_name)
|
||
offset = max((24, (len(name) + 4) % 78))
|
||
wdesc = '\n'.join(' ' * offset + l for l in
|
||
textwrap.wrap(desc, width=78-offset))
|
||
return s + wdesc.strip() + '\n'
|
||
|
||
def add_menu_entries(self, entries, reg=re.compile(r'\s+---?\s+')):
|
||
for entry in entries:
|
||
name = self.node_names[entry]
|
||
# special formatting for entries that are divided by an em-dash
|
||
try:
|
||
parts = reg.split(name, 1)
|
||
except TypeError:
|
||
# could be a gettext proxy
|
||
parts = [name]
|
||
if len(parts) == 2:
|
||
name, desc = parts
|
||
else:
|
||
desc = ''
|
||
name = self.escape_menu(name)
|
||
desc = self.escape(desc)
|
||
self.body.append(self.format_menu_entry(name, entry, desc))
|
||
|
||
def add_menu(self, node_name):
|
||
entries = self.node_menus[node_name]
|
||
if not entries:
|
||
return
|
||
self.body.append('\n@menu\n')
|
||
self.add_menu_entries(entries)
|
||
if (node_name != 'Top' or
|
||
not self.node_menus[entries[0]] or
|
||
self.builder.config.texinfo_no_detailmenu):
|
||
self.body.append('\n@end menu\n')
|
||
return
|
||
|
||
def _add_detailed_menu(name):
|
||
entries = self.node_menus[name]
|
||
if not entries:
|
||
return
|
||
self.body.append('\n%s\n\n' % (self.escape(self.node_names[name],)))
|
||
self.add_menu_entries(entries)
|
||
for subentry in entries:
|
||
_add_detailed_menu(subentry)
|
||
|
||
self.body.append('\n@detailmenu\n'
|
||
' --- The Detailed Node Listing ---\n')
|
||
for entry in entries:
|
||
_add_detailed_menu(entry)
|
||
self.body.append('\n@end detailmenu\n'
|
||
'@end menu\n')
|
||
|
||
def tex_image_length(self, width_str):
|
||
match = re.match('(\d*\.?\d*)\s*(\S*)', width_str)
|
||
if not match:
|
||
# fallback
|
||
return width_str
|
||
res = width_str
|
||
amount, unit = match.groups()[:2]
|
||
if not unit or unit == "px":
|
||
# pixels: let TeX alone
|
||
return ''
|
||
elif unit == "%":
|
||
# a4paper: textwidth=418.25368pt
|
||
res = "%d.0pt" % (float(amount) * 4.1825368)
|
||
return res
|
||
|
||
def collect_indices(self):
|
||
def generate(content, collapsed):
|
||
ret = ['\n@menu\n']
|
||
for letter, entries in content:
|
||
for entry in entries:
|
||
if not entry[3]:
|
||
continue
|
||
name = self.escape_menu(entry[0])
|
||
sid = self.get_short_id('%s:%s' % (entry[2], entry[3]))
|
||
desc = self.escape_arg(entry[6])
|
||
me = self.format_menu_entry(name, sid, desc)
|
||
ret.append(me)
|
||
ret.append('@end menu\n')
|
||
return ''.join(ret)
|
||
|
||
indices_config = self.builder.config.texinfo_domain_indices
|
||
if indices_config:
|
||
for domain in itervalues(self.builder.env.domains):
|
||
for indexcls in domain.indices:
|
||
indexname = '%s-%s' % (domain.name, indexcls.name)
|
||
if isinstance(indices_config, list):
|
||
if indexname not in indices_config:
|
||
continue
|
||
content, collapsed = indexcls(domain).generate(
|
||
self.builder.docnames)
|
||
if not content:
|
||
continue
|
||
self.indices.append((indexcls.localname,
|
||
generate(content, collapsed)))
|
||
# only add the main Index if it's not empty
|
||
for docname in self.builder.docnames:
|
||
if self.builder.env.indexentries[docname]:
|
||
self.indices.append((_('Index'), '\n@printindex ge\n'))
|
||
break
|
||
|
||
# this is copied from the latex writer
|
||
# TODO: move this to sphinx.util
|
||
|
||
def collect_footnotes(self, node):
|
||
def footnotes_under(n):
|
||
if isinstance(n, nodes.footnote):
|
||
yield n
|
||
else:
|
||
for c in n.children:
|
||
if isinstance(c, addnodes.start_of_file):
|
||
continue
|
||
for k in footnotes_under(c):
|
||
yield k
|
||
fnotes = {}
|
||
for fn in footnotes_under(node):
|
||
num = fn.children[0].astext().strip()
|
||
fnotes[num] = [collected_footnote(*fn.children), False]
|
||
return fnotes
|
||
|
||
# -- xref handling
|
||
|
||
def get_short_id(self, id):
|
||
"""Return a shorter 'id' associated with ``id``."""
|
||
# Shorter ids improve paragraph filling in places
|
||
# that the id is hidden by Emacs.
|
||
try:
|
||
sid = self.short_ids[id]
|
||
except KeyError:
|
||
sid = hex(len(self.short_ids))[2:]
|
||
self.short_ids[id] = sid
|
||
return sid
|
||
|
||
def add_anchor(self, id, node):
|
||
if id.startswith('index-'):
|
||
return
|
||
id = self.curfilestack[-1] + ':' + id
|
||
eid = self.escape_id(id)
|
||
sid = self.get_short_id(id)
|
||
for id in (eid, sid):
|
||
if id not in self.written_ids:
|
||
self.body.append('@anchor{%s}' % id)
|
||
self.written_ids.add(id)
|
||
|
||
def add_xref(self, id, name, node):
|
||
name = self.escape_menu(name)
|
||
sid = self.get_short_id(id)
|
||
self.body.append('@ref{%s,,%s}' % (sid, name))
|
||
self.referenced_ids.add(sid)
|
||
self.referenced_ids.add(self.escape_id(id))
|
||
|
||
# -- Visiting
|
||
|
||
def visit_document(self, node):
|
||
self.footnotestack.append(self.collect_footnotes(node))
|
||
self.curfilestack.append(node.get('docname', ''))
|
||
if 'docname' in node:
|
||
self.add_anchor(':doc', node)
|
||
|
||
def depart_document(self, node):
|
||
self.footnotestack.pop()
|
||
self.curfilestack.pop()
|
||
|
||
def visit_Text(self, node):
|
||
s = self.escape(node.astext())
|
||
if self.escape_newlines:
|
||
s = s.replace('\n', ' ')
|
||
if self.escape_hyphens:
|
||
# prevent "--" and "---" conversion
|
||
s = s.replace('-', '@w{-}')
|
||
self.body.append(s)
|
||
|
||
def depart_Text(self, node):
|
||
pass
|
||
|
||
def visit_section(self, node):
|
||
self.next_section_ids.update(node.get('ids', []))
|
||
if not self.seen_title:
|
||
return
|
||
if self.previous_section:
|
||
self.add_menu(self.previous_section['node_name'])
|
||
else:
|
||
self.add_menu('Top')
|
||
|
||
node_name = node['node_name']
|
||
pointers = tuple([node_name] + self.rellinks[node_name])
|
||
self.body.append('\n@node %s,%s,%s,%s\n' % pointers)
|
||
for id in self.next_section_ids:
|
||
self.add_anchor(id, node)
|
||
|
||
self.next_section_ids.clear()
|
||
self.previous_section = node
|
||
self.section_level += 1
|
||
|
||
def depart_section(self, node):
|
||
self.section_level -= 1
|
||
|
||
headings = (
|
||
'@unnumbered',
|
||
'@chapter',
|
||
'@section',
|
||
'@subsection',
|
||
'@subsubsection',
|
||
)
|
||
|
||
rubrics = (
|
||
'@heading',
|
||
'@subheading',
|
||
'@subsubheading',
|
||
)
|
||
|
||
def visit_title(self, node):
|
||
if not self.seen_title:
|
||
self.seen_title = 1
|
||
raise nodes.SkipNode
|
||
parent = node.parent
|
||
if isinstance(parent, nodes.table):
|
||
return
|
||
if isinstance(parent, (nodes.Admonition, nodes.sidebar, nodes.topic)):
|
||
raise nodes.SkipNode
|
||
elif not isinstance(parent, nodes.section):
|
||
self.builder.warn(
|
||
'encountered title node not in section, topic, table, '
|
||
'admonition or sidebar', (self.curfilestack[-1], node.line))
|
||
self.visit_rubric(node)
|
||
else:
|
||
try:
|
||
heading = self.headings[self.section_level]
|
||
except IndexError:
|
||
heading = self.headings[-1]
|
||
self.body.append('\n%s ' % heading)
|
||
|
||
def depart_title(self, node):
|
||
self.body.append('\n\n')
|
||
|
||
def visit_rubric(self, node):
|
||
if len(node.children) == 1 and node.children[0].astext() in \
|
||
('Footnotes', _('Footnotes')):
|
||
raise nodes.SkipNode
|
||
try:
|
||
rubric = self.rubrics[self.section_level]
|
||
except IndexError:
|
||
rubric = self.rubrics[-1]
|
||
self.body.append('\n%s ' % rubric)
|
||
|
||
def depart_rubric(self, node):
|
||
self.body.append('\n\n')
|
||
|
||
def visit_subtitle(self, node):
|
||
self.body.append('\n\n@noindent\n')
|
||
|
||
def depart_subtitle(self, node):
|
||
self.body.append('\n\n')
|
||
|
||
# -- References
|
||
|
||
def visit_target(self, node):
|
||
# postpone the labels until after the sectioning command
|
||
parindex = node.parent.index(node)
|
||
try:
|
||
try:
|
||
next = node.parent[parindex+1]
|
||
except IndexError:
|
||
# last node in parent, look at next after parent
|
||
# (for section of equal level)
|
||
next = node.parent.parent[node.parent.parent.index(node.parent)]
|
||
if isinstance(next, nodes.section):
|
||
if node.get('refid'):
|
||
self.next_section_ids.add(node['refid'])
|
||
self.next_section_ids.update(node['ids'])
|
||
return
|
||
except IndexError:
|
||
pass
|
||
if 'refuri' in node:
|
||
return
|
||
if node.get('refid'):
|
||
self.add_anchor(node['refid'], node)
|
||
for id in node['ids']:
|
||
self.add_anchor(id, node)
|
||
|
||
def depart_target(self, node):
|
||
pass
|
||
|
||
def visit_reference(self, node):
|
||
# an xref's target is displayed in Info so we ignore a few
|
||
# cases for the sake of appearance
|
||
if isinstance(node.parent, (nodes.title, addnodes.desc_type)):
|
||
return
|
||
if isinstance(node[0], nodes.image):
|
||
return
|
||
name = node.get('name', node.astext()).strip()
|
||
uri = node.get('refuri', '')
|
||
if not uri and node.get('refid'):
|
||
uri = '%' + self.curfilestack[-1] + '#' + node['refid']
|
||
if not uri:
|
||
return
|
||
if uri.startswith('mailto:'):
|
||
uri = self.escape_arg(uri[7:])
|
||
name = self.escape_arg(name)
|
||
if not name or name == uri:
|
||
self.body.append('@email{%s}' % uri)
|
||
else:
|
||
self.body.append('@email{%s,%s}' % (uri, name))
|
||
elif uri.startswith('#'):
|
||
# references to labels in the same document
|
||
id = self.curfilestack[-1] + ':' + uri[1:]
|
||
self.add_xref(id, name, node)
|
||
elif uri.startswith('%'):
|
||
# references to documents or labels inside documents
|
||
hashindex = uri.find('#')
|
||
if hashindex == -1:
|
||
# reference to the document
|
||
id = uri[1:] + '::doc'
|
||
else:
|
||
# reference to a label
|
||
id = uri[1:].replace('#', ':')
|
||
self.add_xref(id, name, node)
|
||
elif uri.startswith('info:'):
|
||
# references to an external Info file
|
||
uri = uri[5:].replace('_', ' ')
|
||
uri = self.escape_arg(uri)
|
||
id = 'Top'
|
||
if '#' in uri:
|
||
uri, id = uri.split('#', 1)
|
||
id = self.escape_id(id)
|
||
name = self.escape_menu(name)
|
||
if name == id:
|
||
self.body.append('@ref{%s,,,%s}' % (id, uri))
|
||
else:
|
||
self.body.append('@ref{%s,,%s,%s}' % (id, name, uri))
|
||
else:
|
||
uri = self.escape_arg(uri)
|
||
name = self.escape_arg(name)
|
||
show_urls = self.builder.config.texinfo_show_urls
|
||
if self.in_footnote:
|
||
show_urls = 'inline'
|
||
if not name or uri == name:
|
||
self.body.append('@indicateurl{%s}' % uri)
|
||
elif show_urls == 'inline':
|
||
self.body.append('@uref{%s,%s}' % (uri, name))
|
||
elif show_urls == 'no':
|
||
self.body.append('@uref{%s,,%s}' % (uri, name))
|
||
else:
|
||
self.body.append('%s@footnote{%s}' % (name, uri))
|
||
raise nodes.SkipNode
|
||
|
||
def depart_reference(self, node):
|
||
pass
|
||
|
||
def visit_number_reference(self, node):
|
||
text = nodes.Text(node.get('title', '#'))
|
||
self.visit_Text(text)
|
||
raise nodes.SkipNode
|
||
|
||
def visit_title_reference(self, node):
|
||
text = node.astext()
|
||
self.body.append('@cite{%s}' % self.escape_arg(text))
|
||
raise nodes.SkipNode
|
||
|
||
# -- Blocks
|
||
|
||
def visit_paragraph(self, node):
|
||
self.body.append('\n')
|
||
|
||
def depart_paragraph(self, node):
|
||
self.body.append('\n')
|
||
|
||
def visit_block_quote(self, node):
|
||
self.body.append('\n@quotation\n')
|
||
|
||
def depart_block_quote(self, node):
|
||
self.ensure_eol()
|
||
self.body.append('@end quotation\n')
|
||
|
||
def visit_literal_block(self, node):
|
||
self.body.append('\n@example\n')
|
||
|
||
def depart_literal_block(self, node):
|
||
self.ensure_eol()
|
||
self.body.append('@end example\n')
|
||
|
||
visit_doctest_block = visit_literal_block
|
||
depart_doctest_block = depart_literal_block
|
||
|
||
def visit_line_block(self, node):
|
||
if not isinstance(node.parent, nodes.line_block):
|
||
self.body.append('\n\n')
|
||
self.body.append('@display\n')
|
||
|
||
def depart_line_block(self, node):
|
||
self.body.append('@end display\n')
|
||
if not isinstance(node.parent, nodes.line_block):
|
||
self.body.append('\n\n')
|
||
|
||
def visit_line(self, node):
|
||
self.escape_newlines += 1
|
||
|
||
def depart_line(self, node):
|
||
self.body.append('@w{ }\n')
|
||
self.escape_newlines -= 1
|
||
|
||
# -- Inline
|
||
|
||
def visit_strong(self, node):
|
||
self.body.append('@strong{')
|
||
|
||
def depart_strong(self, node):
|
||
self.body.append('}')
|
||
|
||
def visit_emphasis(self, node):
|
||
self.body.append('@emph{')
|
||
|
||
def depart_emphasis(self, node):
|
||
self.body.append('}')
|
||
|
||
def visit_literal(self, node):
|
||
self.body.append('@code{')
|
||
|
||
def depart_literal(self, node):
|
||
self.body.append('}')
|
||
|
||
def visit_superscript(self, node):
|
||
self.body.append('@w{^')
|
||
|
||
def depart_superscript(self, node):
|
||
self.body.append('}')
|
||
|
||
def visit_subscript(self, node):
|
||
self.body.append('@w{[')
|
||
|
||
def depart_subscript(self, node):
|
||
self.body.append(']}')
|
||
|
||
# -- Footnotes
|
||
|
||
def visit_footnote(self, node):
|
||
raise nodes.SkipNode
|
||
|
||
def visit_collected_footnote(self, node):
|
||
self.in_footnote += 1
|
||
self.body.append('@footnote{')
|
||
|
||
def depart_collected_footnote(self, node):
|
||
self.body.append('}')
|
||
self.in_footnote -= 1
|
||
|
||
def visit_footnote_reference(self, node):
|
||
num = node.astext().strip()
|
||
try:
|
||
footnode, used = self.footnotestack[-1][num]
|
||
except (KeyError, IndexError):
|
||
raise nodes.SkipNode
|
||
# footnotes are repeated for each reference
|
||
footnode.walkabout(self)
|
||
raise nodes.SkipChildren
|
||
|
||
def visit_citation(self, node):
|
||
for id in node.get('ids'):
|
||
self.add_anchor(id, node)
|
||
|
||
def depart_citation(self, node):
|
||
pass
|
||
|
||
def visit_citation_reference(self, node):
|
||
self.body.append('@w{[')
|
||
|
||
def depart_citation_reference(self, node):
|
||
self.body.append(']}')
|
||
|
||
# -- Lists
|
||
|
||
def visit_bullet_list(self, node):
|
||
bullet = node.get('bullet', '*')
|
||
self.body.append('\n\n@itemize %s\n' % bullet)
|
||
|
||
def depart_bullet_list(self, node):
|
||
self.ensure_eol()
|
||
self.body.append('@end itemize\n')
|
||
|
||
def visit_enumerated_list(self, node):
|
||
# doesn't support Roman numerals
|
||
enum = node.get('enumtype', 'arabic')
|
||
starters = {'arabic': '',
|
||
'loweralpha': 'a',
|
||
'upperalpha': 'A'}
|
||
start = node.get('start', starters.get(enum, ''))
|
||
self.body.append('\n\n@enumerate %s\n' % start)
|
||
|
||
def depart_enumerated_list(self, node):
|
||
self.ensure_eol()
|
||
self.body.append('@end enumerate\n')
|
||
|
||
def visit_list_item(self, node):
|
||
self.body.append('\n@item ')
|
||
|
||
def depart_list_item(self, node):
|
||
pass
|
||
|
||
# -- Option List
|
||
|
||
def visit_option_list(self, node):
|
||
self.body.append('\n\n@table @option\n')
|
||
|
||
def depart_option_list(self, node):
|
||
self.ensure_eol()
|
||
self.body.append('@end table\n')
|
||
|
||
def visit_option_list_item(self, node):
|
||
pass
|
||
|
||
def depart_option_list_item(self, node):
|
||
pass
|
||
|
||
def visit_option_group(self, node):
|
||
self.at_item_x = '@item'
|
||
|
||
def depart_option_group(self, node):
|
||
pass
|
||
|
||
def visit_option(self, node):
|
||
self.escape_hyphens += 1
|
||
self.body.append('\n%s ' % self.at_item_x)
|
||
self.at_item_x = '@itemx'
|
||
|
||
def depart_option(self, node):
|
||
self.escape_hyphens -= 1
|
||
|
||
def visit_option_string(self, node):
|
||
pass
|
||
|
||
def depart_option_string(self, node):
|
||
pass
|
||
|
||
def visit_option_argument(self, node):
|
||
self.body.append(node.get('delimiter', ' '))
|
||
|
||
def depart_option_argument(self, node):
|
||
pass
|
||
|
||
def visit_description(self, node):
|
||
self.body.append('\n')
|
||
|
||
def depart_description(self, node):
|
||
pass
|
||
|
||
# -- Definitions
|
||
|
||
def visit_definition_list(self, node):
|
||
self.body.append('\n\n@table @asis\n')
|
||
|
||
def depart_definition_list(self, node):
|
||
self.ensure_eol()
|
||
self.body.append('@end table\n')
|
||
|
||
def visit_definition_list_item(self, node):
|
||
self.at_item_x = '@item'
|
||
|
||
def depart_definition_list_item(self, node):
|
||
pass
|
||
|
||
def visit_term(self, node):
|
||
for id in node.get('ids'):
|
||
self.add_anchor(id, node)
|
||
# anchors and indexes need to go in front
|
||
for n in node[::]:
|
||
if isinstance(n, (addnodes.index, nodes.target)):
|
||
n.walkabout(self)
|
||
node.remove(n)
|
||
self.body.append('\n%s ' % self.at_item_x)
|
||
self.at_item_x = '@itemx'
|
||
|
||
def depart_term(self, node):
|
||
pass
|
||
|
||
def visit_termsep(self, node):
|
||
warnings.warn('sphinx.addnodes.termsep will be removed at Sphinx-1.5',
|
||
DeprecationWarning)
|
||
self.body.append('\n%s ' % self.at_item_x)
|
||
|
||
def depart_termsep(self, node):
|
||
pass
|
||
|
||
def visit_classifier(self, node):
|
||
self.body.append(' : ')
|
||
|
||
def depart_classifier(self, node):
|
||
pass
|
||
|
||
def visit_definition(self, node):
|
||
self.body.append('\n')
|
||
|
||
def depart_definition(self, node):
|
||
pass
|
||
|
||
# -- Tables
|
||
|
||
def visit_table(self, node):
|
||
self.entry_sep = '@item'
|
||
|
||
def depart_table(self, node):
|
||
self.body.append('\n@end multitable\n\n')
|
||
|
||
def visit_tabular_col_spec(self, node):
|
||
pass
|
||
|
||
def depart_tabular_col_spec(self, node):
|
||
pass
|
||
|
||
def visit_colspec(self, node):
|
||
self.colwidths.append(node['colwidth'])
|
||
if len(self.colwidths) != self.n_cols:
|
||
return
|
||
self.body.append('\n\n@multitable ')
|
||
for i, n in enumerate(self.colwidths):
|
||
self.body.append('{%s} ' % ('x' * (n+2)))
|
||
|
||
def depart_colspec(self, node):
|
||
pass
|
||
|
||
def visit_tgroup(self, node):
|
||
self.colwidths = []
|
||
self.n_cols = node['cols']
|
||
|
||
def depart_tgroup(self, node):
|
||
pass
|
||
|
||
def visit_thead(self, node):
|
||
self.entry_sep = '@headitem'
|
||
|
||
def depart_thead(self, node):
|
||
pass
|
||
|
||
def visit_tbody(self, node):
|
||
pass
|
||
|
||
def depart_tbody(self, node):
|
||
pass
|
||
|
||
def visit_row(self, node):
|
||
pass
|
||
|
||
def depart_row(self, node):
|
||
self.entry_sep = '@item'
|
||
|
||
def visit_entry(self, node):
|
||
self.body.append('\n%s\n' % self.entry_sep)
|
||
self.entry_sep = '@tab'
|
||
|
||
def depart_entry(self, node):
|
||
for i in range(node.get('morecols', 0)):
|
||
self.body.append('\n@tab\n')
|
||
|
||
# -- Field Lists
|
||
|
||
def visit_field_list(self, node):
|
||
pass
|
||
|
||
def depart_field_list(self, node):
|
||
pass
|
||
|
||
def visit_field(self, node):
|
||
self.body.append('\n')
|
||
|
||
def depart_field(self, node):
|
||
self.body.append('\n')
|
||
|
||
def visit_field_name(self, node):
|
||
self.ensure_eol()
|
||
self.body.append('@*')
|
||
|
||
def depart_field_name(self, node):
|
||
self.body.append(': ')
|
||
|
||
def visit_field_body(self, node):
|
||
pass
|
||
|
||
def depart_field_body(self, node):
|
||
pass
|
||
|
||
# -- Admonitions
|
||
|
||
def visit_admonition(self, node, name=''):
|
||
if not name:
|
||
name = self.escape(node[0].astext())
|
||
self.body.append(u'\n@cartouche\n@quotation %s ' % name)
|
||
|
||
def depart_admonition(self, node):
|
||
self.ensure_eol()
|
||
self.body.append('@end quotation\n'
|
||
'@end cartouche\n')
|
||
|
||
def _make_visit_admonition(name):
|
||
def visit(self, node):
|
||
self.visit_admonition(node, admonitionlabels[name])
|
||
return visit
|
||
|
||
visit_attention = _make_visit_admonition('attention')
|
||
depart_attention = depart_admonition
|
||
visit_caution = _make_visit_admonition('caution')
|
||
depart_caution = depart_admonition
|
||
visit_danger = _make_visit_admonition('danger')
|
||
depart_danger = depart_admonition
|
||
visit_error = _make_visit_admonition('error')
|
||
depart_error = depart_admonition
|
||
visit_hint = _make_visit_admonition('hint')
|
||
depart_hint = depart_admonition
|
||
visit_important = _make_visit_admonition('important')
|
||
depart_important = depart_admonition
|
||
visit_note = _make_visit_admonition('note')
|
||
depart_note = depart_admonition
|
||
visit_tip = _make_visit_admonition('tip')
|
||
depart_tip = depart_admonition
|
||
visit_warning = _make_visit_admonition('warning')
|
||
depart_warning = depart_admonition
|
||
|
||
# -- Misc
|
||
|
||
def visit_docinfo(self, node):
|
||
raise nodes.SkipNode
|
||
|
||
def visit_generated(self, node):
|
||
raise nodes.SkipNode
|
||
|
||
def visit_header(self, node):
|
||
raise nodes.SkipNode
|
||
|
||
def visit_footer(self, node):
|
||
raise nodes.SkipNode
|
||
|
||
def visit_container(self, node):
|
||
if node.get('literal_block'):
|
||
self.body.append('\n\n@float LiteralBlock\n')
|
||
|
||
def depart_container(self, node):
|
||
if node.get('literal_block'):
|
||
self.body.append('\n@end float\n\n')
|
||
|
||
def visit_decoration(self, node):
|
||
pass
|
||
|
||
def depart_decoration(self, node):
|
||
pass
|
||
|
||
def visit_topic(self, node):
|
||
# ignore TOC's since we have to have a "menu" anyway
|
||
if 'contents' in node.get('classes', []):
|
||
raise nodes.SkipNode
|
||
title = node[0]
|
||
self.visit_rubric(title)
|
||
self.body.append('%s\n' % self.escape(title.astext()))
|
||
|
||
def depart_topic(self, node):
|
||
pass
|
||
|
||
def visit_transition(self, node):
|
||
self.body.append('\n\n%s\n\n' % ('_' * 66))
|
||
|
||
def depart_transition(self, node):
|
||
pass
|
||
|
||
def visit_attribution(self, node):
|
||
self.body.append('\n\n@center --- ')
|
||
|
||
def depart_attribution(self, node):
|
||
self.body.append('\n\n')
|
||
|
||
def visit_raw(self, node):
|
||
format = node.get('format', '').split()
|
||
if 'texinfo' in format or 'texi' in format:
|
||
self.body.append(node.astext())
|
||
raise nodes.SkipNode
|
||
|
||
def visit_figure(self, node):
|
||
self.body.append('\n\n@float Figure\n')
|
||
|
||
def depart_figure(self, node):
|
||
self.body.append('\n@end float\n\n')
|
||
|
||
def visit_caption(self, node):
|
||
if (isinstance(node.parent, nodes.figure) or
|
||
(isinstance(node.parent, nodes.container) and
|
||
node.parent.get('literal_block'))):
|
||
self.body.append('\n@caption{')
|
||
else:
|
||
self.builder.warn('caption not inside a figure.',
|
||
(self.curfilestack[-1], node.line))
|
||
|
||
def depart_caption(self, node):
|
||
if (isinstance(node.parent, nodes.figure) or
|
||
(isinstance(node.parent, nodes.container) and
|
||
node.parent.get('literal_block'))):
|
||
self.body.append('}\n')
|
||
|
||
def visit_image(self, node):
|
||
if node['uri'] in self.builder.images:
|
||
uri = self.builder.images[node['uri']]
|
||
else:
|
||
# missing image!
|
||
if self.ignore_missing_images:
|
||
return
|
||
uri = node['uri']
|
||
if uri.find('://') != -1:
|
||
# ignore remote images
|
||
return
|
||
name, ext = path.splitext(uri)
|
||
attrs = node.attributes
|
||
# width and height ignored in non-tex output
|
||
width = self.tex_image_length(attrs.get('width', ''))
|
||
height = self.tex_image_length(attrs.get('height', ''))
|
||
alt = self.escape_arg(attrs.get('alt', ''))
|
||
self.body.append('\n@image{%s,%s,%s,%s,%s}\n' %
|
||
(name, width, height, alt, ext[1:]))
|
||
|
||
def depart_image(self, node):
|
||
pass
|
||
|
||
def visit_compound(self, node):
|
||
pass
|
||
|
||
def depart_compound(self, node):
|
||
pass
|
||
|
||
def visit_sidebar(self, node):
|
||
self.visit_topic(node)
|
||
|
||
def depart_sidebar(self, node):
|
||
self.depart_topic(node)
|
||
|
||
def visit_label(self, node):
|
||
self.body.append('@w{(')
|
||
|
||
def depart_label(self, node):
|
||
self.body.append(')} ')
|
||
|
||
def visit_legend(self, node):
|
||
pass
|
||
|
||
def depart_legend(self, node):
|
||
pass
|
||
|
||
def visit_substitution_reference(self, node):
|
||
pass
|
||
|
||
def depart_substitution_reference(self, node):
|
||
pass
|
||
|
||
def visit_substitution_definition(self, node):
|
||
raise nodes.SkipNode
|
||
|
||
def visit_system_message(self, node):
|
||
self.body.append('\n@verbatim\n'
|
||
'<SYSTEM MESSAGE: %s>\n'
|
||
'@end verbatim\n' % node.astext())
|
||
raise nodes.SkipNode
|
||
|
||
def visit_comment(self, node):
|
||
self.body.append('\n')
|
||
for line in node.astext().splitlines():
|
||
self.body.append('@c %s\n' % line)
|
||
raise nodes.SkipNode
|
||
|
||
def visit_problematic(self, node):
|
||
self.body.append('>>')
|
||
|
||
def depart_problematic(self, node):
|
||
self.body.append('<<')
|
||
|
||
def unimplemented_visit(self, node):
|
||
self.builder.warn("unimplemented node type: %r" % node,
|
||
(self.curfilestack[-1], node.line))
|
||
|
||
def unknown_visit(self, node):
|
||
self.builder.warn("unknown node type: %r" % node,
|
||
(self.curfilestack[-1], node.line))
|
||
|
||
def unknown_departure(self, node):
|
||
pass
|
||
|
||
# -- Sphinx specific
|
||
|
||
def visit_productionlist(self, node):
|
||
self.visit_literal_block(None)
|
||
names = []
|
||
for production in node:
|
||
names.append(production['tokenname'])
|
||
maxlen = max(len(name) for name in names)
|
||
for production in node:
|
||
if production['tokenname']:
|
||
for id in production.get('ids'):
|
||
self.add_anchor(id, production)
|
||
s = production['tokenname'].ljust(maxlen) + ' ::='
|
||
else:
|
||
s = '%s ' % (' '*maxlen)
|
||
self.body.append(self.escape(s))
|
||
self.body.append(self.escape(production.astext() + '\n'))
|
||
self.depart_literal_block(None)
|
||
raise nodes.SkipNode
|
||
|
||
def visit_production(self, node):
|
||
pass
|
||
|
||
def depart_production(self, node):
|
||
pass
|
||
|
||
def visit_literal_emphasis(self, node):
|
||
self.body.append('@code{')
|
||
|
||
def depart_literal_emphasis(self, node):
|
||
self.body.append('}')
|
||
|
||
def visit_literal_strong(self, node):
|
||
self.body.append('@code{')
|
||
|
||
def depart_literal_strong(self, node):
|
||
self.body.append('}')
|
||
|
||
def visit_index(self, node):
|
||
# terminate the line but don't prevent paragraph breaks
|
||
if isinstance(node.parent, nodes.paragraph):
|
||
self.ensure_eol()
|
||
else:
|
||
self.body.append('\n')
|
||
for entry in node['entries']:
|
||
typ, text, tid, text2, key_ = entry
|
||
text = self.escape_menu(text)
|
||
self.body.append('@geindex %s\n' % text)
|
||
|
||
def visit_versionmodified(self, node):
|
||
self.body.append('\n')
|
||
|
||
def depart_versionmodified(self, node):
|
||
self.body.append('\n')
|
||
|
||
def visit_start_of_file(self, node):
|
||
# add a document target
|
||
self.next_section_ids.add(':doc')
|
||
self.curfilestack.append(node['docname'])
|
||
self.footnotestack.append(self.collect_footnotes(node))
|
||
|
||
def depart_start_of_file(self, node):
|
||
self.curfilestack.pop()
|
||
self.footnotestack.pop()
|
||
|
||
def visit_centered(self, node):
|
||
txt = self.escape_arg(node.astext())
|
||
self.body.append('\n\n@center %s\n\n' % txt)
|
||
raise nodes.SkipNode
|
||
|
||
def visit_seealso(self, node):
|
||
self.body.append(u'\n\n@subsubheading %s\n\n' %
|
||
admonitionlabels['seealso'])
|
||
|
||
def depart_seealso(self, node):
|
||
self.body.append('\n')
|
||
|
||
def visit_meta(self, node):
|
||
raise nodes.SkipNode
|
||
|
||
def visit_glossary(self, node):
|
||
pass
|
||
|
||
def depart_glossary(self, node):
|
||
pass
|
||
|
||
def visit_acks(self, node):
|
||
self.body.append('\n\n')
|
||
self.body.append(', '.join(n.astext()
|
||
for n in node.children[0].children) + '.')
|
||
self.body.append('\n\n')
|
||
raise nodes.SkipNode
|
||
|
||
def visit_highlightlang(self, node):
|
||
pass
|
||
|
||
def depart_highlightlang(self, node):
|
||
pass
|
||
|
||
# -- Desc
|
||
|
||
def visit_desc(self, node):
|
||
self.desc = node
|
||
self.at_deffnx = '@deffn'
|
||
|
||
def depart_desc(self, node):
|
||
self.desc = None
|
||
self.ensure_eol()
|
||
self.body.append('@end deffn\n')
|
||
|
||
def visit_desc_signature(self, node):
|
||
self.escape_hyphens += 1
|
||
objtype = node.parent['objtype']
|
||
if objtype != 'describe':
|
||
for id in node.get('ids'):
|
||
self.add_anchor(id, node)
|
||
# use the full name of the objtype for the category
|
||
try:
|
||
domain = self.builder.env.domains[node.parent['domain']]
|
||
primary = self.builder.config.primary_domain
|
||
name = domain.get_type_name(domain.object_types[objtype],
|
||
primary == domain.name)
|
||
except KeyError:
|
||
name = objtype
|
||
# by convention, the deffn category should be capitalized like a title
|
||
category = self.escape_arg(smart_capwords(name))
|
||
self.body.append('\n%s {%s} ' % (self.at_deffnx, category))
|
||
self.at_deffnx = '@deffnx'
|
||
self.desc_type_name = name
|
||
|
||
def depart_desc_signature(self, node):
|
||
self.body.append("\n")
|
||
self.escape_hyphens -= 1
|
||
self.desc_type_name = None
|
||
|
||
def visit_desc_name(self, node):
|
||
pass
|
||
|
||
def depart_desc_name(self, node):
|
||
pass
|
||
|
||
def visit_desc_addname(self, node):
|
||
pass
|
||
|
||
def depart_desc_addname(self, node):
|
||
pass
|
||
|
||
def visit_desc_type(self, node):
|
||
pass
|
||
|
||
def depart_desc_type(self, node):
|
||
pass
|
||
|
||
def visit_desc_returns(self, node):
|
||
self.body.append(' -> ')
|
||
|
||
def depart_desc_returns(self, node):
|
||
pass
|
||
|
||
def visit_desc_parameterlist(self, node):
|
||
self.body.append(' (')
|
||
self.first_param = 1
|
||
|
||
def depart_desc_parameterlist(self, node):
|
||
self.body.append(')')
|
||
|
||
def visit_desc_parameter(self, node):
|
||
if not self.first_param:
|
||
self.body.append(', ')
|
||
else:
|
||
self.first_param = 0
|
||
text = self.escape(node.astext())
|
||
# replace no-break spaces with normal ones
|
||
text = text.replace(u' ', '@w{ }')
|
||
self.body.append(text)
|
||
raise nodes.SkipNode
|
||
|
||
def visit_desc_optional(self, node):
|
||
self.body.append('[')
|
||
|
||
def depart_desc_optional(self, node):
|
||
self.body.append(']')
|
||
|
||
def visit_desc_annotation(self, node):
|
||
# Try to avoid duplicating info already displayed by the deffn category.
|
||
# e.g.
|
||
# @deffn {Class} Foo
|
||
# -- instead of --
|
||
# @deffn {Class} class Foo
|
||
txt = node.astext().strip()
|
||
if txt == self.desc['desctype'] or \
|
||
txt == self.desc['objtype'] or \
|
||
txt in self.desc_type_name.split():
|
||
raise nodes.SkipNode
|
||
|
||
def depart_desc_annotation(self, node):
|
||
pass
|
||
|
||
def visit_desc_content(self, node):
|
||
pass
|
||
|
||
def depart_desc_content(self, node):
|
||
pass
|
||
|
||
def visit_inline(self, node):
|
||
pass
|
||
|
||
def depart_inline(self, node):
|
||
pass
|
||
|
||
def visit_abbreviation(self, node):
|
||
abbr = node.astext()
|
||
self.body.append('@abbr{')
|
||
if node.hasattr('explanation') and abbr not in self.handled_abbrs:
|
||
self.context.append(',%s}' % self.escape_arg(node['explanation']))
|
||
self.handled_abbrs.add(abbr)
|
||
else:
|
||
self.context.append('}')
|
||
|
||
def depart_abbreviation(self, node):
|
||
self.body.append(self.context.pop())
|
||
|
||
def visit_manpage(self, node):
|
||
return self.visit_literal_emphasis(node)
|
||
|
||
def depart_manpage(self, node):
|
||
return self.depart_literal_emphasis(node)
|
||
|
||
def visit_download_reference(self, node):
|
||
pass
|
||
|
||
def depart_download_reference(self, node):
|
||
pass
|
||
|
||
def visit_hlist(self, node):
|
||
self.visit_bullet_list(node)
|
||
|
||
def depart_hlist(self, node):
|
||
self.depart_bullet_list(node)
|
||
|
||
def visit_hlistcol(self, node):
|
||
pass
|
||
|
||
def depart_hlistcol(self, node):
|
||
pass
|
||
|
||
def visit_pending_xref(self, node):
|
||
pass
|
||
|
||
def depart_pending_xref(self, node):
|
||
pass
|
||
|
||
def visit_math(self, node):
|
||
self.builder.warn('using "math" markup without a Sphinx math extension '
|
||
'active, please use one of the math extensions '
|
||
'described at http://sphinx-doc.org/ext/math.html')
|
||
raise nodes.SkipNode
|
||
|
||
visit_math_block = visit_math
|