diff --git a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/docbook.py b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/docbook.py index 39ce50cc0421..3050093a5bd5 100644 --- a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/docbook.py +++ b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/docbook.py @@ -1,6 +1,6 @@ from collections.abc import Mapping, MutableMapping, Sequence from frozendict import frozendict # type: ignore[attr-defined] -from typing import Any, cast, Optional +from typing import Any, cast, Optional, NamedTuple import markdown_it from markdown_it.token import Token @@ -25,16 +25,27 @@ def make_xml_id(s: str) -> str: class Deflist: has_dd = False +class Heading(NamedTuple): + container_tag: str + level: int + class DocBookRenderer(Renderer): __output__ = "docbook" _link_tags: list[str] _deflists: list[Deflist] + _headings: list[Heading] def __init__(self, manpage_urls: Mapping[str, str], parser: Optional[markdown_it.MarkdownIt] = None): super().__init__(manpage_urls, parser) self._link_tags = [] self._deflists = [] + self._headings = [] + def render(self, tokens: Sequence[Token], options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + result = super().render(tokens, options, env) + result += self._close_headings(None, env) + return result def renderInline(self, tokens: Sequence[Token], options: OptionsDict, env: MutableMapping[str, Any]) -> str: # HACK to support docbook links and xrefs. link handling is only necessary because the docbook @@ -203,3 +214,36 @@ class DocBookRenderer(Renderer): def inline_anchor(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, env: MutableMapping[str, Any]) -> str: return f'' + def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + start = f' startingnumber="{token.attrs["start"]}"' if 'start' in token.attrs else "" + return f"" + def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + return f"" + def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + hlevel = int(token.tag[1:]) + result = self._close_headings(hlevel, env) + (tag, attrs) = self._heading_tag(token, tokens, i, options, env) + self._headings.append(Heading(tag, hlevel)) + attrs_str = "".join([ f" {k}={quoteattr(v)}" for k, v in attrs.items() ]) + return result + f'<{tag}{attrs_str}>\n' + def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + return '' + + def _close_headings(self, level: Optional[int], env: MutableMapping[str, Any]) -> str: + # we rely on markdown-it producing h{1..6} tags in token.tag for this to work + result = [] + while len(self._headings): + if level is None or self._headings[-1].level >= level: + result.append(f"") + self._headings.pop() + else: + break + return "\n".join(result) + + def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> tuple[str, dict[str, str]]: + return ("section", {}) diff --git a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/md.py b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/md.py index 3f4aa7c47679..7aaa56031a3e 100644 --- a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/md.py +++ b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/md.py @@ -64,6 +64,10 @@ class Renderer(markdown_it.renderer.RendererProtocol): "container_admonition_open": self.admonition_open, "container_admonition_close": self.admonition_close, "inline_anchor": self.inline_anchor, + "heading_open": self.heading_open, + "heading_close": self.heading_close, + "ordered_list_open": self.ordered_list_open, + "ordered_list_close": self.ordered_list_close, } self._admonitions = { @@ -218,6 +222,18 @@ class Renderer(markdown_it.renderer.RendererProtocol): def inline_anchor(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, env: MutableMapping[str, Any]) -> str: raise RuntimeError("md token not supported", token) + def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + raise RuntimeError("md token not supported", token) + def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + raise RuntimeError("md token not supported", token) + def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + raise RuntimeError("md token not supported", token) + def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + raise RuntimeError("md token not supported", token) def _is_escaped(src: str, pos: int) -> bool: found = 0 diff --git a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/options.py b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/options.py index d165a5937091..839f29d43704 100644 --- a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/options.py +++ b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/options.py @@ -2,6 +2,9 @@ import argparse import json from abc import abstractmethod +from collections.abc import MutableMapping, Sequence +from markdown_it.utils import OptionsDict +from markdown_it.token import Token from typing import Any, Optional from xml.sax.saxutils import escape, quoteattr @@ -145,8 +148,16 @@ class BaseConverter(Converter): @abstractmethod def finalize(self) -> str: raise NotImplementedError() +class OptionsDocBookRenderer(DocBookRenderer): + def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + raise RuntimeError("md token not supported in options doc", token) + def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, + env: MutableMapping[str, Any]) -> str: + raise RuntimeError("md token not supported in options doc", token) + class DocBookConverter(BaseConverter): - __renderer__ = DocBookRenderer + __renderer__ = OptionsDocBookRenderer def _render_code(self, option: dict[str, Any], key: str) -> list[str]: if lit := option_is(option, key, 'literalDocBook'): diff --git a/pkgs/tools/nix/nixos-render-docs/src/tests/test_options.py b/pkgs/tools/nix/nixos-render-docs/src/tests/test_options.py new file mode 100644 index 000000000000..5a02fabde0fb --- /dev/null +++ b/pkgs/tools/nix/nixos-render-docs/src/tests/test_options.py @@ -0,0 +1,14 @@ +import nixos_render_docs + +from markdown_it.token import Token +import pytest + +def test_option_headings() -> None: + c = nixos_render_docs.options.DocBookConverter({}, 'local', 'none', 'vars', 'opt-', False) + with pytest.raises(RuntimeError) as exc: + c._render("# foo") + assert exc.value.args[0] == 'md token not supported in options doc' + assert exc.value.args[1] == Token( + type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0, children=None, + content='', markup='#', info='', meta={}, block=True, hidden=False + )