From 49a77ad9d5593817556ee69933f5938c861c7025 Mon Sep 17 00:00:00 2001
From: Jeremy Maitin-Shepard ,
and
) if multiple
- IDs are assigned to listings. That is invalid DOM structure.
- (This is a bug of docutils <= 0.12)
-
- This exports hyperlink targets before listings to make valid DOM structure.
- """
- for id in node['ids'][1:]:
- self.body.append('' % id)
- node['ids'].remove(id)
-
# overwritten
def visit_bullet_list(self, node: Element) -> None:
if len(node) == 1 and isinstance(node[0], addnodes.toctree):
# avoid emitting empty
raise nodes.SkipNode
- self.generate_targets_for_listing(node)
super().visit_bullet_list(node)
- # overwritten
- def visit_enumerated_list(self, node: Element) -> None:
- self.generate_targets_for_listing(node)
- super().visit_enumerated_list(node)
-
# overwritten
def visit_definition(self, node: Element) -> None:
# don't insert here.
@@ -559,17 +539,6 @@ def visit_centered(self, node: Element) -> None:
def depart_centered(self, node: Element) -> None:
self.body.append('
tags around paragraph can be omitted.""" - if isinstance(node.parent, addnodes.desc_content): - # Never compact desc_content items. - return False - if isinstance(node.parent, addnodes.versionmodified): - # Never compact versionmodified nodes. - return False - return super().should_be_compact_paragraph(node) - def visit_compact_paragraph(self, node: Element) -> None: pass @@ -691,10 +660,6 @@ def visit_hlistcol(self, node: Element) -> None: def depart_hlistcol(self, node: Element) -> None: self.body.append('') - def visit_option_group(self, node: Element) -> None: - super().visit_option_group(node) - self.context[-2] = self.context[-2].replace(' ', ' ') - # overwritten def visit_Text(self, node: Text) -> None: text = node.astext() @@ -826,11 +791,6 @@ def visit_row(self, node: Element) -> None: self.body.append(self.starttag(node, 'tr', '')) node.column = 0 # type: ignore - def visit_entry(self, node: Element) -> None: - super().visit_entry(node) - if self.body[-1] == ' ': - self.body[-1] = ' ' - def visit_field_list(self, node: Element) -> None: self._fieldlist_row_indices.append(0) return super().visit_field_list(node) @@ -839,20 +799,6 @@ def depart_field_list(self, node: Element) -> None: self._fieldlist_row_indices.pop() return super().depart_field_list(node) - def visit_field(self, node: Element) -> None: - self._fieldlist_row_indices[-1] += 1 - if self._fieldlist_row_indices[-1] % 2 == 0: - node['classes'].append('field-even') - else: - node['classes'].append('field-odd') - self.body.append(self.starttag(node, 'tr', '', CLASS='field')) - - def visit_field_name(self, node: Element) -> None: - context_count = len(self.context) - super().visit_field_name(node) - if context_count != len(self.context): - self.context[-1] = self.context[-1].replace(' ', ' ') - def visit_math(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name visit, _ = self.builder.app.registry.html_inline_math_renderers[name] @@ -892,3 +838,43 @@ def _table_row_index(self): warnings.warn('_table_row_index is deprecated', RemovedInSphinx60Warning, stacklevel=2) return self._table_row_indices[-1] + + +class HTMLTranslator(HTMLTranslatorCommonMixin, SphinxTranslator, BaseTranslator): + """ + Our custom HTML translator. + """ + + # overwritten + def should_be_compact_paragraph(self, node: Node) -> bool: + """Determine if the
tags around paragraph can be omitted."""
+ if isinstance(node.parent, addnodes.desc_content):
+ # Never compact desc_content items.
+ return False
+ if isinstance(node.parent, addnodes.versionmodified):
+ # Never compact versionmodified nodes.
+ return False
+ return super().should_be_compact_paragraph(node)
+
+ def visit_option_group(self, node: Element) -> None:
+ super().visit_option_group(node)
+ self.context[-2] = self.context[-2].replace(' ', ' ')
+
+ def visit_entry(self, node: Element) -> None:
+ super().visit_entry(node)
+ if self.body[-1] == ' ':
+ self.body[-1] = ' '
+
+ def visit_field(self, node: Element) -> None:
+ self._fieldlist_row_indices[-1] += 1
+ if self._fieldlist_row_indices[-1] % 2 == 0:
+ node['classes'].append('field-even')
+ else:
+ node['classes'].append('field-odd')
+ self.body.append(self.starttag(node, 'tr', '', CLASS='field'))
+
+ def visit_field_name(self, node: Element) -> None:
+ context_count = len(self.context)
+ super().visit_field_name(node)
+ if context_count != len(self.context):
+ self.context[-1] = self.context[-1].replace(' ', ' ')
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index b9d0f648c94..b5e3eb80e9c 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -8,27 +8,18 @@
:license: BSD, see LICENSE for details.
"""
-import os
-import posixpath
-import re
-import urllib.parse
-import warnings
-from typing import TYPE_CHECKING, Iterable, Optional, Set, Tuple, cast
+from typing import Set
from docutils import nodes
-from docutils.nodes import Element, Node, Text
+from docutils.nodes import Element
from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator
+import sphinx.writers.html
from sphinx import addnodes
from sphinx.builders import Builder
-from sphinx.deprecation import RemovedInSphinx50Warning, RemovedInSphinx60Warning
-from sphinx.locale import _, __, admonitionlabels
+from sphinx.locale import admonitionlabels
from sphinx.util import logging
from sphinx.util.docutils import SphinxTranslator
-from sphinx.util.images import get_image_size
-
-if TYPE_CHECKING:
- from sphinx.builders.html import StandaloneHTMLBuilder
logger = logging.getLogger(__name__)
@@ -37,97 +28,16 @@
# http://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html
-def multiply_length(length: str, scale: int) -> str:
- """Multiply *length* (width or height) by *scale*."""
- matched = re.match(r'^(\d*\.?\d*)\s*(\S*)$', length)
- if not matched:
- return length
- elif scale == 100:
- return length
- else:
- amount, unit = matched.groups()
- result = float(amount) * scale / 100
- return "%s%s" % (int(result), unit)
-
-
-class HTML5Translator(SphinxTranslator, BaseTranslator):
+class HTML5Translator(sphinx.writers.html.HTMLTranslatorCommonMixin, SphinxTranslator, BaseTranslator):
"""
Our custom HTML translator.
"""
- builder: "StandaloneHTMLBuilder" = None
# Override docutils.writers.html5_polyglot:HTMLTranslator
# otherwise, nodes like ... by `visit_inline`.
supported_inline_tags: Set[str] = set()
- def __init__(self, document: nodes.document, builder: Builder) -> None:
- super().__init__(document, builder)
-
- self.highlighter = self.builder.highlighter
- self.docnames = [self.builder.current_docname] # for singlehtml builder
- self.manpages_url = self.config.manpages_url
- self.protect_literal_text = 0
- self.secnumber_suffix = self.config.html_secnumber_suffix
- self.param_separator = ''
- self.optional_param_level = 0
- self._table_row_indices = [0]
- self._fieldlist_row_indices = [0]
- self.required_params_left = 0
-
- def visit_start_of_file(self, node: Element) -> None:
- # only occurs in the single-file builder
- self.docnames.append(node['docname'])
- self.body.append('' % node['docname'])
-
- def depart_start_of_file(self, node: Element) -> None:
- self.docnames.pop()
-
- #############################################################
- # Domain-specific object descriptions
- #############################################################
-
- # Top-level nodes for descriptions
- ##################################
-
- def visit_desc(self, node: Element) -> None:
- self.body.append(self.starttag(node, 'dl'))
-
- def depart_desc(self, node: Element) -> None:
- self.body.append('\n\n')
-
- def visit_desc_signature(self, node: Element) -> None:
- # the id is set automatically
- self.body.append(self.starttag(node, 'dt'))
- self.protect_literal_text += 1
-
- def depart_desc_signature(self, node: Element) -> None:
- self.protect_literal_text -= 1
- if not node.get('is_multiline'):
- self.add_permalink_ref(node, _('Permalink to this definition'))
- self.body.append('\n')
-
- def visit_desc_signature_line(self, node: Element) -> None:
- pass
-
- def depart_desc_signature_line(self, node: Element) -> None:
- if node.get('add_permalink'):
- # the permalink info is on the parent desc_signature node
- self.add_permalink_ref(node.parent, _('Permalink to this definition'))
- self.body.append('
')
-
- def visit_desc_content(self, node: Element) -> None:
- self.body.append(self.starttag(node, 'dd', ''))
-
- def depart_desc_content(self, node: Element) -> None:
- self.body.append('')
-
- def visit_desc_inline(self, node: Element) -> None:
- self.body.append(self.starttag(node, 'span', ''))
-
- def depart_desc_inline(self, node: Element) -> None:
- self.body.append('')
-
# Nodes for high-level structure in signatures
##############################################
@@ -143,115 +53,6 @@ def visit_desc_addname(self, node: Element) -> None:
def depart_desc_addname(self, node: Element) -> None:
self.body.append('')
- def visit_desc_type(self, node: Element) -> None:
- pass
-
- def depart_desc_type(self, node: Element) -> None:
- pass
-
- def visit_desc_returns(self, node: Element) -> None:
- self.body.append(' ')
- self.body.append('')
-
- def depart_desc_returns(self, node: Element) -> None:
- self.body.append('')
-
- def visit_desc_parameterlist(self, node: Element) -> None:
- self.body.append(' ')
- self.body.append(' (')
- self.first_param = 1
- self.optional_param_level = 0
- # How many required parameters are left.
- self.required_params_left = sum([isinstance(c, addnodes.desc_parameter)
- for c in node.children])
- self.param_separator = node.child_text_separator
-
- def depart_desc_parameterlist(self, node: Element) -> None:
- self.body.append(')')
-
- # If required parameters are still to come, then put the comma after
- # the parameter. Otherwise, put the comma before. This ensures that
- # signatures like the following render correctly (see issue #1001):
- #
- # foo([a, ]b, c[, d])
- #
- def visit_desc_parameter(self, node: Element) -> None:
- if self.first_param:
- self.first_param = 0
- elif not self.required_params_left:
- self.body.append(self.param_separator)
- if self.optional_param_level == 0:
- self.required_params_left -= 1
- if not node.hasattr('noemph'):
- self.body.append('')
-
- def depart_desc_parameter(self, node: Element) -> None:
- if not node.hasattr('noemph'):
- self.body.append('')
- if self.required_params_left:
- self.body.append(self.param_separator)
-
- def visit_desc_optional(self, node: Element) -> None:
- self.optional_param_level += 1
- self.body.append('[')
-
- def depart_desc_optional(self, node: Element) -> None:
- self.optional_param_level -= 1
- self.body.append(']')
-
- def visit_desc_annotation(self, node: Element) -> None:
- self.body.append(self.starttag(node, 'em', '', CLASS='property'))
-
- def depart_desc_annotation(self, node: Element) -> None:
- self.body.append('')
-
- ##############################################
-
- def visit_versionmodified(self, node: Element) -> None:
- self.body.append(self.starttag(node, 'div', CLASS=node['type']))
-
- def depart_versionmodified(self, node: Element) -> None:
- self.body.append('\n')
-
- # overwritten
- def visit_reference(self, node: Element) -> None:
- atts = {'class': 'reference'}
- if node.get('internal') or 'refuri' not in node:
- atts['class'] += ' internal'
- else:
- atts['class'] += ' external'
- if 'refuri' in node:
- atts['href'] = node['refuri'] or '#'
- if self.settings.cloak_email_addresses and atts['href'].startswith('mailto:'):
- atts['href'] = self.cloak_mailto(atts['href'])
- self.in_mailto = True
- else:
- assert 'refid' in node, \
- 'References must have "refuri" or "refid" attribute.'
- atts['href'] = '#' + node['refid']
- if not isinstance(node.parent, nodes.TextElement):
- assert len(node) == 1 and isinstance(node[0], nodes.image)
- atts['class'] += ' image-reference'
- if 'reftitle' in node:
- atts['title'] = node['reftitle']
- if 'target' in node:
- atts['target'] = node['target']
- self.body.append(self.starttag(node, 'a', '', **atts))
-
- if node.get('secnumber'):
- self.body.append(('%s' + self.secnumber_suffix) %
- '.'.join(map(str, node['secnumber'])))
-
- def visit_number_reference(self, node: Element) -> None:
- self.visit_reference(node)
-
- def depart_number_reference(self, node: Element) -> None:
- self.depart_reference(node)
-
- # overwritten -- we don't want source comments to show up in the HTML
- def visit_comment(self, node: Element) -> None: # type: ignore
- raise nodes.SkipNode
-
# overwritten
def visit_admonition(self, node: Element, name: str = '') -> None:
self.body.append(self.starttag(
@@ -259,494 +60,6 @@ def visit_admonition(self, node: Element, name: str = '') -> None:
if name:
node.insert(0, nodes.title(name, admonitionlabels[name]))
- def depart_admonition(self, node: Optional[Element] = None) -> None:
- self.body.append('\n')
-
- def visit_seealso(self, node: Element) -> None:
- self.visit_admonition(node, 'seealso')
-
- def depart_seealso(self, node: Element) -> None:
- self.depart_admonition(node)
-
- def get_secnumber(self, node: Element) -> Tuple[int, ...]:
- if node.get('secnumber'):
- return node['secnumber']
-
- if isinstance(node.parent, nodes.section):
- if self.builder.name == 'singlehtml':
- docname = self.docnames[-1]
- anchorname = "%s/#%s" % (docname, node.parent['ids'][0])
- if anchorname not in self.builder.secnumbers:
- anchorname = "%s/" % docname # try first heading which has no anchor
- else:
- anchorname = '#' + node.parent['ids'][0]
- if anchorname not in self.builder.secnumbers:
- anchorname = '' # try first heading which has no anchor
-
- if self.builder.secnumbers.get(anchorname):
- return self.builder.secnumbers[anchorname]
-
- return None
-
- def add_secnumber(self, node: Element) -> None:
- secnumber = self.get_secnumber(node)
- if secnumber:
- self.body.append('%s' %
- ('.'.join(map(str, secnumber)) + self.secnumber_suffix))
-
- def add_fignumber(self, node: Element) -> None:
- def append_fignumber(figtype: str, figure_id: str) -> None:
- if self.builder.name == 'singlehtml':
- key = "%s/%s" % (self.docnames[-1], figtype)
- else:
- key = figtype
-
- if figure_id in self.builder.fignumbers.get(key, {}):
- self.body.append(' ')
-
- figtype = self.builder.env.domains['std'].get_enumerable_node_type(node)
- if figtype:
- if len(node['ids']) == 0:
- msg = __('Any IDs not assigned for %s node') % node.tagname
- logger.warning(msg, location=node)
- else:
- append_fignumber(figtype, node['ids'][0])
-
- def add_permalink_ref(self, node: Element, title: str) -> None:
- if node['ids'] and self.config.html_permalinks and self.builder.add_permalinks:
- format = '%s'
- self.body.append(format % (node['ids'][0], title,
- self.config.html_permalinks_icon))
-
- # overwritten
- def visit_bullet_list(self, node: Element) -> None:
- if len(node) == 1 and isinstance(node[0], addnodes.toctree):
- # avoid emitting empty