def visit_rubric(self, node: Element) -> None: self.ensure_eol() if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')): self.body.append('.SH ' + self.deunicode(node.astext()).upper() + '\n') raise nodes.SkipNode else: self.body.append('.sp\n')
def apply_source_workaround(node: Element) -> None: # workaround: nodes.term have wrong rawsource if classifier is specified. # The behavior of docutils-0.11, 0.12 is: # * when ``term text : classifier1 : classifier2`` is specified, # * rawsource of term node will have: ``term text : classifier1 : classifier2`` # * rawsource of classifier node will be None if isinstance(node, nodes.classifier) and not node.rawsource: logger.debug('[i18n] PATCH: %r to have source, line and rawsource: %s', get_full_module_name(node), repr_domxml(node)) definition_list_item = node.parent node.source = definition_list_item.source node.line = definition_list_item.line - 1 node.rawsource = node.astext() # set 'classifier1' (or 'classifier2') elif isinstance(node, nodes.classifier) and not node.source: # docutils-0.15 fills in rawsource attribute, but not in source. node.source = node.parent.source if isinstance(node, nodes.image) and node.source is None: logger.debug('[i18n] PATCH: %r to have source, line: %s', get_full_module_name(node), repr_domxml(node)) node.source, node.line = node.parent.source, node.parent.line if isinstance(node, nodes.title) and node.source is None: logger.debug('[i18n] PATCH: %r to have source: %s', get_full_module_name(node), repr_domxml(node)) node.source, node.line = node.parent.source, node.parent.line if isinstance(node, nodes.term): logger.debug('[i18n] PATCH: %r to have rawsource: %s', get_full_module_name(node), repr_domxml(node)) # strip classifier from rawsource of term for classifier in reversed(list(node.parent.findall(nodes.classifier))): node.rawsource = re.sub(r'\s*:\s*%s' % re.escape(classifier.astext()), '', node.rawsource) if isinstance(node, nodes.topic) and node.source is None: # docutils-0.18 does not fill the source attribute of topic logger.debug('[i18n] PATCH: %r to have source, line: %s', get_full_module_name(node), repr_domxml(node)) node.source, node.line = node.parent.source, node.parent.line # workaround: literal_block under bullet list (#4913) if isinstance(node, nodes.literal_block) and node.source is None: node.source = get_node_source(node) # workaround: recommonmark-0.2.0 doesn't set rawsource attribute if not node.rawsource: node.rawsource = node.astext() if node.source and node.rawsource: return # workaround: some docutils nodes doesn't have source, line. if (isinstance(node, ( nodes.rubric, # #1305 rubric directive nodes.line, # #1477 line node nodes.image, # #3093 image directive in substitution nodes.field_name, # #3335 field list syntax ))): logger.debug('[i18n] PATCH: %r to have source and line: %s', get_full_module_name(node), repr_domxml(node)) node.source = get_node_source(node) or '' node.line = 0 # need fix docutils to get `node.line` return
def error_collector(data): # Mutate the data since it was just generated data.type = data['type'] data.level = data['level'] data.message = Element.astext(data.children[0]) data.full_message = Element.astext(data) # Save the error errors.append(data)
def error_collector(data): # Mutate the data since it was just generated # DEV: We will generate negative line numbers for RST prolog errors data.line = data.get('line') if isinstance(data.line, int): data.line -= rst_prolog_line_offset data.source = data['source'] data.level = data['level'] data.type = data['type'] data.message = Element.astext(data.children[0]) data.full_message = Element.astext(data) # Save the error errors.append(data)
def add_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: TextElement) -> Optional[nodes.reference]: # Fix references in docstrings that are inherited from the base pytket.backends.Backend class. mapping = app.config.external_url_mapping if node.astext() in mapping: newnode = nodes.reference( "", "", internal=False, refuri=mapping[node.astext()], reftitle=node.get("reftitle", node.astext()), ) newnode.append(contnode) return newnode return None
def visit_reference(self, node: Element) -> None: title = chm_htmlescape(node.astext(), True) self.append('<OBJECT type="text/sitemap">') self.append(' <PARAM name="Name" value="%s" />' % title) self.append(' <PARAM name="Local" value="%s" />' % node['refuri']) self.append('</OBJECT>') raise nodes.SkipNode
def visit_literal(self, node: Element) -> None: if 'kbd' in node['classes']: self.body.append( self.starttag(node, 'kbd', '', CLASS='docutils literal notranslate')) return lang = node.get("language", None) if 'code' not in node['classes'] or not lang: self.body.append( self.starttag(node, 'code', '', CLASS='docutils literal notranslate')) self.protect_literal_text += 1 return opts = self.config.highlight_options.get(lang, {}) highlighted = self.highlighter.highlight_block(node.astext(), lang, opts=opts, location=node, nowrap=True) starttag = self.starttag( node, "code", suffix="", CLASS="docutils literal highlight highlight-%s" % lang, ) self.body.append(starttag + highlighted.strip() + "</code>") raise nodes.SkipNode
def visit_literal_block(self, node: Element) -> None: if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight return super().visit_literal_block(node) lang = node.get('language', 'default') linenos = node.get('linenos', False) highlight_args = node.get('highlight_args', {}) highlight_args['force'] = node.get('force', False) opts = self.config.highlight_options.get(lang, {}) if linenos and self.config.html_codeblock_linenos_style: linenos = self.config.html_codeblock_linenos_style highlighted = self.highlighter.highlight_block(node.rawsource, lang, opts=opts, linenos=linenos, location=node, **highlight_args) starttag = self.starttag(node, 'div', suffix='', CLASS='highlight-%s notranslate' % lang) self.body.append(starttag + highlighted + '</div>\n') raise nodes.SkipNode
def visit_desc_parameter(self, node: nodes.Element) -> None: if not self.first_param: self.add_text(", ") else: self.first_param = 0 self.add_text(node.astext()) raise nodes.SkipNode
def visit_literal_block(self, node: Element) -> None: if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight return super().visit_literal_block(node) lang = node.get('language', 'default') linenos = node.get('linenos', False) highlight_args = node.get('highlight_args', {}) highlight_args['force'] = node.get('force', False) if lang is self.builder.config.highlight_language: # only pass highlighter options for original language opts = self.builder.config.highlight_options else: opts = {} highlighted = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, location=(self.builder.current_docname, node.line), **highlight_args) starttag = self.starttag(node, 'div', suffix='', CLASS='highlight-%s notranslate' % lang) self.body.append(starttag + highlighted + '</div>\n') raise nodes.SkipNode
def dispatch_visit(self, node: nodes.Element) -> None: if self.stop: raise nodes.StopTraversal # Skip comments if isinstance(node, nodes.Invisible): raise nodes.SkipNode # Skip all admonitions if isinstance(node, nodes.Admonition): raise nodes.SkipNode # Mark start of nested lists if isinstance(node, nodes.Sequential): self.list_level += 1 if self.list_level > 1: self.description += "-" # Skip the first title if it's the title of the page if not self.first_title_found and isinstance(node, nodes.title): self.first_title_found = True if node.astext() in self.known_titles: raise nodes.SkipNode if isinstance(node, nodes.raw) or isinstance(node.parent, nodes.literal_block): raise nodes.SkipNode # Only include leaf nodes in the description if len(node.children) == 0: text = node.astext().replace("\r", "").replace("\n", " ").strip() # Remove double spaces while text.find(" ") != -1: text = text.replace(" ", " ") # Put a space between elements if one does not already exist. if ( len(self.description) > 0 and len(text) > 0 and self.description[-1] not in string.whitespace and text[0] not in string.whitespace + string.punctuation ): self.description += " " self.description += text
def pass_to_format_logger(self, msg): if msg['type'] == 'ERROR': log = self.doc_logger.error elif msg['type'] == 'WARNING': log = self.doc_logger.warning else: return log(Element.astext(msg), line=msg['line'])
def clean_astext(node: Element) -> str: """Like node.astext(), but ignore images.""" node = node.deepcopy() for img in node.traverse(nodes.image): img['alt'] = '' for raw in node.traverse(nodes.raw): raw.parent.remove(raw) return node.astext()
def depart_paragraph(self, node: Element) -> None: within_admonition = isinstance(node.parent, nodes.Admonition) # Avoid wrapping paragraphs which are just URLs, such as those within # footnotes. looks_like_url = bool(re.match(r'(?i)^https?://[^ ]+?$', node.astext().strip())) if not within_admonition: self.end_state(wrap = not looks_like_url)
def visit_title(self, node: Element) -> None: if isinstance(node.parent, addnodes.seealso): self.body.append('.IP "') return elif isinstance(node.parent, nodes.section): if self.section_level == 0: # skip the document title raise nodes.SkipNode elif self.section_level == 1: self.body.append('.SH %s\n' % self.deunicode(node.astext().upper())) raise nodes.SkipNode return super().visit_title(node)
def depart_title(self, node: Element) -> None: """Change the permalinks for headlines. - for headlines: "Copy link to section: <section title>" - for tables: "Copy link to this table" - for admonitions: "Copy link to this <type>" Admonitions don't have an ID by default. The AdmonitionID post-transform adds IDs to admonitions. """ close_tag = self.context[-1] if (self.config.html_permalinks and self.builder.add_permalinks and node.parent.hasattr("ids") and node.parent["ids"]): # add permalink anchor to normal headings if close_tag.startswith("</h"): self.add_permalink_ref( node.parent, _(f"Copy link to section: {node.astext()}.")) # and to headings when the 'contents' directive is used elif close_tag.startswith("</a></h"): self.body.append("</a><a role='button' " "class='headerlink tooltipped tooltipped-ne' " 'href="#{}" ' 'aria-label="Copy link to this section: {}">'. format(node.parent["ids"][0], node.astext()) + ICONS["headerlink"]) elif isinstance(node.parent, nodes.table): self.body.append("</span>") self.add_permalink_ref(node.parent, _("Copy link to this table.")) elif isinstance(node.parent, nodes.Admonition): admon_type = type(node.parent).__name__ self.add_permalink_ref(node.parent, _(f"Copy link to this {admon_type}.")) elif isinstance(node.parent, nodes.table): self.body.append("</span>") self.body.append(self.context.pop()) if self.in_document_title: # type: ignore self.title = self.body[self.in_document_title:-1] # type: ignore self.in_document_title = 0 self.body_pre_docinfo.extend(self.body) self.html_title.extend(self.body) del self.body[:]
def visit_reference(self, node: Element) -> None: self.body.append(self.defs['reference'][0]) # avoid repeating escaping code... fine since # visit_Text calls astext() and only works on that afterwards self.visit_Text(node) # type: ignore self.body.append(self.defs['reference'][1]) uri = node.get('refuri', '') if uri.startswith('mailto:') or uri.startswith('http:') or \ uri.startswith('https:') or uri.startswith('ftp:'): # if configured, put the URL after the link if self.config.man_show_urls and node.astext() != uri: if uri.startswith('mailto:'): uri = uri[7:] self.body.extend([ ' <', self.defs['strong'][0], uri, self.defs['strong'][1], '>']) raise nodes.SkipNode
def visit_Text(self, node: nodes.Element) -> None: self.add_text(node.astext())
def visit_title(self, node: nodes.Element) -> None: if isinstance(node.parent, nodes.Admonition): self.add_text(node.astext() + ": ") raise nodes.SkipNode self.new_state(0)
def visit_desc_content(self, node: Element) -> None: """Add panel class to definitions.""" if self.config.html_collapsible_definitions and len(node.astext()) > 0: self.body.append(self.starttag(node, "dd", CLASS="panel")) else: self.body.append(self.starttag(node, "dd"))
def visit_literal_block(self, node: Element) -> None: """Overwrite code blocks. All code blocks have a header with at least a copy button. For code blocks with syntax highlighting, the language is shown on the left side and an optional caption is included in the center. """ if node.rawsource == node.astext(): # node doens't have markup, highlight it! lang = node.get("language", "default") linenos = node.get("linenos", False) highlight_args = node.get("highlight_args", {}) highlight_args["force"] = node.get("force", False) if lang is self.builder.config.highlight_language: # only pass highlighter options for original language opts = self.builder.config.highlight_options else: opts = {} if linenos and self.builder.config.html_codeblock_linenos_style: linenos = "inline" highlighted = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, location=node, **highlight_args, ) # Code blocks that don't have a caption are not wrapped inside a <container> # node so we add the header here. With captions, see: visit_caption if not (isinstance(node.parent, nodes.container) and node.parent.get("literal_block")): self.body.append(self.starttag(node, "div", CLASS="highlight")) code_header = "<div class='code-header'>\n" code_lang = lang.replace("default", "python").replace("console", "shell") code_header += f"<span class='code-lang'>{code_lang}</span>\n" code_header += COPY_BUTTON code_header += "</div>\n" self.body.append(code_header) # wrap the highlighted string in a div self.body.append(highlighted) if not (isinstance(node.parent, nodes.container) and node.parent.get("literal_block")): self.body.append("</div>\n") # we already included everything here in the `highlighted` string, # so we need to skip further processing raise nodes.SkipNode else: # node has markup, it's a samp directive or parsed-literal self.body.append(self.starttag(node, "div", CLASS="highlight")) code_header = "<div class='code-header'>\n" code_header += COPY_BUTTON code_header += "</div>\n" self.body.append(code_header) self.body.append("<pre><code>")
def _resolve_numref_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: if target in self.labels: docname, labelid, figname = self.labels.get(target, ('', '', '')) else: docname, labelid = self.anonlabels.get(target, ('', '')) figname = None if not docname: return None target_node = env.get_doctree(docname).ids.get(labelid) figtype = self.get_enumerable_node_type(target_node) if figtype is None: return None if figtype != 'section' and env.config.numfig is False: logger.warning(__('numfig is disabled. :numref: is ignored.'), location=node) return contnode try: fignumber = self.get_fignumber(env, builder, figtype, docname, target_node) if fignumber is None: return contnode except ValueError: logger.warning(__("no number is assigned for %s: %s"), figtype, labelid, location=node) return contnode try: if node['refexplicit']: title = contnode.astext() else: title = env.config.numfig_format.get(figtype, '') if figname is None and '{name}' in title: logger.warning(__('the link has no caption: %s'), title, location=node) return contnode else: fignum = '.'.join(map(str, fignumber)) if '{name}' in title or 'number' in title: # new style format (cf. "Fig.{number}") if figname: newtitle = title.format(name=figname, number=fignum) else: newtitle = title.format(number=fignum) else: # old style format (cf. "Fig.%s") newtitle = title % fignum except KeyError as exc: logger.warning(__('invalid numfig_format: %s (%r)'), title, exc, location=node) return contnode except TypeError: logger.warning(__('invalid numfig_format: %s'), title, location=node) return contnode return self.build_reference_node(fromdocname, builder, docname, labelid, newtitle, 'numref', nodeclass=addnodes.number_reference, title=title)
def visit_field(self, node: nodes.Element) -> None: print("Visit field", node.astext()) pass
def visit_raw(self, node: Element) -> None: if 'manpage' in node.get('format', '').split(): self.body.append(node.astext()) raise nodes.SkipNode
def visit_citation_reference(self, node: nodes.Element) -> None: self.add_text("[%s]" % node.astext()) raise nodes.SkipNode
def visit_field_name(self, node: nodes.Element) -> None: print("Visit field name", node.astext()) self.new_state(0)
def visit_system_message(self, node: nodes.Element) -> None: self.new_state(0) self.add_text("<SYSTEM MESSAGE: %s>" % node.astext()) self.end_state() raise nodes.SkipNode
def visit_raw(self, node: nodes.Element) -> None: if "text" in node.get("format", "").split(): self.new_state(0) self.add_text(node.astext()) self.end_state(wrap=False) raise nodes.SkipNode
def stringify(name: str, node: Element) -> str: reftitle = node.get('reftitle', node.astext()) return ':%s:`%s`' % (name, reftitle)
def visit_raw(self, node: Element) -> None: if 'text' in node.get('format', '').split(): self.new_state(0) self.add_text(node.astext()) self.end_state(wrap=False) raise nodes.SkipNode