diff options
Diffstat (limited to 'sphinx/writers/latex.py')
-rw-r--r-- | sphinx/writers/latex.py | 312 |
1 files changed, 282 insertions, 30 deletions
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 60483ded5..e084c0b49 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -32,6 +32,11 @@ from sphinx.util.template import LaTeXRenderer from sphinx.util.texescape import tex_escape_map, tex_replace_map from sphinx.util.smartypants import educate_quotes_latex +if False: + # For type annotation + from typing import Any, Callable, Iterator, Pattern, Tuple, Union # NOQA + from sphinx.builder import Builder # NOQA + BEGIN_DOC = r''' \begin{document} @@ -96,7 +101,7 @@ DEFAULT_SETTINGS = { 'tocdepth': '', 'secnumdepth': '', 'pageautorefname': '', -} +} # type: Dict[unicode, unicode] ADDITIONAL_SETTINGS = { 'pdflatex': { @@ -121,7 +126,7 @@ ADDITIONAL_SETTINGS = { 'platex': { 'latex_engine': 'platex', }, -} +} # type: Dict[unicode, Dict[unicode, unicode]] class collected_footnote(nodes.footnote): @@ -141,17 +146,19 @@ class LaTeXWriter(writers.Writer): ('Document class', ['--docclass'], {'default': 'manual'}), ('Author', ['--author'], {'default': ''}), )) - settings_defaults = {} + settings_defaults = {} # type: Dict output = None def __init__(self, builder): + # type: (Builder) -> None writers.Writer.__init__(self) self.builder = builder self.translator_class = ( self.builder.translator_class or LaTeXTranslator) def translate(self): + # type: () -> None transform = ShowUrlsTransform(self.document) transform.apply() visitor = self.translator_class(self.document, self.builder) @@ -163,10 +170,12 @@ class LaTeXWriter(writers.Writer): class ExtBabel(Babel): def __init__(self, language_code): + # type: (unicode) -> None super(ExtBabel, self).__init__(language_code or '') self.language_code = language_code def get_shorthandoff(self): + # type: () -> unicode shortlang = self.language.split('_')[0] if shortlang in ('de', 'ngerman', 'sl', 'slovene', 'pt', 'portuges', 'es', 'spanish', 'nl', 'dutch', 'pl', 'polish', 'it', @@ -177,15 +186,18 @@ class ExtBabel(Babel): return '' def uses_cyrillic(self): + # type: () -> bool shortlang = self.language.split('_')[0] return shortlang in ('bg', 'bulgarian', 'kk', 'kazakh', 'mn', 'mongolian', 'ru', 'russian', 'uk', 'ukrainian') def is_supported_language(self): + # type: () -> bool return bool(super(ExtBabel, self).get_language()) def get_language(self): + # type: () -> unicode language = super(ExtBabel, self).get_language() if not language: return 'english' # fallback to english @@ -197,9 +209,11 @@ class ShowUrlsTransform(object): expanded = False def __init__(self, document): + # type: (nodes.Node) -> None self.document = document def apply(self): + # type: () -> None # replace id_prefix temporarily id_prefix = self.document.settings.id_prefix self.document.settings.id_prefix = 'show_urls' @@ -212,6 +226,7 @@ class ShowUrlsTransform(object): self.document.settings.id_prefix = id_prefix def expand_show_urls(self): + # type: () -> None show_urls = self.document.settings.env.config.latex_show_urls if show_urls is False or show_urls == 'no': return @@ -234,6 +249,7 @@ class ShowUrlsTransform(object): node.parent.insert(index + 1, textnode) def create_footnote(self, uri): + # type: (unicode) -> List[Union[nodes.footnote, nodes.footnote_ref]] label = nodes.label('', '#') para = nodes.paragraph() para.append(nodes.reference('', nodes.Text(uri), refuri=uri, nolinkurl=True)) @@ -250,7 +266,9 @@ class ShowUrlsTransform(object): return [footnote, footnote_ref] def renumber_footnotes(self): + # type: () -> None def is_used_number(number): + # type: (unicode) -> bool for node in self.document.traverse(nodes.footnote): if not node.get('auto') and number in node['names']: return True @@ -258,13 +276,16 @@ class ShowUrlsTransform(object): return False def is_auto_footnote(node): + # type: (nodes.Node) -> bool return isinstance(node, nodes.footnote) and node.get('auto') def footnote_ref_by(node): + # type: (nodes.Node) -> Callable[[nodes.Node], bool] ids = node['ids'] parent = list(traverse_parent(node, (nodes.document, addnodes.start_of_file)))[0] def is_footnote_ref(node): + # type: (nodes.Node) -> bool return (isinstance(node, nodes.footnote_reference) and ids[0] == node['refid'] and parent in list(traverse_parent(node))) @@ -293,23 +314,26 @@ class ShowUrlsTransform(object): class Table(object): def __init__(self): + # type: () -> None self.col = 0 self.colcount = 0 - self.colspec = None + self.colspec = None # type: unicode self.rowcount = 0 self.had_head = False self.has_problematic = False self.has_verbatim = False - self.caption = None + self.caption = None # type: List[unicode] self.longtable = False def escape_abbr(text): + # type: (unicode) -> unicode """Adjust spacing after abbreviations.""" return re.sub('\.(?=\s|$)', '.\\@', text) def rstdim_to_latexdim(width_str): + # type: (unicode) -> unicode """Convert `width_str` with rst length to LaTeX length.""" match = re.match('^(\d*\.?\d*)\s*(\S*)$', width_str) if not match: @@ -336,9 +360,10 @@ class LaTeXTranslator(nodes.NodeVisitor): docclasses = ('howto', 'manual') def __init__(self, document, builder): + # type: (nodes.Node, Builder) -> None nodes.NodeVisitor.__init__(self, document) self.builder = builder - self.body = [] + self.body = [] # type: List[unicode] # flags self.in_title = 0 @@ -355,8 +380,8 @@ class LaTeXTranslator(nodes.NodeVisitor): self.no_contractions = 0 self.compact_list = 0 self.first_param = 0 - self.remember_multirow = {} - self.remember_multirowcol = {} + self.remember_multirow = {} # type: Dict[int, int] + self.remember_multirowcol = {} # type: Dict[int, int] # determine top section level if builder.config.latex_toplevel_sectioning: @@ -438,6 +463,7 @@ class LaTeXTranslator(nodes.NodeVisitor): if getattr(builder, 'usepackages', None): def declare_package(packagename, options=None): + # type:(unicode, unicode) -> unicode if options: return '\\usepackage[%s]{%s}' % (options, packagename) else: @@ -486,54 +512,61 @@ class LaTeXTranslator(nodes.NodeVisitor): self.highlighter = highlighting.PygmentsBridge( 'latex', builder.config.pygments_style, builder.config.trim_doctest_flags) - self.context = [] - self.descstack = [] - self.bibitems = [] - self.table = None - self.next_table_colspec = None + self.context = [] # type: List[Any] + self.descstack = [] # type: List[unicode] + self.bibitems = [] # type: List[List[unicode]] + self.table = None # type: Table + self.next_table_colspec = None # type: unicode # stack of [language, linenothreshold] settings per file # the first item here is the default and must not be changed # the second item is the default for the master file and can be changed # by .. highlight:: directive in the master file self.hlsettingstack = 2 * [[builder.config.highlight_language, sys.maxsize]] - self.bodystack = [] - self.footnotestack = [] + self.bodystack = [] # type: List[List[unicode]] + self.footnotestack = [] # type: List[Dict[unicode, List[Union[collected_footnote, bool]]]] # NOQA self.footnote_restricted = False - self.pending_footnotes = [] - self.curfilestack = [] - self.handled_abbrs = set() - self.next_hyperlink_ids = {} - self.next_section_ids = set() + self.pending_footnotes = [] # type: List[nodes.footnote_reference] + self.curfilestack = [] # type: List[unicode] + self.handled_abbrs = set() # type: Set[unicode] + self.next_hyperlink_ids = {} # type: Dict[unicode, Set[unicode]] + self.next_section_ids = set() # type: Set[unicode] def pushbody(self, newbody): + # type: (List[unicode]) -> None self.bodystack.append(self.body) self.body = newbody def popbody(self): + # type: () -> List[unicode] body = self.body self.body = self.bodystack.pop() return body def push_hyperlink_ids(self, figtype, ids): + # type: (unicode, Set[unicode]) -> None hyperlink_ids = self.next_hyperlink_ids.setdefault(figtype, set()) hyperlink_ids.update(ids) def pop_hyperlink_ids(self, figtype): + # type: (unicode) -> Set[unicode] return self.next_hyperlink_ids.pop(figtype, set()) def check_latex_elements(self): + # type: () -> None for key in self.builder.config.latex_elements: if key not in self.elements: msg = _("Unknown configure key: latex_elements[%r] is ignored.") self.builder.warn(msg % key) def restrict_footnote(self, node): + # type: (nodes.Node) -> None if self.footnote_restricted is False: self.footnote_restricted = node self.pending_footnotes = [] def unrestrict_footnote(self, node): + # type: (nodes.Node) -> None if self.footnote_restricted == node: self.footnote_restricted = False for footnode in self.pending_footnotes: @@ -542,6 +575,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.pending_footnotes = [] def format_docclass(self, docclass): + # type: (unicode) -> unicode """ prepends prefix to sphinx document classes """ if docclass in self.docclasses: @@ -549,6 +583,7 @@ class LaTeXTranslator(nodes.NodeVisitor): return docclass def astext(self): + # type: () -> unicode self.elements.update({ 'body': u''.join(self.body), 'indices': self.generate_indices() @@ -561,26 +596,32 @@ class LaTeXTranslator(nodes.NodeVisitor): return LaTeXRenderer().render(DEFAULT_TEMPLATE, self.elements) def hypertarget(self, id, withdoc=True, anchor=True): + # type: (unicode, bool, bool) -> unicode if withdoc: id = self.curfilestack[-1] + ':' + id return (anchor and '\\phantomsection' or '') + \ '\\label{%s}' % self.idescape(id) def hyperlink(self, id): + # type: (unicode) -> unicode return '{\\hyperref[%s]{' % self.hyperrefescape(id) def hyperpageref(self, id): + # type: (unicode) -> unicode return '\\autopageref*{%s}' % self.idescape(id) def idescape(self, id): + # type: (unicode) -> unicode return text_type(id).translate(tex_replace_map).\ encode('ascii', 'backslashreplace').decode('ascii').\ replace('\\', '_') def hyperrefescape(self, ref): + # type: (unicode) -> unicode return self.idescape(ref).replace('-', '\\string-') def babel_renewcommand(self, command, definition): + # type: (unicode, unicode) -> unicode if self.elements['babel']: prefix = '\\addto\\captions%s{' % self.babel.get_language() suffix = '}' @@ -591,6 +632,7 @@ class LaTeXTranslator(nodes.NodeVisitor): return ('%s\\renewcommand{%s}{%s}%s\n' % (prefix, command, definition, suffix)) def babel_defmacro(self, name, definition): + # type: (unicode, unicode) -> unicode if self.elements['babel']: prefix = '\\addto\\extras%s{' % self.babel.get_language() suffix = '}' @@ -601,7 +643,8 @@ class LaTeXTranslator(nodes.NodeVisitor): return ('%s\\def%s{%s}%s\n' % (prefix, name, definition, suffix)) def generate_numfig_format(self, builder): - ret = [] + # type: (Builder) -> unicode + ret = [] # type: List[unicode] figure = self.builder.config.numfig_format['figure'].split('%s', 1) if len(figure) == 1: ret.append('\\def\\fnum@figure{%s}\n' % @@ -640,7 +683,9 @@ class LaTeXTranslator(nodes.NodeVisitor): return ''.join(ret) def generate_indices(self): + # type: (Builder) -> unicode def generate(content, collapsed): + # type: (List[Tuple[unicode, List[Tuple[unicode, unicode, unicode, unicode, unicode]]]], bool) -> unicode # NOQA ret.append('\\begin{sphinxtheindex}\n') ret.append('\\def\\bigletter#1{{\\Large\\sffamily#1}' '\\nopagebreak\\vspace{1mm}}\n') @@ -685,6 +730,7 @@ class LaTeXTranslator(nodes.NodeVisitor): return ''.join(ret) def visit_document(self, node): + # type: (nodes.Node) -> None self.footnotestack.append(self.collect_footnotes(node)) self.curfilestack.append(node.get('docname', '')) if self.first_document == 1: @@ -701,8 +747,9 @@ class LaTeXTranslator(nodes.NodeVisitor): self.sectionlevel = self.top_sectionlevel - 1 def depart_document(self, node): + # type: (nodes.Node) -> None if self.bibitems: - widest_label = "" + widest_label = "" # type: unicode for bi in self.bibitems: if len(widest_label) < len(bi[0]): widest_label = bi[0] @@ -717,6 +764,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.bibitems = [] def visit_start_of_file(self, node): + # type: (nodes.Node) -> None # collect new footnotes self.footnotestack.append(self.collect_footnotes(node)) # also add a document target @@ -726,7 +774,9 @@ class LaTeXTranslator(nodes.NodeVisitor): self.hlsettingstack.append(self.hlsettingstack[0]) def collect_footnotes(self, node): + # type: (nodes.Node) -> Dict[unicode, List[Union[collected_footnote, bool]]] def footnotes_under(n): + # type: (nodes.Node) -> Iterator[nodes.Node] if isinstance(n, nodes.footnote): yield n else: @@ -735,7 +785,8 @@ class LaTeXTranslator(nodes.NodeVisitor): continue for k in footnotes_under(c): yield k - fnotes = {} + + fnotes = {} # type: Dict[unicode, List[Union[collected_footnote, bool]]] for fn in footnotes_under(node): num = fn.children[0].astext().strip() newnode = collected_footnote(*fn.children, number=num) @@ -743,15 +794,18 @@ class LaTeXTranslator(nodes.NodeVisitor): return fnotes def depart_start_of_file(self, node): + # type: (nodes.Node) -> None self.footnotestack.pop() self.curfilestack.pop() self.hlsettingstack.pop() def visit_highlightlang(self, node): + # type: (nodes.Node) -> None self.hlsettingstack[-1] = [node['lang'], node['linenothreshold']] raise nodes.SkipNode def visit_section(self, node): + # type: (nodes.Node) -> None if not self.this_is_the_title: self.sectionlevel += 1 self.body.append('\n\n') @@ -759,40 +813,50 @@ class LaTeXTranslator(nodes.NodeVisitor): self.next_section_ids.update(node['ids']) def depart_section(self, node): + # type: (nodes.Node) -> None self.sectionlevel = max(self.sectionlevel - 1, self.top_sectionlevel - 1) def visit_problematic(self, node): + # type: (nodes.Node) -> None self.body.append(r'{\color{red}\bfseries{}') def depart_problematic(self, node): + # type: (nodes.Node) -> None self.body.append('}') def visit_topic(self, node): + # type: (nodes.Node) -> None self.in_minipage = 1 self.body.append('\n\\begin{sphinxShadowBox}\n') def depart_topic(self, node): + # type: (nodes.Node) -> None self.in_minipage = 0 self.body.append('\\end{sphinxShadowBox}\n') visit_sidebar = visit_topic depart_sidebar = depart_topic def visit_glossary(self, node): + # type: (nodes.Node) -> None pass def depart_glossary(self, node): + # type: (nodes.Node) -> None pass def visit_productionlist(self, node): + # type: (nodes.Node) -> None self.body.append('\n\n\\begin{productionlist}\n') self.in_production_list = 1 def depart_productionlist(self, node): + # type: (nodes.Node) -> None self.body.append('\\end{productionlist}\n\n') self.in_production_list = 0 def visit_production(self, node): + # type: (nodes.Node) -> None if node['tokenname']: tn = node['tokenname'] self.body.append(self.hypertarget('grammar-token-' + tn)) @@ -801,15 +865,19 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\productioncont{') def depart_production(self, node): + # type: (nodes.Node) -> None self.body.append('}\n') def visit_transition(self, node): + # type: (nodes.Node) -> None self.body.append(self.elements['transition']) def depart_transition(self, node): + # type: (nodes.Node) -> None pass def visit_title(self, node): + # type: (nodes.Node) -> None parent = node.parent if isinstance(parent, addnodes.seealso): # the environment already handles this @@ -866,6 +934,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.in_title = 1 def depart_title(self, node): + # type: (nodes.Node) -> None self.in_title = 0 if isinstance(node.parent, nodes.table): self.table.caption = self.popbody() @@ -874,6 +943,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.unrestrict_footnote(node) def visit_subtitle(self, node): + # type: (nodes.Node) -> None if isinstance(node.parent, nodes.sidebar): self.body.append('\\sphinxstylesidebarsubtitle{') self.context.append('}\n') @@ -881,17 +951,21 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('') def depart_subtitle(self, node): + # type: (nodes.Node) -> None self.body.append(self.context.pop()) def visit_desc(self, node): + # type: (nodes.Node) -> None self.body.append('\n\n\\begin{fulllineitems}\n') if self.table: self.table.has_problematic = True def depart_desc(self, node): + # type: (nodes.Node) -> None self.body.append('\n\\end{fulllineitems}\n\n') def _visit_signature_line(self, node): + # type: (nodes.Node) -> None for child in node: if isinstance(child, addnodes.desc_parameterlist): self.body.append(r'\pysiglinewithargsret{') @@ -900,9 +974,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(r'\pysigline{') def _depart_signature_line(self, node): + # type: (nodes.Node) -> None self.body.append('}') def visit_desc_signature(self, node): + # type: (nodes.Node) -> None if node.parent['objtype'] != 'describe' and node['ids']: hyper = self.hypertarget(node['ids'][0]) else: @@ -912,55 +988,69 @@ class LaTeXTranslator(nodes.NodeVisitor): self._visit_signature_line(node) def depart_desc_signature(self, node): + # type: (nodes.Node) -> None if not node.get('is_multiline'): self._depart_signature_line(node) def visit_desc_signature_line(self, node): + # type: (nodes.Node) -> None self._visit_signature_line(node) def depart_desc_signature_line(self, node): + # type: (nodes.Node) -> None self._depart_signature_line(node) def visit_desc_addname(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxcode{') self.literal_whitespace += 1 def depart_desc_addname(self, node): + # type: (nodes.Node) -> None self.body.append('}') self.literal_whitespace -= 1 def visit_desc_type(self, node): + # type: (nodes.Node) -> None pass def depart_desc_type(self, node): + # type: (nodes.Node) -> None pass def visit_desc_returns(self, node): + # type: (nodes.Node) -> None self.body.append(r'{ $\rightarrow$ ') def depart_desc_returns(self, node): + # type: (nodes.Node) -> None self.body.append(r'}') def visit_desc_name(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxbfcode{') self.no_contractions += 1 self.literal_whitespace += 1 def depart_desc_name(self, node): + # type: (nodes.Node) -> None self.body.append('}') self.literal_whitespace -= 1 self.no_contractions -= 1 def visit_desc_parameterlist(self, node): + # type: (nodes.Node) -> None # close name, open parameterlist self.body.append('}{') self.first_param = 1 def depart_desc_parameterlist(self, node): + # type: (nodes.Node) -> None # close parameterlist, open return annotation self.body.append('}{') def visit_desc_parameter(self, node): + # type: (nodes.Node) -> None if not self.first_param: self.body.append(', ') else: @@ -969,36 +1059,46 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(r'\emph{') def depart_desc_parameter(self, node): + # type: (nodes.Node) -> None if not node.hasattr('noemph'): self.body.append('}') def visit_desc_optional(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxoptional{') def depart_desc_optional(self, node): + # type: (nodes.Node) -> None self.body.append('}') def visit_desc_annotation(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxstrong{') def depart_desc_annotation(self, node): + # type: (nodes.Node) -> None self.body.append('}') def visit_desc_content(self, node): + # type: (nodes.Node) -> None if node.children and not isinstance(node.children[0], nodes.paragraph): # avoid empty desc environment which causes a formatting bug self.body.append('~') def depart_desc_content(self, node): + # type: (nodes.Node) -> None pass def visit_seealso(self, node): + # type: (nodes.Node) -> None self.body.append(u'\n\n\\sphinxstrong{%s:}\n\n' % admonitionlabels['seealso']) def depart_seealso(self, node): + # type: (nodes.Node) -> None self.body.append("\n\n") def visit_rubric(self, node): + # type: (nodes.Node) -> None if len(node.children) == 1 and node.children[0].astext() in \ ('Footnotes', _('Footnotes')): raise nodes.SkipNode @@ -1007,13 +1107,16 @@ class LaTeXTranslator(nodes.NodeVisitor): self.in_title = 1 def depart_rubric(self, node): + # type: (nodes.Node) -> None self.in_title = 0 self.body.append(self.context.pop()) def visit_footnote(self, node): + # type: (nodes.Node) -> None raise nodes.SkipNode def visit_collected_footnote(self, node): + # type: (nodes.Node) -> None self.in_footnote += 1 if 'footnotetext' in node: self.body.append('%%\n\\begin{footnotetext}[%s]' @@ -1023,6 +1126,7 @@ class LaTeXTranslator(nodes.NodeVisitor): '\\sphinxAtStartFootnote\n' % node['number']) def depart_collected_footnote(self, node): + # type: (nodes.Node) -> None if 'footnotetext' in node: self.body.append('%\n\\end{footnotetext}') else: @@ -1030,6 +1134,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.in_footnote -= 1 def visit_label(self, node): + # type: (nodes.Node) -> None if isinstance(node.parent, nodes.citation): self.bibitems[-1][0] = node.astext() self.bibitems[-1][2] = self.curfilestack[-1] @@ -1037,23 +1142,26 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_tabular_col_spec(self, node): + # type: (nodes.Node) -> None self.next_table_colspec = node['spec'] raise nodes.SkipNode def visit_table(self, node): + # type: (nodes.Node) -> None if self.table: raise UnsupportedError( '%s:%s: nested tables are not yet implemented.' % (self.curfilestack[-1], node.line or '')) self.table = Table() self.table.longtable = 'longtable' in node['classes'] - self.tablebody = [] - self.tableheaders = [] + self.tablebody = [] # type: List[unicode] + self.tableheaders = [] # type: List[unicode] # Redirect body output until table is finished. self.pushbody(self.tablebody) self.restrict_footnote(node) def depart_table(self, node): + # type: (nodes.Node) -> None if self.table.rowcount > 30: self.table.longtable = True self.popbody() @@ -1130,18 +1238,23 @@ class LaTeXTranslator(nodes.NodeVisitor): self.tablebody = None def visit_colspec(self, node): + # type: (nodes.Node) -> None self.table.colcount += 1 def depart_colspec(self, node): + # type: (nodes.Node) -> None pass def visit_tgroup(self, node): + # type: (nodes.Node) -> None pass def depart_tgroup(self, node): + # type: (nodes.Node) -> None pass def visit_thead(self, node): + # type: (nodes.Node) -> None self.table.had_head = True if self.next_table_colspec: self.table.colspec = '{%s}\n' % self.next_table_colspec @@ -1150,24 +1263,29 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body = self.tableheaders def depart_thead(self, node): + # type: (nodes.Node) -> None pass def visit_tbody(self, node): + # type: (nodes.Node) -> None if not self.table.had_head: self.visit_thead(node) self.body = self.tablebody def depart_tbody(self, node): + # type: (nodes.Node) -> None self.remember_multirow = {} self.remember_multirowcol = {} def visit_row(self, node): + # type: (nodes.Node) -> None self.table.col = 0 for key, value in self.remember_multirow.items(): if not value and key in self.remember_multirowcol: del self.remember_multirowcol[key] def depart_row(self, node): + # type: (nodes.Node) -> None self.body.append('\\\\\n') if any(self.remember_multirow.values()): linestart = 1 @@ -1188,6 +1306,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.rowcount += 1 def visit_entry(self, node): + # type: (nodes.Node) -> None if self.table.col == 0: while self.remember_multirow.get(self.table.col + 1, 0): self.table.col += 1 @@ -1249,6 +1368,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append(context) def depart_entry(self, node): + # type: (nodes.Node) -> None if self.in_merged_cell: self.in_merged_cell = 0 self.literal_whitespace -= 1 @@ -1262,6 +1382,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(self.context.pop()) # header def visit_acks(self, node): + # type: (nodes.Node) -> None # this is a list in the source, but should be rendered as a # comma-separated list here self.body.append('\n\n') @@ -1271,16 +1392,19 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_bullet_list(self, node): + # type: (nodes.Node) -> None if not self.compact_list: self.body.append('\\begin{itemize}\n') if self.table: self.table.has_problematic = True def depart_bullet_list(self, node): + # type: (nodes.Node) -> None if not self.compact_list: self.body.append('\\end{itemize}\n') def visit_enumerated_list(self, node): + # type: (nodes.Node) -> None self.body.append('\\begin{enumerate}\n') if 'start' in node: self.body.append('\\setcounter{enumi}{%d}\n' % (node['start'] - 1)) @@ -1288,33 +1412,41 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.has_problematic = True def depart_enumerated_list(self, node): + # type: (nodes.Node) -> None self.body.append('\\end{enumerate}\n') def visit_list_item(self, node): + # type: (nodes.Node) -> None # Append "{}" in case the next character is "[", which would break # LaTeX's list environment (no numbering and the "[" is not printed). self.body.append(r'\item {} ') def depart_list_item(self, node): + # type: (nodes.Node) -> None self.body.append('\n') def visit_definition_list(self, node): + # type: (nodes.Node) -> None self.body.append('\\begin{description}\n') if self.table: self.table.has_problematic = True def depart_definition_list(self, node): + # type: (nodes.Node) -> None self.body.append('\\end{description}\n') def visit_definition_list_item(self, node): + # type: (nodes.Node) -> None pass def depart_definition_list_item(self, node): + # type: (nodes.Node) -> None pass def visit_term(self, node): + # type: (nodes.Node) -> None self.in_term += 1 - ctx = '}] \\leavevmode' + ctx = '}] \\leavevmode' # type: unicode if node.get('ids'): ctx += self.hypertarget(node['ids'][0]) self.body.append('\\item[{') @@ -1322,40 +1454,50 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append(ctx) def depart_term(self, node): + # type: (nodes.Node) -> None self.body.append(self.context.pop()) self.unrestrict_footnote(node) self.in_term -= 1 def visit_termsep(self, node): + # type: (nodes.Node) -> None warnings.warn('sphinx.addnodes.termsep will be removed at Sphinx-1.5', DeprecationWarning) self.body.append(', ') raise nodes.SkipNode def visit_classifier(self, node): + # type: (nodes.Node) -> None self.body.append('{[}') def depart_classifier(self, node): + # type: (nodes.Node) -> None self.body.append('{]}') def visit_definition(self, node): + # type: (nodes.Node) -> None pass def depart_definition(self, node): + # type: (nodes.Node) -> None self.body.append('\n') def visit_field_list(self, node): + # type: (nodes.Node) -> None self.body.append('\\begin{quote}\\begin{description}\n') if self.table: self.table.has_problematic = True def depart_field_list(self, node): + # type: (nodes.Node) -> None self.body.append('\\end{description}\\end{quote}\n') def visit_field(self, node): + # type: (nodes.Node) -> None pass def depart_field(self, node): + # type: (nodes.Node) -> None pass visit_field_name = visit_term @@ -1365,6 +1507,7 @@ class LaTeXTranslator(nodes.NodeVisitor): depart_field_body = depart_definition def visit_paragraph(self, node): + # type: (nodes.Node) -> None index = node.parent.index(node) if (index > 0 and isinstance(node.parent, nodes.compound) and not isinstance(node.parent[index - 1], nodes.paragraph) and @@ -1378,17 +1521,21 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\n') def depart_paragraph(self, node): + # type: (nodes.Node) -> None self.body.append('\n') def visit_centered(self, node): + # type: (nodes.Node) -> None self.body.append('\n\\begin{center}') if self.table: self.table.has_problematic = True def depart_centered(self, node): + # type: (nodes.Node) -> None self.body.append('\n\\end{center}') def visit_hlist(self, node): + # type: (nodes.Node) -> None # for now, we don't support a more compact list format # don't add individual itemize environments, but one for all columns self.compact_list += 1 @@ -1398,26 +1545,32 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.has_problematic = True def depart_hlist(self, node): + # type: (nodes.Node) -> None self.compact_list -= 1 self.body.append('\\end{itemize}\n') def visit_hlistcol(self, node): + # type: (nodes.Node) -> None pass def depart_hlistcol(self, node): + # type: (nodes.Node) -> None pass def latex_image_length(self, width_str): + # type: (nodes.Node) -> unicode try: return rstdim_to_latexdim(width_str) except ValueError: self.builder.warn('dimension unit %s is invalid. Ignored.' % width_str) def is_inline(self, node): + # type: (nodes.Node) -> bool """Check whether a node represents an inline element.""" return isinstance(node.parent, nodes.TextElement) def visit_image(self, node): + # type: (nodes.Node) -> None attrs = node.attributes pre = [] # in reverse order post = [] @@ -1490,10 +1643,12 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.extend(post) def depart_image(self, node): + # type: (nodes.Node) -> None pass def visit_figure(self, node): - ids = '' + # type: (nodes.Node) -> None + ids = '' # type: unicode for id in self.pop_hyperlink_ids('figure'): ids += self.hypertarget(id, anchor=False) if node['ids']: @@ -1549,10 +1704,12 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append(ids + align_end + '\\end{figure}\n') def depart_figure(self, node): + # type: (nodes.Node) -> None self.body.append(self.context.pop()) self.unrestrict_footnote(node) def visit_caption(self, node): + # type: (nodes.Node) -> None self.in_caption += 1 self.restrict_footnote(node) if self.in_container_literal_block: @@ -1565,29 +1722,36 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\caption{') def depart_caption(self, node): + # type: (nodes.Node) -> None self.body.append('}') self.in_caption -= 1 self.unrestrict_footnote(node) def visit_legend(self, node): + # type: (nodes.Node) -> None self.body.append('{\\small ') def depart_legend(self, node): + # type: (nodes.Node) -> None self.body.append('}') def visit_admonition(self, node): + # type: (nodes.Node) -> None self.body.append('\n\\begin{sphinxadmonition}{note}') def depart_admonition(self, node): + # type: (nodes.Node) -> None self.body.append('\\end{sphinxadmonition}\n') def _make_visit_admonition(name): def visit_admonition(self, node): + # type: (nodes.Node) -> None self.body.append(u'\n\\begin{sphinxadmonition}{%s}{%s:}' % (name, admonitionlabels[name])) return visit_admonition def _depart_named_admonition(self, node): + # type: (nodes.Node) -> None self.body.append('\\end{sphinxadmonition}\n') visit_attention = _make_visit_admonition('attention') @@ -1610,13 +1774,17 @@ class LaTeXTranslator(nodes.NodeVisitor): depart_warning = _depart_named_admonition def visit_versionmodified(self, node): + # type: (nodes.Node) -> None pass def depart_versionmodified(self, node): + # type: (nodes.Node) -> None pass def visit_target(self, node): + # type: (nodes.Node) -> None def add_target(id): + # type: (unicode) -> None # indexing uses standard LaTeX index markup, so the targets # will be generated differently if id.startswith('index-'): @@ -1664,16 +1832,20 @@ class LaTeXTranslator(nodes.NodeVisitor): add_target(id) def depart_target(self, node): + # type: (nodes.Node) -> None pass def visit_attribution(self, node): + # type: (nodes.Node) -> None self.body.append('\n\\begin{flushright}\n') self.body.append('---') def depart_attribution(self, node): + # type: (nodes.Node) -> None self.body.append('\n\\end{flushright}\n') def visit_index(self, node, scre=re.compile(r';\s*')): + # type: (nodes.Node, Pattern) -> None if not node.get('inline', True): self.body.append('\n') entries = node['entries'] @@ -1710,11 +1882,13 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_raw(self, node): + # type: (nodes.Node) -> None if 'latex' in node.get('format', '').split(): self.body.append(node.astext()) raise nodes.SkipNode def visit_reference(self, node): + # type: (nodes.Node) -> None if not self.in_title: for id in node.get('ids'): anchor = not self.in_caption @@ -1773,9 +1947,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('') def depart_reference(self, node): + # type: (nodes.Node) -> None self.body.append(self.context.pop()) def visit_number_reference(self, node): + # type: (nodes.Node) -> None if node.get('refid'): id = self.curfilestack[-1] + ':' + node['refid'] else: @@ -1797,46 +1973,59 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_download_reference(self, node): + # type: (nodes.Node) -> None pass def depart_download_reference(self, node): + # type: (nodes.Node) -> None pass def visit_pending_xref(self, node): + # type: (nodes.Node) -> None pass def depart_pending_xref(self, node): + # type: (nodes.Node) -> None pass def visit_emphasis(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxstyleemphasis{') def depart_emphasis(self, node): + # type: (nodes.Node) -> None self.body.append('}') def visit_literal_emphasis(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxstyleliteralemphasis{') self.no_contractions += 1 def depart_literal_emphasis(self, node): + # type: (nodes.Node) -> None self.body.append('}') self.no_contractions -= 1 def visit_strong(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxstylestrong{') def depart_strong(self, node): + # type: (nodes.Node) -> None self.body.append('}') def visit_literal_strong(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxstyleliteralstrong{') self.no_contractions += 1 def depart_literal_strong(self, node): + # type: (nodes.Node) -> None self.body.append('}') self.no_contractions -= 1 def visit_abbreviation(self, node): + # type: (nodes.Node) -> None abbr = node.astext() self.body.append(r'\sphinxstyleabbreviation{') # spell out the explanation once @@ -1847,39 +2036,48 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('}') def depart_abbreviation(self, node): + # type: (nodes.Node) -> None self.body.append(self.context.pop()) def visit_manpage(self, node): + # type: (nodes.Node) -> Any return self.visit_literal_emphasis(node) def depart_manpage(self, node): + # type: (nodes.Node) -> Any return self.depart_literal_emphasis(node) def visit_title_reference(self, node): + # type: (nodes.Node) -> None self.body.append(r'\sphinxtitleref{') def depart_title_reference(self, node): + # type: (nodes.Node) -> None self.body.append('}') def visit_citation(self, node): + # type: (nodes.Node) -> None # TODO maybe use cite bibitems # bibitem: [citelabel, citetext, docname, citeid] self.bibitems.append(['', '', '', '']) self.context.append(len(self.body)) def depart_citation(self, node): + # type: (nodes.Node) -> None size = self.context.pop() text = ''.join(self.body[size:]) del self.body[size:] self.bibitems[-1][1] = text def visit_citation_reference(self, node): + # type: (nodes.Node) -> None # This is currently never encountered, since citation_reference nodes # are already replaced by pending_xref nodes in the environment. self.body.append('\\cite{%s}' % self.idescape(node.astext())) raise nodes.SkipNode def visit_literal(self, node): + # type: (nodes.Node) -> None self.no_contractions += 1 if self.in_title: self.body.append(r'\sphinxstyleliteralintitle{') @@ -1887,10 +2085,12 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(r'\sphinxcode{') def depart_literal(self, node): + # type: (nodes.Node) -> None self.no_contractions -= 1 self.body.append('}') def visit_footnote_reference(self, node): + # type: (nodes.Node) -> None num = node.astext().strip() try: footnode, used = self.footnotestack[-1][num] @@ -1906,18 +2106,20 @@ class LaTeXTranslator(nodes.NodeVisitor): self.pending_footnotes.append(footnode) else: self.footnotestack[-1][num][1] = True - footnode.walkabout(self) + footnode.walkabout(self) # type: ignore raise nodes.SkipChildren def depart_footnote_reference(self, node): + # type: (nodes.Node) -> None pass def visit_literal_block(self, node): + # type: (nodes.Node) -> None if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight self.body.append('\\begin{alltt}\n') else: - ids = '' + ids = '' # type: unicode for id in self.pop_hyperlink_ids('code-block'): ids += self.hypertarget(id, anchor=False) if node['ids']: @@ -1943,6 +2145,7 @@ class LaTeXTranslator(nodes.NodeVisitor): opts = {} def warner(msg): + # type: (unicode) -> None self.builder.warn(msg, (self.curfilestack[-1], node.line)) hlcode = self.highlighter.highlight_block(code, lang, opts=opts, warn=warner, linenos=linenos, @@ -1974,17 +2177,21 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def depart_literal_block(self, node): + # type: (nodes.Node) -> None self.body.append('\n\\end{alltt}\n') visit_doctest_block = visit_literal_block depart_doctest_block = depart_literal_block def visit_line(self, node): + # type: (nodes.Node) -> None self.body.append('\item[] ') def depart_line(self, node): + # type: (nodes.Node) -> None self.body.append('\n') def visit_line_block(self, node): + # type: (nodes.Node) -> None if isinstance(node.parent, nodes.line_block): self.body.append('\\item[]\n' '\\begin{DUlineblock}{\\DUlineblockindent}\n') @@ -1994,9 +2201,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.has_problematic = True def depart_line_block(self, node): + # type: (nodes.Node) -> None self.body.append('\\end{DUlineblock}\n') def visit_block_quote(self, node): + # type: (nodes.Node) -> None # If the block quote contains a single object and that object # is a list, then generate a list not a block quote. # This lets us indent lists. @@ -2012,6 +2221,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.has_problematic = True def depart_block_quote(self, node): + # type: (nodes.Node) -> None done = 0 if len(node.children) == 1: child = node.children[0] @@ -2024,45 +2234,56 @@ class LaTeXTranslator(nodes.NodeVisitor): # option node handling copied from docutils' latex writer def visit_option(self, node): + # type: (nodes.Node) -> None if self.context[-1]: # this is not the first option self.body.append(', ') def depart_option(self, node): + # type: (nodes.Node) -> None # flag that the first option is done. self.context[-1] += 1 def visit_option_argument(self, node): + # type: (nodes.Node) -> None """The delimiter betweeen an option and its argument.""" self.body.append(node.get('delimiter', ' ')) def depart_option_argument(self, node): + # type: (nodes.Node) -> None pass def visit_option_group(self, node): + # type: (nodes.Node) -> None self.body.append('\\item [') # flag for first option self.context.append(0) def depart_option_group(self, node): + # type: (nodes.Node) -> None self.context.pop() # the flag self.body.append('] ') def visit_option_list(self, node): + # type: (nodes.Node) -> None self.body.append('\\begin{optionlist}{3cm}\n') if self.table: self.table.has_problematic = True def depart_option_list(self, node): + # type: (nodes.Node) -> None self.body.append('\\end{optionlist}\n') def visit_option_list_item(self, node): + # type: (nodes.Node) -> None pass def depart_option_list_item(self, node): + # type: (nodes.Node) -> None pass def visit_option_string(self, node): + # type: (nodes.Node) -> None ostring = node.astext() self.no_contractions += 1 self.body.append(self.encode(ostring)) @@ -2070,30 +2291,39 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_description(self, node): + # type: (nodes.Node) -> None self.body.append(' ') def depart_description(self, node): + # type: (nodes.Node) -> None pass def visit_superscript(self, node): + # type: (nodes.Node) -> None self.body.append('$^{\\text{') def depart_superscript(self, node): + # type: (nodes.Node) -> None self.body.append('}}$') def visit_subscript(self, node): + # type: (nodes.Node) -> None self.body.append('$_{\\text{') def depart_subscript(self, node): + # type: (nodes.Node) -> None self.body.append('}}$') def visit_substitution_definition(self, node): + # type: (nodes.Node) -> None raise nodes.SkipNode def visit_substitution_reference(self, node): + # type: (nodes.Node) -> None raise nodes.SkipNode def visit_inline(self, node): + # type: (nodes.Node) -> None classes = node.get('classes', []) if classes in [['menuselection'], ['guilabel']]: self.body.append(r'\sphinxmenuselection{') @@ -2108,24 +2338,30 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('') def depart_inline(self, node): + # type: (nodes.Node) -> None self.body.append(self.context.pop()) def visit_generated(self, node): + # type: (nodes.Node) -> None pass def depart_generated(self, node): + # type: (nodes.Node) -> None pass def visit_compound(self, node): + # type: (nodes.Node) -> None pass def depart_compound(self, node): + # type: (nodes.Node) -> None pass def visit_container(self, node): + # type: (nodes.Node) -> None if node.get('literal_block'): self.in_container_literal_block += 1 - ids = '' + ids = '' # type: unicode for id in self.pop_hyperlink_ids('code-block'): ids += self.hypertarget(id, anchor=False) if node['ids']: @@ -2136,31 +2372,38 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + ids + '}\n') def depart_container(self, node): + # type: (nodes.Node) -> None if node.get('literal_block'): self.in_container_literal_block -= 1 self.body.append('\\let\\sphinxVerbatimTitle\\empty\n') self.body.append('\\let\\sphinxLiteralBlockLabel\\empty\n') def visit_decoration(self, node): + # type: (nodes.Node) -> None pass def depart_decoration(self, node): + # type: (nodes.Node) -> None pass # docutils-generated elements that we don't support def visit_header(self, node): + # type: (nodes.Node) -> None raise nodes.SkipNode def visit_footer(self, node): + # type: (nodes.Node) -> None raise nodes.SkipNode def visit_docinfo(self, node): + # type: (nodes.Node) -> None raise nodes.SkipNode # text handling def encode(self, text): + # type: (unicode) -> unicode text = text_type(text).translate(tex_escape_map) if self.literal_whitespace: # Insert a blank before the newline, to avoid @@ -2172,32 +2415,40 @@ class LaTeXTranslator(nodes.NodeVisitor): return text def encode_uri(self, text): + # type: (unicode) -> unicode # in \href, the tilde is allowed and must be represented literally return self.encode(text).replace('\\textasciitilde{}', '~') def visit_Text(self, node): + # type: (nodes.Node) -> None text = self.encode(node.astext()) if not self.no_contractions: text = educate_quotes_latex(text) self.body.append(text) def depart_Text(self, node): + # type: (nodes.Node) -> None pass def visit_comment(self, node): + # type: (nodes.Node) -> None raise nodes.SkipNode def visit_meta(self, node): + # type: (nodes.Node) -> None # only valid for HTML raise nodes.SkipNode def visit_system_message(self, node): + # type: (nodes.Node) -> None pass def depart_system_message(self, node): + # type: (nodes.Node) -> None self.body.append('\n') def visit_math(self, node): + # type: (nodes.Node) -> None 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', @@ -2207,4 +2458,5 @@ class LaTeXTranslator(nodes.NodeVisitor): visit_math_block = visit_math def unknown_visit(self, node): + # type: (nodes.Node) -> None raise NotImplementedError('Unknown node: ' + node.__class__.__name__) |