From 4d322f1633602e52efc345e778ce010900c52da8 Mon Sep 17 00:00:00 2001 From: Shengyu Zhang Date: Fri, 22 Dec 2023 00:17:39 +0800 Subject: [PATCH] Impl roles composing --- docs/_images/rst.png | Bin 0 -> 1060 bytes docs/conf.py | 5 ++ docs/index.rst | 46 +++++++++++ docs/usage.rst | 25 ++++++ src/sphinxnotes/comboroles/__init__.py | 104 +++++++++++++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 docs/_images/rst.png create mode 100644 docs/usage.rst create mode 100644 src/sphinxnotes/comboroles/__init__.py diff --git a/docs/_images/rst.png b/docs/_images/rst.png new file mode 100644 index 0000000000000000000000000000000000000000..58a5e7e3a1cdbedf5a838d1dfee07dfe86c380da GIT binary patch literal 1060 zcmV+<1l#+GP)9L_t(|0iD6It{hhZ z0MK*q4!h&<;++l)P~J*32t{Bd3Kx;zM55pm=<BQRWl;5frJ=Rrmpy z=`x{mfy5YWX`GDbqM6+_8ym2n&vA8|QcCF^d^_4ZFN3%8G}^ngb``yY;GmR}yqQf} zb)j<*qDAn=Zb}qKtLTN+8~KG(t1((cvr8{od)4oY6KA5m$D!WnOEud2?`PelI_ zYA(At}xM~f0YZ@rBv+Pm)x>g38gCrF_Gm;D#zv`8h&JH=VgDq#G71c1?vU)^=&rv>IB}AF2==J1UD%H@{PB zoQWzlNmcLKx<-jV!jA`ogSDuFY%ju@%1%^?7T+2D&AWG2o4>~&4y~Ut)?}y!vw+c2 zBWeY;VDVi(GUo3K@zSp)rizZNu@@O9MvEO1sd3N>*$tQeAw15Z+vG422DYWUNTXH1 z8F8NYSQySrz85AJ`66G8oYSwmG+qjQsK~LCi=(bHt4F7DHtEQnS?G0XgiRC^HTNB4 zi{_D2NqnN;wW-HBDqLcU{-q+x$)Y5CofFT^2E`MzNEWi#e-%ySEn17o-Yz>aI+`rH zI$69ERci0i*&FrAEIBdw!d@|}Z22rf)Zhd9oyB1As$TD#-m~{rj052}42{J@&%Ek^ zfBL3BQ{Y+!e=MlI#vfKjgXzduC;ZxsBep|;vfBS)!q|=%8{?h9(nGC)d%zC=X zc-OG$hVzFt6ORVB*-UK9$ZE1lSdFqx8Mx?#$>kO{r6l*dTT1Cx#Wt@7=c#s+(7Uq1 eCWX#*^}hjpEL}K18Dwez0000 tuple[list[Node], list[system_message]]: + nodes: list[TextElement] = [] + reporter = self.inliner.reporter # type: ignore[attr-defined] + + # Run all RoleFunction, collect the produced nodes. + for comp in reversed(self.components): + ns, sysmsgs = comp(self.name, self.rawtext, self.text, self.lineno, self.inliner, self.options, self.content) + if len(sysmsgs) != 0: + return [], sysmsgs # once system_message is thrown, return + if len(ns) != 1: + msg = reporter.error(f'role should returns exactly 1 nodes, but {len(ns)} found: {ns}', line=self.lineno) + return [], [msg] + if not isinstance(ns[0], (Inline, TextElement)): + msg = reporter.error(f'node {ns[0]} is not ({Inline}, {TextElement})', line=self.lineno) + return [], [msg] + n = cast(TextElement, ns[0]) + if len(n.children) != 1: + msg = reporter.error(f'node {n} should has exactly 1 child, but {len(n.children)} found', line=self.lineno) + return [], [msg] + if not isinstance(n[0], Text): + msg = reporter.error(f'child of node {n} should have Text, but {type(n[0])} found', line=self.lineno) + return [], [msg] + nodes.append(n) + + + # ref: https://stackoverflow.com/questions/44829580/composing-roles-in-restructuredtext + if self.nested_parse: + memo = states.Struct( + document=self.inliner.document, # type: ignore[attr-defined] + reporter=reporter, + language=self.inliner.language) # type: ignore[attr-defined] + + n, sysmsgs = self.inliner.parse(self.text, self.lineno, memo, nodes[-1]) # type: ignore[attr-defined] + if len(sysmsgs) != 0: + return [], sysmsgs + nodes[-1].replace(nodes[-1][0], n) + + # Composite all nodes together. + for i in range(0, len(nodes) -1): + nodes[i].replace(nodes[i][0], nodes[i+1]) + + return [nodes[0]], [] + +def _config_inited(app:Sphinx, config:Config) -> None: + for name, cfg in config.comboroles_roles.items(): + if isinstance(cfg, list): + rolenames = cfg + nested_parse = False + else: + rolenames = cfg[0] + nested_parse = cfg[1] + app.add_role(name, CompositeRole(rolenames, nested_parse)) + + +def setup(app:Sphinx): + """Sphinx extension entrypoint.""" + + app.connect('config-inited', _config_inited) + + app.add_config_value('comboroles_roles', {}, 'env', types=dict[str, list[str] | tuple[list[str],bool]]) + + return { + "version": __version__, + "parallel_read_safe": True, + "parallel_write_safe": True, + }