"""Convert Markdown-it tokens to docutils nodes, including sphinx specific elements."""from__future__importannotationsimportosfrompathlibimportPathfromtypingimportcastfromuuidimportuuid4fromdocutilsimportnodesfrommarkdown_it.treeimportSyntaxTreeNodefromsphinximportaddnodesfromsphinx.domains.mathimportMathDomainfromsphinx.environmentimportBuildEnvironmentfromsphinx.ext.intersphinximportInventoryAdapterfromsphinx.utilimportloggingfrommyst_parserimportinventoryfrommyst_parser.mdit_to_docutils.baseimportDocutilsRenderer,token_linefrommyst_parser.warnings_importMystWarningsLOGGER=logging.getLogger(__name__)
[文档]classSphinxRenderer(DocutilsRenderer):"""A markdown-it-py renderer to populate (in-place) a `docutils.document` AST. This is sub-class of `DocutilsRenderer` that handles sphinx specific aspects, such as cross-referencing. """@propertydefsphinx_env(self)->BuildEnvironment:returnself.document.settings.envdef_process_wrap_node(self,wrap_node:nodes.Element,token:SyntaxTreeNode,explicit:bool,classes:list[str],path_dest:str,):"""Process a wrap node, which is a node that wraps a link."""self.add_line_and_source_path(wrap_node,token)self.copy_attributes(token,wrap_node,("class","id","title"))self.current_node.append(wrap_node)ifexplicit:inner_node=nodes.inline("","",classes=classes)withself.current_node_context(inner_node):self.render_children(token)elifisinstance(wrap_node,addnodes.download_reference):inner_node=nodes.literal(path_dest,path_dest,classes=classes)else:inner_node=nodes.inline("","",classes=classes)wrap_node.append(inner_node)def_handle_relative_docs(self,destination:str)->str:"""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)ifrelative_includeisnotNoneanddestination.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)returndestination
[文档]defrender_link_project(self,token:SyntaxTreeNode)->None:destination=cast(str,token.attrGet("href")or"")ifdestination.startswith("project:"):destination=destination[8:]ifdestination.startswith("#"):returnself.render_link_anchor(token,destination)ifnotself.sphinx_env.srcdir:# not set in some test situationsreturnself.render_link_url(token)destination=self.md.normalizeLinkText(destination)destination=self._handle_relative_docs(destination)path_dest,*_path_ids=destination.split("#",maxsplit=1)path_id=_path_ids[0]if_path_idselseNoneexplicit=(token.info!="auto")and(len(token.childrenor[])>0)_,abs_path=self.sphinx_env.relfn2path(path_dest,self.sphinx_env.docname)docname=self.sphinx_env.path2doc(abs_path)ifnotdocname:self.create_warning(f"Could not find document: {abs_path}",MystWarnings.XREF_MISSING,line=token_line(token,0),append_to=self.current_node,)returnself.render_link_url(token)wrap_node=addnodes.pending_xref(refdomain="doc",reftarget=docname,reftargetid=path_id,refdoc=self.sphinx_env.docname,reftype="myst",refexplicit=explicit,)classes=["xref","myst"]self._process_wrap_node(wrap_node,token,explicit,classes,destination)
[文档]defrender_link_unknown(self,token:SyntaxTreeNode)->None:"""Render link token `[text](link "title")`, where the link has not been identified as an external URL. """destination=self.md.normalizeLinkText(cast(str,token.attrGet("href")or""))destination=self._handle_relative_docs(destination)explicit=(token.info!="auto")and(len(token.childrenor[])>0)kwargs={"refdoc":self.sphinx_env.docname,"reftype":"myst","refexplicit":explicit,}path_dest,*_path_ids=destination.split("#",maxsplit=1)path_id=_path_ids[0]if_path_idselseNonepotential_path:None|Path=Noneifself.sphinx_env.srcdir:# not set in some test situations_,path_str=self.sphinx_env.relfn2path(path_dest,self.sphinx_env.docname)potential_path=Path(path_str)ifpotential_pathandpotential_path.is_file():docname=self.sphinx_env.path2doc(str(potential_path))ifdocname:wrap_node=addnodes.pending_xref(refdomain="doc",reftarget=docname,reftargetid=path_id,**kwargs)classes=["xref","myst"]else:wrap_node=addnodes.download_reference(refdomain=None,reftarget=path_dest,**kwargs)classes=["xref","download","myst"]else:wrap_node=addnodes.pending_xref(refdomain=None,reftarget=destination,**kwargs)classes=["xref","myst"]self._process_wrap_node(wrap_node,token,explicit,classes,path_dest)
[文档]defrender_math_block_label(self,token:SyntaxTreeNode)->None:"""Render math with referenceable labels, e.g. ``$a=1$ (label)``."""label=token.infocontent=token.contentnode=nodes.math_block(content,content,nowrap=False,number=None,label=label)target=self.add_math_target(node)self.add_line_and_source_path(target,token)self.current_node.append(target)self.add_line_and_source_path(node,token)self.current_node.append(node)
def_random_label(self)->str:returnstr(uuid4())
[文档]defrender_amsmath(self,token:SyntaxTreeNode)->None:"""Renderer for the amsmath extension."""# environment = token.meta["environment"]content=token.contentiftoken.meta["numbered"]!="*":# TODO how to parse and reference labels within environment?# for now we give create a unique hash, so the equation will be numbered# but there will be no reference clasheslabel=self._random_label()node=nodes.math_block(content,content,nowrap=True,number=None,classes=["amsmath"],label=label,)target=self.add_math_target(node)self.add_line_and_source_path(target,token)self.current_node.append(target)else:node=nodes.math_block(content,content,nowrap=True,number=None,classes=["amsmath"])self.add_line_and_source_path(node,token)self.current_node.append(node)
[文档]defadd_math_target(self,node:nodes.math_block)->nodes.target:# Code mainly copied from sphinx.directives.patches.MathDirective# register label to domaindomain=cast(MathDomain,self.sphinx_env.get_domain("math"))domain.note_equation(self.sphinx_env.docname,node["label"],location=node)node["number"]=domain.get_equation_number_for(node["label"])node["docname"]=self.sphinx_env.docname# create target nodenode_id=nodes.make_id("equation-{}".format(node["label"]))target=nodes.target("","",ids=[node_id])self.document.note_explicit_target(target)returntarget