mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-16 14:54:29 +01:00
nixos-render-docs: add heading id support
as with inline spans we support only ids being set for heading, not arbitrary attributes or classes.
This commit is contained in:
parent
82d5698e22
commit
8e3b2a4eaa
3 changed files with 119 additions and 1 deletions
|
@ -246,4 +246,7 @@ class DocBookRenderer(Renderer):
|
|||
|
||||
def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
|
||||
env: MutableMapping[str, Any]) -> tuple[str, dict[str, str]]:
|
||||
return ("section", {})
|
||||
attrs = {}
|
||||
if id := token.attrs.get('id'):
|
||||
attrs['xml:id'] = cast(str, id)
|
||||
return ("section", attrs)
|
||||
|
|
|
@ -316,6 +316,8 @@ def _block_comment_plugin(md: markdown_it.MarkdownIt) -> None:
|
|||
|
||||
md.block.ruler.after("code", "block_comment", block_comment)
|
||||
|
||||
_HEADER_ID_RE = re.compile(r"\s*\{\s*\#([\w-]+)\s*\}\s*$")
|
||||
|
||||
class Converter(ABC):
|
||||
__renderer__: Callable[[Mapping[str, str], markdown_it.MarkdownIt], Renderer]
|
||||
|
||||
|
@ -346,6 +348,17 @@ class Converter(ABC):
|
|||
self._md.enable(["smartquotes", "replacements"])
|
||||
|
||||
def _post_parse(self, tokens: list[Token]) -> list[Token]:
|
||||
for i in range(0, len(tokens)):
|
||||
# parse header IDs. this is purposely simple and doesn't support
|
||||
# classes or other inds of attributes.
|
||||
if tokens[i].type == 'heading_open':
|
||||
children = tokens[i + 1].children
|
||||
assert children is not None
|
||||
if len(children) == 0 or children[-1].type != 'text':
|
||||
continue
|
||||
if m := _HEADER_ID_RE.search(children[-1].content):
|
||||
tokens[i].attrs['id'] = m[1]
|
||||
children[-1].content = children[-1].content[:-len(m[0])].rstrip()
|
||||
return tokens
|
||||
|
||||
def _parse(self, src: str, env: Optional[MutableMapping[str, Any]] = None) -> list[Token]:
|
||||
|
|
102
pkgs/tools/nix/nixos-render-docs/src/tests/test_headings.py
Normal file
102
pkgs/tools/nix/nixos-render-docs/src/tests/test_headings.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
import nixos_render_docs
|
||||
|
||||
from markdown_it.token import Token
|
||||
|
||||
class Converter(nixos_render_docs.md.Converter):
|
||||
# actual renderer doesn't matter, we're just parsing.
|
||||
__renderer__ = nixos_render_docs.docbook.DocBookRenderer
|
||||
|
||||
def test_heading_id_absent() -> None:
|
||||
c = Converter({})
|
||||
assert c._parse("# foo") == [
|
||||
Token(type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0, children=None,
|
||||
content='', markup='#', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1,
|
||||
children=[
|
||||
Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None,
|
||||
content='foo', markup='', info='', meta={}, block=False, hidden=False)
|
||||
],
|
||||
content='foo', markup='', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None,
|
||||
content='', markup='#', info='', meta={}, block=True, hidden=False)
|
||||
]
|
||||
|
||||
def test_heading_id_present() -> None:
|
||||
c = Converter({})
|
||||
assert c._parse("# foo {#foo}\n## bar { #bar}\n### bal { #bal} ") == [
|
||||
Token(type='heading_open', tag='h1', nesting=1, attrs={'id': 'foo'}, map=[0, 1], level=0,
|
||||
children=None, content='', markup='#', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1,
|
||||
content='foo {#foo}', markup='', info='', meta={}, block=True, hidden=False,
|
||||
children=[
|
||||
Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None,
|
||||
content='foo', markup='', info='', meta={}, block=False, hidden=False)
|
||||
]),
|
||||
Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None,
|
||||
content='', markup='#', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='heading_open', tag='h2', nesting=1, attrs={'id': 'bar'}, map=[1, 2], level=0,
|
||||
children=None, content='', markup='##', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='inline', tag='', nesting=0, attrs={}, map=[1, 2], level=1,
|
||||
content='bar { #bar}', markup='', info='', meta={}, block=True, hidden=False,
|
||||
children=[
|
||||
Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None,
|
||||
content='bar', markup='', info='', meta={}, block=False, hidden=False)
|
||||
]),
|
||||
Token(type='heading_close', tag='h2', nesting=-1, attrs={}, map=None, level=0, children=None,
|
||||
content='', markup='##', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='heading_open', tag='h3', nesting=1, attrs={'id': 'bal'}, map=[2, 3], level=0,
|
||||
children=None, content='', markup='###', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='inline', tag='', nesting=0, attrs={}, map=[2, 3], level=1,
|
||||
content='bal { #bal}', markup='', info='', meta={}, block=True, hidden=False,
|
||||
children=[
|
||||
Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None,
|
||||
content='bal', markup='', info='', meta={}, block=False, hidden=False)
|
||||
]),
|
||||
Token(type='heading_close', tag='h3', nesting=-1, attrs={}, map=None, level=0, children=None,
|
||||
content='', markup='###', info='', meta={}, block=True, hidden=False)
|
||||
]
|
||||
|
||||
def test_heading_id_incomplete() -> None:
|
||||
c = Converter({})
|
||||
assert c._parse("# foo {#}") == [
|
||||
Token(type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0, children=None,
|
||||
content='', markup='#', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1,
|
||||
content='foo {#}', markup='', info='', meta={}, block=True, hidden=False,
|
||||
children=[
|
||||
Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None,
|
||||
content='foo {#}', markup='', info='', meta={}, block=False, hidden=False)
|
||||
]),
|
||||
Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None,
|
||||
content='', markup='#', info='', meta={}, block=True, hidden=False)
|
||||
]
|
||||
|
||||
def test_heading_id_double() -> None:
|
||||
c = Converter({})
|
||||
assert c._parse("# foo {#a} {#b}") == [
|
||||
Token(type='heading_open', tag='h1', nesting=1, attrs={'id': 'b'}, map=[0, 1], level=0,
|
||||
children=None, content='', markup='#', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1,
|
||||
content='foo {#a} {#b}', markup='', info='', meta={}, block=True, hidden=False,
|
||||
children=[
|
||||
Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None,
|
||||
content='foo {#a}', markup='', info='', meta={}, block=False, hidden=False)
|
||||
]),
|
||||
Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None,
|
||||
content='', markup='#', info='', meta={}, block=True, hidden=False)
|
||||
]
|
||||
|
||||
def test_heading_id_suffixed() -> None:
|
||||
c = Converter({})
|
||||
assert c._parse("# foo {#a} s") == [
|
||||
Token(type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0,
|
||||
children=None, content='', markup='#', info='', meta={}, block=True, hidden=False),
|
||||
Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1,
|
||||
content='foo {#a} s', markup='', info='', meta={}, block=True, hidden=False,
|
||||
children=[
|
||||
Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None,
|
||||
content='foo {#a} s', markup='', info='', meta={}, block=False, hidden=False)
|
||||
]),
|
||||
Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None,
|
||||
content='', markup='#', info='', meta={}, block=True, hidden=False)
|
||||
]
|
Loading…
Reference in a new issue