def render_internal_link(self, token: SyntaxTreeNode) -> None: """Render link token `[text](link "title")`, where the link has not been identified as an external URL. """ destination = unquote(cast(str, token.attrGet("href") or "")) # make the path relative to an "including" document # this is set when using the `relative-docs` option of the MyST `include` directive relative_include = self.md_env.get("relative-docs", None) if relative_include is not None and destination.startswith( relative_include[0]): source_dir, include_dir = relative_include[1:] destination = os.path.relpath( os.path.join(include_dir, os.path.normpath(destination)), source_dir) potential_path = ( Path(self.doc_env.doc2path(self.doc_env.docname)).parent / destination if self.doc_env.srcdir # not set in some test situations else None) if (potential_path and potential_path.is_file() and not any( destination.endswith(suffix) for suffix in self.doc_env.config.source_suffix)): wrap_node = addnodes.download_reference( refdoc=self.doc_env.docname, reftarget=destination, reftype="myst", refdomain=None, # Added to enable cross-linking refexplicit=len(token.children or []) > 0, refwarn=False, ) classes = ["xref", "download", "myst"] text = destination if not token.children else "" else: wrap_node = addnodes.pending_xref( refdoc=self.doc_env.docname, reftarget=destination, reftype="myst", refdomain=None, # Added to enable cross-linking refexplicit=len(token.children or []) > 0, refwarn=True, ) classes = ["xref", "myst"] text = "" self.add_line_and_source_path(wrap_node, token) title = token.attrGet("title") if title: wrap_node["title"] = title self.current_node.append(wrap_node) inner_node = nodes.inline("", text, classes=classes) wrap_node.append(inner_node) with self.current_node_context(inner_node): self.render_children(token)
def render_image(self, token: SyntaxTreeNode) -> None: img_node = nodes.image() self.add_line_and_source_path(img_node, token) destination = cast(str, token.attrGet("src") or "") if self.config.get("relative-images", None) is not None and not is_external_url( destination, None, True): # make the path relative to an "including" document destination = os.path.normpath( os.path.join( self.config.get("relative-images", ""), os.path.normpath(destination), )) img_node["uri"] = destination img_node["alt"] = self.renderInlineAsText(token.children or []) title = token.attrGet("title") if title: img_node["title"] = token.attrGet("title") self.current_node.append(img_node)
def render_link(self, token: SyntaxTreeNode) -> None: if token.markup == "autolink": return self.render_autolink(token) ref_node = nodes.reference() self.add_line_and_source_path(ref_node, token) destination = cast(str, token.attrGet("href") or "") if self.config.get("relative-docs", None) is not None and destination.startswith( self.config["relative-docs"][0]): # make the path relative to an "including" document source_dir, include_dir = self.config["relative-docs"][1:] destination = os.path.relpath( os.path.join(include_dir, os.path.normpath(destination)), source_dir) ref_node["refuri"] = destination # type: ignore[index] title = token.attrGet("title") if title: ref_node["title"] = title # type: ignore[index] next_node = ref_node # TODO currently any reference with a fragment # is deemed external # (if anchors are not enabled) # This comes from recommonmark, but I am not sure of the rationale for it if is_external_url( destination, self.config.get("myst_url_schemes", None), "heading_anchors" not in self.config.get("myst_extensions", []), ): self.current_node.append(next_node) with self.current_node_context(ref_node): self.render_children(token) else: self.handle_cross_reference(token, destination)
def handle_cross_reference(self, token: SyntaxTreeNode, destination: str) -> None: """Create nodes for references that are not immediately resolvable.""" wrap_node = addnodes.pending_xref( refdoc=self.doc_env.docname, reftarget=unquote(destination), reftype="myst", refdomain=None, # Added to enable cross-linking refexplicit=len(token.children or []) > 0, refwarn=True, ) self.add_line_and_source_path(wrap_node, token) title = token.attrGet("title") if title: wrap_node["title"] = title self.current_node.append(wrap_node) inner_node = nodes.inline("", "", classes=["xref", "myst"]) wrap_node.append(inner_node) with self.current_node_context(inner_node): self.render_children(token)
def render_heading(self, token: SyntaxTreeNode) -> None: """This extends the docutils method, to allow for the addition of heading ids. These ids are computed by the ``markdown-it-py`` ``anchors_plugin`` as "slugs" which are unique to a document. The approach is similar to ``sphinx.ext.autosectionlabel`` """ super().render_heading(token) if not isinstance(self.current_node, nodes.section): return # create the slug string slug = cast(str, token.attrGet("id")) if slug is None: return section = self.current_node doc_slug = self.doc_env.doc2path(self.doc_env.docname, base=False) + "#" + slug # save the reference in the standard domain, so that it can be handled properly domain = cast(StandardDomain, self.doc_env.get_domain("std")) if doc_slug in domain.labels: other_doc = self.doc_env.doc2path(domain.labels[doc_slug][0]) self.create_warning( f"duplicate label {doc_slug}, other instance in {other_doc}", line=section.line, subtype="anchor", ) labelid = section["ids"][0] domain.anonlabels[doc_slug] = self.doc_env.docname, labelid domain.labels[doc_slug] = ( self.doc_env.docname, labelid, clean_astext(section[0]), ) self.doc_env.metadata[self.doc_env.docname]["myst_anchors"] = True section["myst-anchor"] = doc_slug
def render_autolink(self, token: SyntaxTreeNode) -> None: refuri = target = escapeHtml(token.attrGet("href") or "") # type: ignore[arg-type] ref_node = nodes.reference(target, target, refuri=refuri) self.add_line_and_source_path(ref_node, token) self.current_node.append(ref_node)