diff --git a/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/commonmark.py b/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/commonmark.py index 7f31d0be44ae..3831ae3d344d 100644 --- a/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/commonmark.py +++ b/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/commonmark.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from typing import cast, Optional from .md import md_escape, md_make_code, Renderer +from .types import AdmonitionStyle from markdown_it.token import Token @@ -20,12 +21,14 @@ class Par: class CommonMarkRenderer(Renderer): __output__ = "commonmark" + _admonition_style: AdmonitionStyle _parstack: list[Par] _link_stack: list[str] _list_stack: list[List] - def __init__(self, manpage_urls: Mapping[str, str]): + def __init__(self, manpage_urls: Mapping[str, str], admonition_style: AdmonitionStyle = AdmonitionStyle.PLAIN): super().__init__(manpage_urls) + self._admonition_style = admonition_style self._parstack = [ Par("") ] self._link_stack = [] self._list_stack = [] @@ -44,11 +47,23 @@ class CommonMarkRenderer(Renderer): return result def _admonition_open(self, kind: str) -> str: - pbreak = self._maybe_parbreak() - self._enter_block("") - return f"{pbreak}**{kind}:** " + match self._admonition_style: + case AdmonitionStyle.PLAIN: + pbreak = self._maybe_parbreak() + self._enter_block("") + return f"{pbreak}**{kind}:** " + case AdmonitionStyle.GFM: + pbreak = self._maybe_parbreak() + lbreak = self._break() + self._enter_block("> ") + return f"{pbreak}> [!{kind}]{lbreak}> " + def _admonition_close(self) -> str: - self._leave_block() + match self._admonition_style: + case AdmonitionStyle.PLAIN: + self._leave_block() + case AdmonitionStyle.GFM: + self._leave_block() return "" def _indent_raw(self, s: str) -> str: diff --git a/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/options.py b/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/options.py index c6bfdcbf9b81..da99429044bf 100644 --- a/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/options.py +++ b/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/options.py @@ -21,7 +21,7 @@ from .html import HTMLRenderer from .manpage import ManpageRenderer, man_escape from .manual_structure import make_xml_id, XrefTarget from .md import Converter, md_escape, md_make_code -from .types import OptionLoc, Option, RenderedOption, AnchorStyle +from .types import OptionLoc, Option, RenderedOption, AnchorStyle, AdmonitionStyle def option_is(option: Option, key: str, typ: str) -> Optional[dict[str, str]]: if key not in option: @@ -317,18 +317,20 @@ class OptionsCommonMarkRenderer(OptionDocsRestrictions, CommonMarkRenderer): class CommonMarkConverter(BaseConverter[OptionsCommonMarkRenderer]): __option_block_separator__ = "" + _admonition_style: AdmonitionStyle _anchor_style: AnchorStyle _anchor_prefix: str - def __init__(self, manpage_urls: Mapping[str, str], revision: str, anchor_style: AnchorStyle = AnchorStyle.NONE, anchor_prefix: str = ""): + def __init__(self, manpage_urls: Mapping[str, str], revision: str, anchor_style: AnchorStyle = AnchorStyle.NONE, anchor_prefix: str = "", admonition_style: AdmonitionStyle = AdmonitionStyle.PLAIN): super().__init__(revision) - self._renderer = OptionsCommonMarkRenderer(manpage_urls) + self._renderer = OptionsCommonMarkRenderer(manpage_urls, admonition_style) + self._admonition_style = admonition_style self._anchor_style = anchor_style self._anchor_prefix = anchor_prefix def _parallel_render_prepare(self) -> Any: - return (self._renderer._manpage_urls, self._revision, self._anchor_style, self._anchor_prefix) + return (self._renderer._manpage_urls, self._revision, self._anchor_style, self._anchor_prefix, self._admonition_style) @classmethod def _parallel_render_init_worker(cls, a: Any) -> CommonMarkConverter: return cls(*a) @@ -514,6 +516,15 @@ def parse_anchor_style(value: str|AnchorStyle) -> AnchorStyle: except ValueError: raise argparse.ArgumentTypeError(f"Invalid value {value}\nExpected one of {', '.join(style.value for style in AnchorStyle)}") +def parse_admonition_style(value: str|AdmonitionStyle) -> AdmonitionStyle: + if isinstance(value, AdmonitionStyle): + # Used by `argparse.add_argument`'s `default` + return value + try: + return AdmonitionStyle(value.lower()) + except ValueError: + raise argparse.ArgumentTypeError(f"Invalid value {value}\nExpected one of {', '.join(style.value for style in AdmonitionStyle)}") + def _build_cli_commonmark(p: argparse.ArgumentParser) -> None: p.add_argument('--manpage-urls', required=True) p.add_argument('--revision', required=True) @@ -529,6 +540,13 @@ def _build_cli_commonmark(p: argparse.ArgumentParser) -> None: default="", help="(default: no prefix) String to prepend to anchor ids. Not used when anchor style is none." ) + p.add_argument( + '--admonition-style', + required=False, + default=AdmonitionStyle.PLAIN.value, + choices = [style.value for style in AdmonitionStyle], + help = "(default: %(default)s) Admonition style to use for notes, warnings, etc. \nOnly plain is standard CommonMark." + ) p.add_argument("infile") p.add_argument("outfile") @@ -566,7 +584,9 @@ def _run_cli_commonmark(args: argparse.Namespace) -> None: md = CommonMarkConverter(json.load(manpage_urls), revision = args.revision, anchor_style = parse_anchor_style(args.anchor_style), - anchor_prefix = args.anchor_prefix) + anchor_prefix = args.anchor_prefix, + admonition_style = parse_admonition_style(args.admonition_style), + ) with open(args.infile, 'r') as f: md.add_options(json.load(f)) diff --git a/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/types.py b/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/types.py index b5c6e91a9b03..4e8bcf674bc7 100644 --- a/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/types.py +++ b/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/types.py @@ -17,3 +17,7 @@ RenderFn = Callable[[Token, Sequence[Token], int], str] class AnchorStyle(Enum): NONE = "none" LEGACY = "legacy" + +class AdmonitionStyle(Enum): + PLAIN = "plain" + GFM = "gfm" diff --git a/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition.json b/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition.json new file mode 100644 index 000000000000..7306b0d278f4 --- /dev/null +++ b/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition.json @@ -0,0 +1,17 @@ +{ + "services.frobnicator.types..enable": { + "declarations": [ + "nixos/modules/services/frobnicator.nix" + ], + "description": "Whether to enable the frobnication of this (``) type.\n::: {.important}\n\nAdmonition.\n\n:::", + "loc": [ + "services", + "frobnicator", + "types", + "", + "enable" + ], + "readOnly": false, + "type": "boolean" + } +} diff --git a/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition_gfm.md b/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition_gfm.md new file mode 100644 index 000000000000..f66121dad7ac --- /dev/null +++ b/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition_gfm.md @@ -0,0 +1,16 @@ +## services\.frobnicator\.types\.\\.enable + +Whether to enable the frobnication of this (` `) type\. + +> [!Important] +> Admonition\. + + + +*Type:* +boolean + +*Declared by:* + - [\](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/frobnicator.nix) + + diff --git a/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition_plain.md b/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition_plain.md new file mode 100644 index 000000000000..c011f8ac1c45 --- /dev/null +++ b/pkgs/by-name/ni/nixos-render-docs/src/tests/sample_options_admonition_plain.md @@ -0,0 +1,15 @@ +## services\.frobnicator\.types\.\\.enable + +Whether to enable the frobnication of this (` `) type\. + +**Important:** Admonition\. + + + +*Type:* +boolean + +*Declared by:* + - [\](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/frobnicator.nix) + + diff --git a/pkgs/by-name/ni/nixos-render-docs/src/tests/test_commonmark.py b/pkgs/by-name/ni/nixos-render-docs/src/tests/test_commonmark.py index 8a4e20d3d564..c1c713f1f416 100644 --- a/pkgs/by-name/ni/nixos-render-docs/src/tests/test_commonmark.py +++ b/pkgs/by-name/ni/nixos-render-docs/src/tests/test_commonmark.py @@ -1,3 +1,5 @@ +import pytest + import nixos_render_docs as nrd from sample_md import sample1 @@ -6,9 +8,14 @@ from typing import Mapping class Converter(nrd.md.Converter[nrd.commonmark.CommonMarkRenderer]): - def __init__(self, manpage_urls: Mapping[str, str]): + def __init__( + self, + manpage_urls: Mapping[str, str], + admonition_style: nrd.types.AdmonitionStyle = nrd.types.AdmonitionStyle.PLAIN, + ): super().__init__() - self._renderer = nrd.commonmark.CommonMarkRenderer(manpage_urls) + self._renderer = nrd.commonmark.CommonMarkRenderer(manpage_urls, admonition_style) + # NOTE: in these tests we represent trailing spaces by ` ` and replace them with real space later, # since a number of editors will strip trailing whitespace on save and that would break the tests. @@ -24,6 +31,84 @@ def test_indented_fence() -> None: """.replace(' ', ' ') assert c._render(s) == s + +@pytest.mark.parametrize( + ("style", "expected"), + [ + ( + nrd.types.AdmonitionStyle.PLAIN, + """\ +**Warning:** foo + +**Note:** nested + +**Important:** nested GFM + +::: + +**Caution:** GFM caution + +**Important:** nested fenced + +**Note:** nested GFM\ +""", + ), + ( + nrd.types.AdmonitionStyle.GFM, + f"""\ +> [!Warning] +> foo +>{" "} +> > [!Note] +> > nested + +> [!Important] +> nested GFM + +::: + +> [!Caution] +> GFM caution +>{" "} +> > [!Important] +> > nested fenced +>{" "} +> > [!Note] +> > nested GFM\ +""", + ), + ], +) +def test_admonition_styles( + style: nrd.types.AdmonitionStyle, + expected: str, +) -> None: + c = Converter({}, admonition_style=style) + assert c._render("""\ +:::: {.warning} +foo +::: {.note} +nested +::: +:::: + +> [!important] +> nested GFM + +::: + +> [!caution] +> GFM caution +> +> ::: {.important} +> nested fenced +> ::: +> +> > [!note] +> > nested GFM +""") == expected + + def test_full() -> None: c = Converter({ 'man(1)': 'http://example.org' }) assert c._render(sample1) == """\ diff --git a/pkgs/by-name/ni/nixos-render-docs/src/tests/test_options.py b/pkgs/by-name/ni/nixos-render-docs/src/tests/test_options.py index 12639c0f30f8..3e774b1b020b 100644 --- a/pkgs/by-name/ni/nixos-render-docs/src/tests/test_options.py +++ b/pkgs/by-name/ni/nixos-render-docs/src/tests/test_options.py @@ -39,3 +39,29 @@ def test_options_commonmark_legacy_anchors() -> None: c.add_options(opts) s = c.finalize() assert s == expected + +@pytest.mark.parametrize( + ("style", "expected_file"), + [ + (nixos_render_docs.types.AdmonitionStyle.PLAIN, + "tests/sample_options_admonition_plain.md"), + (nixos_render_docs.types.AdmonitionStyle.GFM, + "tests/sample_options_admonition_gfm.md"), + ], +) +def test_options_commonmark_admonition_style(style, expected_file): + c = nixos_render_docs.options.CommonMarkConverter( + {}, + "local", + admonition_style=style, + ) + + with Path("tests/sample_options_admonition.json").open() as f: + opts = json.load(f) + + with Path(expected_file).open() as f: + expected = f.read() + + c.add_options(opts) + + assert c.finalize() == expected