nixos-render-docs: drop options, env parameters

these weren't used for anything. options never was (and does not contain
any information for the renderer that we *want* to honor), and env is
not used because typed renderer state is much more useful for all our cases.
This commit is contained in:
pennae
2023-02-17 21:29:22 +01:00
parent 0236dcb59f
commit 6f253fc70b
8 changed files with 288 additions and 538 deletions

View File

@@ -1,4 +1,4 @@
from collections.abc import Mapping, MutableMapping, Sequence from collections.abc import Mapping, Sequence
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, cast, Optional from typing import Any, cast, Optional
from urllib.parse import quote from urllib.parse import quote
@@ -6,7 +6,6 @@ from urllib.parse import quote
from .md import Renderer from .md import Renderer
from markdown_it.token import Token from markdown_it.token import Token
from markdown_it.utils import OptionsDict
_asciidoc_escapes = { _asciidoc_escapes = {
# escape all dots, just in case one is pasted at SOL # escape all dots, just in case one is pasted at SOL
@@ -95,142 +94,103 @@ class AsciiDocRenderer(Renderer):
self._list_stack.pop() self._list_stack.pop()
return "" return ""
def text(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def text(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
return asciidoc_escape(token.content) return asciidoc_escape(token.content)
def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._break() return self._break()
def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def hardbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def hardbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return " +\n" return " +\n"
def softbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def softbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f" " return f" "
def code_inline(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_inline(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
return f"``{asciidoc_escape(token.content)}``" return f"``{asciidoc_escape(token.content)}``"
def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_block(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str: return self.fence(token, tokens, i)
return self.fence(token, tokens, i, options, env) def link_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
def link_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
return f"link:{quote(cast(str, token.attrs['href']), safe='/:')}[" return f"link:{quote(cast(str, token.attrs['href']), safe='/:')}["
def link_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def link_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "]" return "]"
def list_item_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._enter_block(True) self._enter_block(True)
# allow the next token to be a block or an inline. # allow the next token to be a block or an inline.
return f'\n{self._list_stack[-1].head} {{empty}}' return f'\n{self._list_stack[-1].head} {{empty}}'
def list_item_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return "\n" return "\n"
def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._list_open(token, '*') return self._list_open(token, '*')
def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._list_close() return self._list_close()
def em_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "__" return "__"
def em_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "__" return "__"
def strong_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "**" return "**"
def strong_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "**" return "**"
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def fence(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
attrs = f"[source,{token.info}]\n" if token.info else "" attrs = f"[source,{token.info}]\n" if token.info else ""
code = token.content code = token.content
if code.endswith('\n'): if code.endswith('\n'):
code = code[:-1] code = code[:-1]
return f"{self._break(True)}{attrs}----\n{code}\n----" return f"{self._break(True)}{attrs}----\n{code}\n----"
def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
pbreak = self._break(True) pbreak = self._break(True)
self._enter_block(False) self._enter_block(False)
return f"{pbreak}[quote]\n{self._parstack[-2].block_delim}\n" return f"{pbreak}[quote]\n{self._parstack[-2].block_delim}\n"
def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return f"\n{self._parstack[-1].block_delim}" return f"\n{self._parstack[-1].block_delim}"
def note_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("NOTE") return self._admonition_open("NOTE")
def note_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def caution_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("CAUTION") return self._admonition_open("CAUTION")
def caution_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def important_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("IMPORTANT") return self._admonition_open("IMPORTANT")
def important_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def tip_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("TIP") return self._admonition_open("TIP")
def tip_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def warning_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("WARNING") return self._admonition_open("WARNING")
def warning_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def dl_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f"{self._break()}[]" return f"{self._break()}[]"
def dl_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def dt_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._break() return self._break()
def dt_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._enter_block(True) self._enter_block(True)
return ":: {empty}" return ":: {empty}"
def dd_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def dd_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return "\n" return "\n"
def myst_role(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def myst_role(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
content = asciidoc_escape(token.content) content = asciidoc_escape(token.content)
if token.meta['name'] == 'manpage' and (url := self._manpage_urls.get(token.content)): if token.meta['name'] == 'manpage' and (url := self._manpage_urls.get(token.content)):
return f"link:{quote(url, safe='/:')}[{content}]" return f"link:{quote(url, safe='/:')}[{content}]"
return f"[.{token.meta['name']}]``{asciidoc_escape(token.content)}``" return f"[.{token.meta['name']}]``{asciidoc_escape(token.content)}``"
def inline_anchor(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def inline_anchor(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
return f"[[{token.attrs['id']}]]" return f"[[{token.attrs['id']}]]"
def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
(id_part, class_part) = ("", "") (id_part, class_part) = ("", "")
if id := token.attrs.get('id'): if id := token.attrs.get('id'):
@@ -240,22 +200,17 @@ class AsciiDocRenderer(Renderer):
class_part = "kbd:[" class_part = "kbd:["
self._attrspans.append("]") self._attrspans.append("]")
else: else:
return super().attr_span_begin(token, tokens, i, options, env) return super().attr_span_begin(token, tokens, i)
else: else:
self._attrspans.append("") self._attrspans.append("")
return id_part + class_part return id_part + class_part
def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._attrspans.pop() return self._attrspans.pop()
def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return token.markup.replace("#", "=") + " " return token.markup.replace("#", "=") + " "
def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "\n" return "\n"
def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._list_open(token, '.') return self._list_open(token, '.')
def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._list_close() return self._list_close()

View File

@@ -1,11 +1,10 @@
from collections.abc import Mapping, MutableMapping, Sequence from collections.abc import Mapping, Sequence
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, cast, Optional from typing import Any, cast, Optional
from .md import md_escape, md_make_code, Renderer from .md import md_escape, md_make_code, Renderer
from markdown_it.token import Token from markdown_it.token import Token
from markdown_it.utils import OptionsDict
@dataclass(kw_only=True) @dataclass(kw_only=True)
class List: class List:
@@ -57,39 +56,29 @@ class CommonMarkRenderer(Renderer):
return s return s
return f"\n{self._parstack[-1].indent}".join(s.splitlines()) return f"\n{self._parstack[-1].indent}".join(s.splitlines())
def text(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def text(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
return self._indent_raw(md_escape(token.content)) return self._indent_raw(md_escape(token.content))
def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._maybe_parbreak() return self._maybe_parbreak()
def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def hardbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def hardbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f" {self._break()}" return f" {self._break()}"
def softbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def softbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._break() return self._break()
def code_inline(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_inline(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
return md_make_code(token.content) return md_make_code(token.content)
def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_block(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str: return self.fence(token, tokens, i)
return self.fence(token, tokens, i, options, env) def link_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
def link_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
self._link_stack.append(cast(str, token.attrs['href'])) self._link_stack.append(cast(str, token.attrs['href']))
return "[" return "["
def link_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def link_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f"]({md_escape(self._link_stack.pop())})" return f"]({md_escape(self._link_stack.pop())})"
def list_item_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
lst = self._list_stack[-1] lst = self._list_stack[-1]
lbreak = "" if not lst.first_item_seen else self._break() * (1 if lst.compact else 2) lbreak = "" if not lst.first_item_seen else self._break() * (1 if lst.compact else 2)
lst.first_item_seen = True lst.first_item_seen = True
@@ -99,132 +88,99 @@ class CommonMarkRenderer(Renderer):
lst.next_idx += 1 lst.next_idx += 1
self._enter_block(" " * (len(head) + 1)) self._enter_block(" " * (len(head) + 1))
return f'{lbreak}{head} ' return f'{lbreak}{head} '
def list_item_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return "" return ""
def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.append(List(compact=bool(token.meta['compact']))) self._list_stack.append(List(compact=bool(token.meta['compact'])))
return self._maybe_parbreak() return self._maybe_parbreak()
def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.pop() self._list_stack.pop()
return "" return ""
def em_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "*" return "*"
def em_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "*" return "*"
def strong_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "**" return "**"
def strong_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "**" return "**"
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def fence(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
code = token.content code = token.content
if code.endswith('\n'): if code.endswith('\n'):
code = code[:-1] code = code[:-1]
pbreak = self._maybe_parbreak() pbreak = self._maybe_parbreak()
return pbreak + self._indent_raw(md_make_code(code, info=token.info, multiline=True)) return pbreak + self._indent_raw(md_make_code(code, info=token.info, multiline=True))
def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
pbreak = self._maybe_parbreak() pbreak = self._maybe_parbreak()
self._enter_block("> ") self._enter_block("> ")
return pbreak + "> " return pbreak + "> "
def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return "" return ""
def note_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("Note") return self._admonition_open("Note")
def note_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def caution_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("Caution") return self._admonition_open("Caution")
def caution_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def important_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("Important") return self._admonition_open("Important")
def important_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def tip_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("Tip") return self._admonition_open("Tip")
def tip_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def warning_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("Warning") return self._admonition_open("Warning")
def warning_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def dl_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.append(List(compact=False)) self._list_stack.append(List(compact=False))
return "" return ""
def dl_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.pop() self._list_stack.pop()
return "" return ""
def dt_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
pbreak = self._maybe_parbreak() pbreak = self._maybe_parbreak()
self._enter_block(" ") self._enter_block(" ")
# add an opening zero-width non-joiner to separate *our* emphasis from possible # add an opening zero-width non-joiner to separate *our* emphasis from possible
# emphasis in the provided term # emphasis in the provided term
return f'{pbreak} - *{chr(0x200C)}' return f'{pbreak} - *{chr(0x200C)}'
def dt_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f"{chr(0x200C)}*" return f"{chr(0x200C)}*"
def dd_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
return "" return ""
def dd_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return "" return ""
def myst_role(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def myst_role(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._parstack[-1].continuing = True self._parstack[-1].continuing = True
content = md_make_code(token.content) content = md_make_code(token.content)
if token.meta['name'] == 'manpage' and (url := self._manpage_urls.get(token.content)): if token.meta['name'] == 'manpage' and (url := self._manpage_urls.get(token.content)):
return f"[{content}]({url})" return f"[{content}]({url})"
return content # no roles in regular commonmark return content # no roles in regular commonmark
def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
# there's no way we can emit attrspans correctly in all cases. we could use inline # there's no way we can emit attrspans correctly in all cases. we could use inline
# html for ids, but that would not round-trip. same holds for classes. since this # html for ids, but that would not round-trip. same holds for classes. since this
# renderer is only used for approximate options export and all of these things are # renderer is only used for approximate options export and all of these things are
# not allowed in options we can ignore them for now. # not allowed in options we can ignore them for now.
return "" return ""
def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return token.markup + " " return token.markup + " "
def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "\n" return "\n"
def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.append( self._list_stack.append(
List(next_idx = cast(int, token.attrs.get('start', 1)), List(next_idx = cast(int, token.attrs.get('start', 1)),
compact = bool(token.meta['compact']))) compact = bool(token.meta['compact'])))
return self._maybe_parbreak() return self._maybe_parbreak()
def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.pop() self._list_stack.pop()
return "" return ""

View File

@@ -1,9 +1,8 @@
from collections.abc import Mapping, MutableMapping, Sequence from collections.abc import Mapping, Sequence
from typing import Any, cast, Optional, NamedTuple from typing import Any, cast, Optional, NamedTuple
import markdown_it import markdown_it
from markdown_it.token import Token from markdown_it.token import Token
from markdown_it.utils import OptionsDict
from xml.sax.saxutils import escape, quoteattr from xml.sax.saxutils import escape, quoteattr
from .md import Renderer from .md import Renderer
@@ -44,13 +43,11 @@ class DocBookRenderer(Renderer):
self._headings = [] self._headings = []
self._attrspans = [] self._attrspans = []
def render(self, tokens: Sequence[Token], options: OptionsDict, def render(self, tokens: Sequence[Token]) -> str:
env: MutableMapping[str, Any]) -> str: result = super().render(tokens)
result = super().render(tokens, options, env) result += self._close_headings(None)
result += self._close_headings(None, env)
return result return result
def renderInline(self, tokens: Sequence[Token], options: OptionsDict, def renderInline(self, tokens: Sequence[Token]) -> str:
env: MutableMapping[str, Any]) -> str:
# HACK to support docbook links and xrefs. link handling is only necessary because the docbook # HACK to support docbook links and xrefs. link handling is only necessary because the docbook
# manpage stylesheet converts - in urls to a mathematical minus, which may be somewhat incorrect. # manpage stylesheet converts - in urls to a mathematical minus, which may be somewhat incorrect.
for i, token in enumerate(tokens): for i, token in enumerate(tokens):
@@ -64,135 +61,98 @@ class DocBookRenderer(Renderer):
if tokens[i + 1].type == 'text' and tokens[i + 1].content == token.attrs['href']: if tokens[i + 1].type == 'text' and tokens[i + 1].content == token.attrs['href']:
tokens[i + 1].content = '' tokens[i + 1].content = ''
return super().renderInline(tokens, options, env) return super().renderInline(tokens)
def text(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def text(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return escape(token.content) return escape(token.content)
def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<para>" return "<para>"
def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</para>" return "</para>"
def hardbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def hardbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<literallayout>\n</literallayout>" return "<literallayout>\n</literallayout>"
def softbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def softbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
# should check options.breaks() and emit hard break if so # should check options.breaks() and emit hard break if so
return "\n" return "\n"
def code_inline(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_inline(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f"<literal>{escape(token.content)}</literal>" return f"<literal>{escape(token.content)}</literal>"
def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_block(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f"<programlisting>{escape(token.content)}</programlisting>" return f"<programlisting>{escape(token.content)}</programlisting>"
def link_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def link_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._link_tags.append(token.tag) self._link_tags.append(token.tag)
href = cast(str, token.attrs['href']) href = cast(str, token.attrs['href'])
(attr, start) = ('linkend', 1) if href[0] == '#' else ('xlink:href', 0) (attr, start) = ('linkend', 1) if href[0] == '#' else ('xlink:href', 0)
return f"<{token.tag} {attr}={quoteattr(href[start:])}>" return f"<{token.tag} {attr}={quoteattr(href[start:])}>"
def link_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def link_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f"</{self._link_tags.pop()}>" return f"</{self._link_tags.pop()}>"
def list_item_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<listitem>" return "<listitem>"
def list_item_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</listitem>\n" return "</listitem>\n"
# HACK open and close para for docbook change size. remove soon. # HACK open and close para for docbook change size. remove soon.
def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
spacing = ' spacing="compact"' if token.meta.get('compact', False) else '' spacing = ' spacing="compact"' if token.meta.get('compact', False) else ''
return f"<para><itemizedlist{spacing}>\n" return f"<para><itemizedlist{spacing}>\n"
def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "\n</itemizedlist></para>" return "\n</itemizedlist></para>"
def em_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<emphasis>" return "<emphasis>"
def em_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</emphasis>" return "</emphasis>"
def strong_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<emphasis role=\"strong\">" return "<emphasis role=\"strong\">"
def strong_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</emphasis>" return "</emphasis>"
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def fence(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
info = f" language={quoteattr(token.info)}" if token.info != "" else "" info = f" language={quoteattr(token.info)}" if token.info != "" else ""
return f"<programlisting{info}>{escape(token.content)}</programlisting>" return f"<programlisting{info}>{escape(token.content)}</programlisting>"
def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<para><blockquote>" return "<para><blockquote>"
def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</blockquote></para>" return "</blockquote></para>"
def note_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<para><note>" return "<para><note>"
def note_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</note></para>" return "</note></para>"
def caution_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<para><caution>" return "<para><caution>"
def caution_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</caution></para>" return "</caution></para>"
def important_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<para><important>" return "<para><important>"
def important_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</important></para>" return "</important></para>"
def tip_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<para><tip>" return "<para><tip>"
def tip_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</tip></para>" return "</tip></para>"
def warning_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "<para><warning>" return "<para><warning>"
def warning_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</warning></para>" return "</warning></para>"
# markdown-it emits tokens based on the html syntax tree, but docbook is # markdown-it emits tokens based on the html syntax tree, but docbook is
# slightly different. html has <dl>{<dt/>{<dd/>}}</dl>, # slightly different. html has <dl>{<dt/>{<dd/>}}</dl>,
# docbook has <variablelist>{<varlistentry><term/><listitem/></varlistentry>}<variablelist> # docbook has <variablelist>{<varlistentry><term/><listitem/></varlistentry>}<variablelist>
# we have to reject multiple definitions for the same term for time being. # we have to reject multiple definitions for the same term for time being.
def dl_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._deflists.append(Deflist()) self._deflists.append(Deflist())
return "<para><variablelist>" return "<para><variablelist>"
def dl_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._deflists.pop() self._deflists.pop()
return "</variablelist></para>" return "</variablelist></para>"
def dt_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._deflists[-1].has_dd = False self._deflists[-1].has_dd = False
return "<varlistentry><term>" return "<varlistentry><term>"
def dt_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</term>" return "</term>"
def dd_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
if self._deflists[-1].has_dd: if self._deflists[-1].has_dd:
raise Exception("multiple definitions per term not supported") raise Exception("multiple definitions per term not supported")
self._deflists[-1].has_dd = True self._deflists[-1].has_dd = True
return "<listitem>" return "<listitem>"
def dd_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "</listitem></varlistentry>" return "</listitem></varlistentry>"
def myst_role(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def myst_role(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
if token.meta['name'] == 'command': if token.meta['name'] == 'command':
return f"<command>{escape(token.content)}</command>" return f"<command>{escape(token.content)}</command>"
if token.meta['name'] == 'file': if token.meta['name'] == 'file':
@@ -215,8 +175,7 @@ class DocBookRenderer(Renderer):
else: else:
return ref return ref
raise NotImplementedError("md node not supported yet", token) raise NotImplementedError("md node not supported yet", token)
def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
# we currently support *only* inline anchors and the special .keycap class to produce # we currently support *only* inline anchors and the special .keycap class to produce
# <keycap> docbook elements. # <keycap> docbook elements.
(id_part, class_part) = ("", "") (id_part, class_part) = ("", "")
@@ -227,31 +186,26 @@ class DocBookRenderer(Renderer):
class_part = "<keycap>" class_part = "<keycap>"
self._attrspans.append("</keycap>") self._attrspans.append("</keycap>")
else: else:
return super().attr_span_begin(token, tokens, i, options, env) return super().attr_span_begin(token, tokens, i)
else: else:
self._attrspans.append("") self._attrspans.append("")
return id_part + class_part return id_part + class_part
def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._attrspans.pop() return self._attrspans.pop()
def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
start = f' startingnumber="{token.attrs["start"]}"' if 'start' in token.attrs else "" start = f' startingnumber="{token.attrs["start"]}"' if 'start' in token.attrs else ""
spacing = ' spacing="compact"' if token.meta.get('compact', False) else '' spacing = ' spacing="compact"' if token.meta.get('compact', False) else ''
return f"<orderedlist{start}{spacing}>" return f"<orderedlist{start}{spacing}>"
def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return f"</orderedlist>" return f"</orderedlist>"
def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
hlevel = int(token.tag[1:]) hlevel = int(token.tag[1:])
result = self._close_headings(hlevel, env) result = self._close_headings(hlevel)
(tag, attrs) = self._heading_tag(token, tokens, i, options, env) (tag, attrs) = self._heading_tag(token, tokens, i)
self._headings.append(Heading(tag, hlevel)) self._headings.append(Heading(tag, hlevel))
attrs_str = "".join([ f" {k}={quoteattr(v)}" for k, v in attrs.items() ]) attrs_str = "".join([ f" {k}={quoteattr(v)}" for k, v in attrs.items() ])
return result + f'<{tag}{attrs_str}>\n<title>' return result + f'<{tag}{attrs_str}>\n<title>'
def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
heading = self._headings[-1] heading = self._headings[-1]
result = '</title>' result = '</title>'
if heading.container_tag == 'part': if heading.container_tag == 'part':
@@ -263,16 +217,14 @@ class DocBookRenderer(Renderer):
maybe_id = " xml:id=" + quoteattr(id + "-intro") maybe_id = " xml:id=" + quoteattr(id + "-intro")
result += f"<partintro{maybe_id}>" result += f"<partintro{maybe_id}>"
return result return result
def example_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def example_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
if id := token.attrs.get('id'): if id := token.attrs.get('id'):
return f"<anchor xml:id={quoteattr(cast(str, id))} />" return f"<anchor xml:id={quoteattr(cast(str, id))} />"
return "" return ""
def example_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def example_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def _close_headings(self, level: Optional[int], env: MutableMapping[str, Any]) -> str: def _close_headings(self, level: Optional[int]) -> str:
# we rely on markdown-it producing h{1..6} tags in token.tag for this to work # we rely on markdown-it producing h{1..6} tags in token.tag for this to work
result = [] result = []
while len(self._headings): while len(self._headings):
@@ -285,8 +237,7 @@ class DocBookRenderer(Renderer):
break break
return "\n".join(result) return "\n".join(result)
def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int) -> tuple[str, dict[str, str]]:
env: MutableMapping[str, Any]) -> tuple[str, dict[str, str]]:
attrs = {} attrs = {}
if id := token.attrs.get('id'): if id := token.attrs.get('id'):
attrs['xml:id'] = cast(str, id) attrs['xml:id'] = cast(str, id)

View File

@@ -1,4 +1,4 @@
from collections.abc import Mapping, MutableMapping, Sequence from collections.abc import Mapping, Sequence
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, cast, Iterable, Optional from typing import Any, cast, Iterable, Optional
@@ -6,7 +6,6 @@ import re
import markdown_it import markdown_it
from markdown_it.token import Token from markdown_it.token import Token
from markdown_it.utils import OptionsDict
from .md import Renderer from .md import Renderer
@@ -123,36 +122,27 @@ class ManpageRenderer(Renderer):
self._leave_block() self._leave_block()
return ".RE" return ".RE"
def render(self, tokens: Sequence[Token], options: OptionsDict, def render(self, tokens: Sequence[Token]) -> str:
env: MutableMapping[str, Any]) -> str:
self._do_parbreak_stack = [ False ] self._do_parbreak_stack = [ False ]
self._font_stack = [ "\\fR" ] self._font_stack = [ "\\fR" ]
return super().render(tokens, options, env) return super().render(tokens)
def text(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def text(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return man_escape(token.content) return man_escape(token.content)
def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._maybe_parbreak() return self._maybe_parbreak()
def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def hardbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def hardbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return ".br" return ".br"
def softbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def softbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return " " return " "
def code_inline(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_inline(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
s = _protect_spaces(man_escape(token.content)) s = _protect_spaces(man_escape(token.content))
return f"\\fR\\(oq{s}\\(cq\\fP" if self.inline_code_is_quoted else s return f"\\fR\\(oq{s}\\(cq\\fP" if self.inline_code_is_quoted else s
def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_block(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str: return self.fence(token, tokens, i)
return self.fence(token, tokens, i, options, env) def link_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
def link_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
env: MutableMapping[str, Any]) -> str:
href = cast(str, token.attrs['href']) href = cast(str, token.attrs['href'])
self._link_stack.append(href) self._link_stack.append(href)
text = "" text = ""
@@ -161,8 +151,7 @@ class ManpageRenderer(Renderer):
text = self._href_targets[href] text = self._href_targets[href]
self._font_stack.append("\\fB") self._font_stack.append("\\fB")
return f"\\fB{text}\0 <" return f"\\fB{text}\0 <"
def link_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def link_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
href = self._link_stack.pop() href = self._link_stack.pop()
text = "" text = ""
if self.link_footnotes is not None: if self.link_footnotes is not None:
@@ -174,8 +163,7 @@ class ManpageRenderer(Renderer):
text = "\\fR" + man_escape(f"[{idx}]") text = "\\fR" + man_escape(f"[{idx}]")
self._font_stack.pop() self._font_stack.pop()
return f">\0 {text}{self._font_stack[-1]}" return f">\0 {text}{self._font_stack[-1]}"
def list_item_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._enter_block() self._enter_block()
lst = self._list_stack[-1] lst = self._list_stack[-1]
maybe_space = '' if lst.compact or not lst.first_item_seen else '.sp\n' maybe_space = '' if lst.compact or not lst.first_item_seen else '.sp\n'
@@ -189,36 +177,28 @@ class ManpageRenderer(Renderer):
f'.RS {lst.width}\n' f'.RS {lst.width}\n'
f"\\h'-{len(head) + 1}'\\fB{man_escape(head)}\\fP\\h'1'\\c" f"\\h'-{len(head) + 1}'\\fB{man_escape(head)}\\fP\\h'1'\\c"
) )
def list_item_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return ".RE" return ".RE"
def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.append(List(width=4, compact=bool(token.meta['compact']))) self._list_stack.append(List(width=4, compact=bool(token.meta['compact'])))
return self._maybe_parbreak() return self._maybe_parbreak()
def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.pop() self._list_stack.pop()
return "" return ""
def em_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._font_stack.append("\\fI") self._font_stack.append("\\fI")
return "\\fI" return "\\fI"
def em_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._font_stack.pop() self._font_stack.pop()
return self._font_stack[-1] return self._font_stack[-1]
def strong_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._font_stack.append("\\fB") self._font_stack.append("\\fB")
return "\\fB" return "\\fB"
def strong_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._font_stack.pop() self._font_stack.pop()
return self._font_stack[-1] return self._font_stack[-1]
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def fence(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
s = man_escape(token.content).rstrip('\n') s = man_escape(token.content).rstrip('\n')
return ( return (
'.sp\n' '.sp\n'
@@ -228,8 +208,7 @@ class ManpageRenderer(Renderer):
'.fi\n' '.fi\n'
'.RE' '.RE'
) )
def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
maybe_par = self._maybe_parbreak("\n") maybe_par = self._maybe_parbreak("\n")
self._enter_block() self._enter_block()
return ( return (
@@ -237,62 +216,44 @@ class ManpageRenderer(Renderer):
".RS 4\n" ".RS 4\n"
f"\\h'-3'\\fI\\(lq\\(rq\\fP\\h'1'\\c" f"\\h'-3'\\fI\\(lq\\(rq\\fP\\h'1'\\c"
) )
def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return ".RE" return ".RE"
def note_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open("Note") return self._admonition_open("Note")
def note_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def caution_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open( "Caution") return self._admonition_open( "Caution")
def caution_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def important_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open( "Important") return self._admonition_open( "Important")
def important_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def tip_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open( "Tip") return self._admonition_open( "Tip")
def tip_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def warning_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_open( "Warning") return self._admonition_open( "Warning")
def warning_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return self._admonition_close() return self._admonition_close()
def dl_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return ".RS 4" return ".RS 4"
def dl_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return ".RE" return ".RE"
def dt_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return ".PP" return ".PP"
def dt_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def dd_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._enter_block() self._enter_block()
return ".RS 4" return ".RS 4"
def dd_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._leave_block() self._leave_block()
return ".RE" return ".RE"
def myst_role(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def myst_role(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
if token.meta['name'] in [ 'command', 'env', 'option' ]: if token.meta['name'] in [ 'command', 'env', 'option' ]:
return f'\\fB{man_escape(token.content)}\\fP' return f'\\fB{man_escape(token.content)}\\fP'
elif token.meta['name'] in [ 'file', 'var' ]: elif token.meta['name'] in [ 'file', 'var' ]:
@@ -303,23 +264,18 @@ class ManpageRenderer(Renderer):
return f'\\fB{man_escape(page)}\\fP\\fR({man_escape(section)})\\fP' return f'\\fB{man_escape(page)}\\fP\\fR({man_escape(section)})\\fP'
else: else:
raise NotImplementedError("md node not supported yet", token) raise NotImplementedError("md node not supported yet", token)
def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
# mdoc knows no anchors so we can drop those, but classes must be rejected. # mdoc knows no anchors so we can drop those, but classes must be rejected.
if 'class' in token.attrs: if 'class' in token.attrs:
return super().attr_span_begin(token, tokens, i, options, env) return super().attr_span_begin(token, tokens, i)
return "" return ""
def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return "" return ""
def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported in manpages", token) raise RuntimeError("md token not supported in manpages", token)
def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported in manpages", token) raise RuntimeError("md token not supported in manpages", token)
def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
# max item head width for a number, a dot, and one leading space and one trailing space # max item head width for a number, a dot, and one leading space and one trailing space
width = 3 + len(str(cast(int, token.meta['end']))) width = 3 + len(str(cast(int, token.meta['end'])))
self._list_stack.append( self._list_stack.append(
@@ -327,7 +283,6 @@ class ManpageRenderer(Renderer):
next_idx = cast(int, token.attrs.get('start', 1)), next_idx = cast(int, token.attrs.get('start', 1)),
compact = bool(token.meta['compact']))) compact = bool(token.meta['compact'])))
return self._maybe_parbreak() return self._maybe_parbreak()
def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
self._list_stack.pop() self._list_stack.pop()
return "" return ""

View File

@@ -2,14 +2,13 @@ import argparse
import json import json
from abc import abstractmethod from abc import abstractmethod
from collections.abc import Mapping, MutableMapping, Sequence from collections.abc import Mapping, Sequence
from pathlib import Path from pathlib import Path
from typing import Any, cast, NamedTuple, Optional, Union from typing import Any, cast, NamedTuple, Optional, Union
from xml.sax.saxutils import escape, quoteattr from xml.sax.saxutils import escape, quoteattr
import markdown_it import markdown_it
from markdown_it.token import Token from markdown_it.token import Token
from markdown_it.utils import OptionsDict
from . import options from . import options
from .docbook import DocBookRenderer, Heading from .docbook import DocBookRenderer, Heading
@@ -30,8 +29,7 @@ class ManualDocBookRenderer(DocBookRenderer):
'included_options': self.included_options, 'included_options': self.included_options,
} }
def render(self, tokens: Sequence[Token], options: OptionsDict, def render(self, tokens: Sequence[Token]) -> str:
env: MutableMapping[str, Any]) -> str:
wanted = { 'h1': 'title' } wanted = { 'h1': 'title' }
wanted |= { 'h2': 'subtitle' } if self._toplevel_tag == 'book' else {} wanted |= { 'h2': 'subtitle' } if self._toplevel_tag == 'book' else {}
for (i, (tag, kind)) in enumerate(wanted.items()): for (i, (tag, kind)) in enumerate(wanted.items()):
@@ -62,16 +60,15 @@ class ManualDocBookRenderer(DocBookRenderer):
return (f'<book xmlns="http://docbook.org/ns/docbook"' return (f'<book xmlns="http://docbook.org/ns/docbook"'
f' xmlns:xlink="http://www.w3.org/1999/xlink"' f' xmlns:xlink="http://www.w3.org/1999/xlink"'
f' {maybe_id} version="5.0">' f' {maybe_id} version="5.0">'
f' <title>{self.renderInline(tokens[1].children, options, env)}</title>' f' <title>{self.renderInline(tokens[1].children)}</title>'
f' <subtitle>{self.renderInline(tokens[4].children, options, env)}</subtitle>' f' <subtitle>{self.renderInline(tokens[4].children)}</subtitle>'
f' {super().render(tokens[6:], options, env)}' f' {super().render(tokens[6:])}'
f'</book>') f'</book>')
return super().render(tokens, options, env) return super().render(tokens)
def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int) -> tuple[str, dict[str, str]]:
env: MutableMapping[str, Any]) -> tuple[str, dict[str, str]]: (tag, attrs) = super()._heading_tag(token, tokens, i)
(tag, attrs) = super()._heading_tag(token, tokens, i, options, env)
# render() has already verified that we don't have supernumerary headings and since the # render() has already verified that we don't have supernumerary headings and since the
# book tag is handled specially we can leave the check this simple # book tag is handled specially we can leave the check this simple
if token.tag != 'h1': if token.tag != 'h1':
@@ -81,8 +78,7 @@ class ManualDocBookRenderer(DocBookRenderer):
'xmlns:xlink': "http://www.w3.org/1999/xlink", 'xmlns:xlink': "http://www.w3.org/1999/xlink",
}) })
def _included_thing(self, tag: str, token: Token, tokens: Sequence[Token], i: int, def _included_thing(self, tag: str, token: Token, tokens: Sequence[Token], i: int) -> str:
options: OptionsDict, env: MutableMapping[str, Any]) -> str:
result = [] result = []
# close existing partintro. the generic render doesn't really need this because # close existing partintro. the generic render doesn't really need this because
# it doesn't have a concept of structure in the way the manual does. # it doesn't have a concept of structure in the way the manual does.
@@ -94,26 +90,21 @@ class ManualDocBookRenderer(DocBookRenderer):
r = ManualDocBookRenderer(tag, self._manpage_urls) r = ManualDocBookRenderer(tag, self._manpage_urls)
for (included, path) in token.meta['included']: for (included, path) in token.meta['included']:
try: try:
result.append(r.render(included, options, env)) result.append(r.render(included))
except Exception as e: except Exception as e:
raise RuntimeError(f"rendering {path}") from e raise RuntimeError(f"rendering {path}") from e
return "".join(result) return "".join(result)
def included_options(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def included_options(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
return cast(str, token.meta['rendered-options']) return cast(str, token.meta['rendered-options'])
# TODO minimize docbook diffs with existing conversions. remove soon. # TODO minimize docbook diffs with existing conversions. remove soon.
def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str: return super().paragraph_open(token, tokens, i) + "\n "
return super().paragraph_open(token, tokens, i, options, env) + "\n " def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, return "\n" + super().paragraph_close(token, tokens, i)
env: MutableMapping[str, Any]) -> str: def code_block(self, token: Token, tokens: Sequence[Token], i: int) -> str:
return "\n" + super().paragraph_close(token, tokens, i, options, env)
def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
env: MutableMapping[str, Any]) -> str:
return f"<programlisting>\n{escape(token.content)}</programlisting>" return f"<programlisting>\n{escape(token.content)}</programlisting>"
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def fence(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
info = f" language={quoteattr(token.info)}" if token.info != "" else "" info = f" language={quoteattr(token.info)}" if token.info != "" else ""
return f"<programlisting{info}>\n{escape(token.content)}</programlisting>" return f"<programlisting{info}>\n{escape(token.content)}</programlisting>"
@@ -134,8 +125,8 @@ class DocBookConverter(Converter[ManualDocBookRenderer]):
except Exception as e: except Exception as e:
raise RuntimeError(f"failed to render manual {file}") from e raise RuntimeError(f"failed to render manual {file}") from e
def _parse(self, src: str, env: Optional[MutableMapping[str, Any]] = None) -> list[Token]: def _parse(self, src: str) -> list[Token]:
tokens = super()._parse(src, env) tokens = super()._parse(src)
for token in tokens: for token in tokens:
if token.type != "fence" or not token.info.startswith("{=include=} "): if token.type != "fence" or not token.info.startswith("{=include=} "):
continue continue
@@ -145,12 +136,12 @@ class DocBookConverter(Converter[ManualDocBookRenderer]):
self._parse_options(token) self._parse_options(token)
elif typ in [ 'sections', 'chapters', 'preface', 'parts', 'appendix' ]: elif typ in [ 'sections', 'chapters', 'preface', 'parts', 'appendix' ]:
token.type = 'included_' + typ token.type = 'included_' + typ
self._parse_included_blocks(token, env) self._parse_included_blocks(token)
else: else:
raise RuntimeError(f"unsupported structural include type '{typ}'") raise RuntimeError(f"unsupported structural include type '{typ}'")
return tokens return tokens
def _parse_included_blocks(self, token: Token, env: Optional[MutableMapping[str, Any]]) -> None: def _parse_included_blocks(self, token: Token) -> None:
assert token.map assert token.map
included = token.meta['included'] = [] included = token.meta['included'] = []
for (lnum, line) in enumerate(token.content.splitlines(), token.map[0] + 2): for (lnum, line) in enumerate(token.content.splitlines(), token.map[0] + 2):
@@ -161,7 +152,7 @@ class DocBookConverter(Converter[ManualDocBookRenderer]):
try: try:
self._base_paths.append(path) self._base_paths.append(path)
with open(path, 'r') as f: with open(path, 'r') as f:
tokens = self._parse(f.read(), env) tokens = self._parse(f.read())
included.append((tokens, path)) included.append((tokens, path))
self._base_paths.pop() self._base_paths.pop()
except Exception as e: except Exception as e:

View File

@@ -104,169 +104,120 @@ class Renderer:
def _join_inline(self, ls: Iterable[str]) -> str: def _join_inline(self, ls: Iterable[str]) -> str:
return "".join(ls) return "".join(ls)
def admonition_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def admonition_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
tag = token.meta['kind'] tag = token.meta['kind']
self._admonition_stack.append(tag) self._admonition_stack.append(tag)
return self._admonitions[tag][0](token, tokens, i, options, env) return self._admonitions[tag][0](token, tokens, i)
def admonition_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def admonition_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str: return self._admonitions[self._admonition_stack.pop()][1](token, tokens, i)
return self._admonitions[self._admonition_stack.pop()][1](token, tokens, i, options, env)
def render(self, tokens: Sequence[Token], options: OptionsDict, def render(self, tokens: Sequence[Token]) -> str:
env: MutableMapping[str, Any]) -> str:
def do_one(i: int, token: Token) -> str: def do_one(i: int, token: Token) -> str:
if token.type == "inline": if token.type == "inline":
assert token.children is not None assert token.children is not None
return self.renderInline(token.children, options, env) return self.renderInline(token.children)
elif token.type in self.rules: elif token.type in self.rules:
return self.rules[token.type](tokens[i], tokens, i, options, env) return self.rules[token.type](tokens[i], tokens, i)
else: else:
raise NotImplementedError("md token not supported yet", token) raise NotImplementedError("md token not supported yet", token)
return self._join_block(map(lambda arg: do_one(*arg), enumerate(tokens))) return self._join_block(map(lambda arg: do_one(*arg), enumerate(tokens)))
def renderInline(self, tokens: Sequence[Token], options: OptionsDict, def renderInline(self, tokens: Sequence[Token]) -> str:
env: MutableMapping[str, Any]) -> str:
def do_one(i: int, token: Token) -> str: def do_one(i: int, token: Token) -> str:
if token.type in self.rules: if token.type in self.rules:
return self.rules[token.type](tokens[i], tokens, i, options, env) return self.rules[token.type](tokens[i], tokens, i)
else: else:
raise NotImplementedError("md token not supported yet", token) raise NotImplementedError("md token not supported yet", token)
return self._join_inline(map(lambda arg: do_one(*arg), enumerate(tokens))) return self._join_inline(map(lambda arg: do_one(*arg), enumerate(tokens)))
def text(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def text(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def hardbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def hardbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def softbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def softbreak(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def code_inline(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_inline(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def code_block(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def link_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def link_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def link_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def link_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def list_item_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def list_item_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def list_item_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def em_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def em_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def em_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def strong_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def strong_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def strong_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def fence(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def note_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def note_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def note_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def caution_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def caution_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def caution_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def important_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def important_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def important_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def tip_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def tip_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def tip_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def warning_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def warning_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def warning_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def dl_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def dl_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dl_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def dt_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def dt_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dt_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def dd_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def dd_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def dd_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def myst_role(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def myst_role(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_end(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def example_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def example_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def example_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def example_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported", token) raise RuntimeError("md token not supported", token)
def _is_escaped(src: str, pos: int) -> bool: def _is_escaped(src: str, pos: int) -> bool:
@@ -510,10 +461,9 @@ class Converter(ABC, Generic[TR]):
self._md.use(_block_attr) self._md.use(_block_attr)
self._md.enable(["smartquotes", "replacements"]) self._md.enable(["smartquotes", "replacements"])
def _parse(self, src: str, env: Optional[MutableMapping[str, Any]] = None) -> list[Token]: def _parse(self, src: str) -> list[Token]:
return self._md.parse(src, env if env is not None else {}) return self._md.parse(src, {})
def _render(self, src: str, env: Optional[MutableMapping[str, Any]] = None) -> str: def _render(self, src: str) -> str:
env = {} if env is None else env tokens = self._parse(src)
tokens = self._parse(src, env) return self._renderer.render(tokens)
return self._renderer.render(tokens, self._md.options, env)

View File

@@ -4,8 +4,7 @@ import argparse
import json import json
from abc import abstractmethod from abc import abstractmethod
from collections.abc import Mapping, MutableMapping, Sequence from collections.abc import Mapping, Sequence
from markdown_it.utils import OptionsDict
from markdown_it.token import Token from markdown_it.token import Token
from typing import Any, Generic, Optional from typing import Any, Generic, Optional
from urllib.parse import quote from urllib.parse import quote
@@ -174,29 +173,23 @@ class BaseConverter(Converter[md.TR], Generic[md.TR]):
def finalize(self) -> str: raise NotImplementedError() def finalize(self) -> str: raise NotImplementedError()
class OptionDocsRestrictions: class OptionDocsRestrictions:
def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported in options doc", token) raise RuntimeError("md token not supported in options doc", token)
def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def heading_close(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported in options doc", token) raise RuntimeError("md token not supported in options doc", token)
def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def attr_span_begin(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported in options doc", token) raise RuntimeError("md token not supported in options doc", token)
def example_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def example_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
raise RuntimeError("md token not supported in options doc", token) raise RuntimeError("md token not supported in options doc", token)
class OptionsDocBookRenderer(OptionDocsRestrictions, DocBookRenderer): class OptionsDocBookRenderer(OptionDocsRestrictions, DocBookRenderer):
# TODO keep optionsDocBook diff small. remove soon if rendering is still good. # TODO keep optionsDocBook diff small. remove soon if rendering is still good.
def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
token.meta['compact'] = False token.meta['compact'] = False
return super().ordered_list_open(token, tokens, i, options, env) return super().ordered_list_open(token, tokens, i)
def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int) -> str:
env: MutableMapping[str, Any]) -> str:
token.meta['compact'] = False token.meta['compact'] = False
return super().bullet_list_open(token, tokens, i, options, env) return super().bullet_list_open(token, tokens, i)
class DocBookConverter(BaseConverter[OptionsDocBookRenderer]): class DocBookConverter(BaseConverter[OptionsDocBookRenderer]):
__option_block_separator__ = "" __option_block_separator__ = ""

View File

@@ -1,8 +1,7 @@
from collections.abc import Sequence, MutableMapping from collections.abc import Sequence
from typing import Any, Callable, Optional, Tuple, NamedTuple from typing import Any, Callable, Optional, Tuple, NamedTuple
from markdown_it.token import Token from markdown_it.token import Token
from markdown_it.utils import OptionsDict
OptionLoc = str | dict[str, str] OptionLoc = str | dict[str, str]
Option = dict[str, str | dict[str, str] | list[OptionLoc]] Option = dict[str, str | dict[str, str] | list[OptionLoc]]
@@ -12,4 +11,4 @@ class RenderedOption(NamedTuple):
lines: list[str] lines: list[str]
links: Optional[list[str]] = None links: Optional[list[str]] = None
RenderFn = Callable[[Token, Sequence[Token], int, OptionsDict, MutableMapping[str, Any]], str] RenderFn = Callable[[Token, Sequence[Token], int], str]