nixos-render-docs: genericize block numbering
examples and figures behave identically regarding numbering and titling, they just don't share a common number space. make the numbering/titling function generic over block types now so figures can just use it.
This commit is contained in:
@@ -569,23 +569,24 @@ class HTMLConverter(BaseConverter[ManualHTMLRenderer]):
|
|||||||
self._redirection_targets.add(into)
|
self._redirection_targets.add(into)
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
def _number_examples(self, tokens: Sequence[Token], start: int = 1) -> int:
|
def _number_block(self, block: str, prefix: str, tokens: Sequence[Token], start: int = 1) -> int:
|
||||||
|
title_open, title_close = f'{block}_title_open', f'{block}_title_close'
|
||||||
for (i, token) in enumerate(tokens):
|
for (i, token) in enumerate(tokens):
|
||||||
if token.type == "example_title_open":
|
if token.type == title_open:
|
||||||
title = tokens[i + 1]
|
title = tokens[i + 1]
|
||||||
assert title.type == 'inline' and title.children
|
assert title.type == 'inline' and title.children
|
||||||
# the prefix is split into two tokens because the xref title_html will want
|
# the prefix is split into two tokens because the xref title_html will want
|
||||||
# only the first of the two, but both must be rendered into the example itself.
|
# only the first of the two, but both must be rendered into the example itself.
|
||||||
title.children = (
|
title.children = (
|
||||||
[
|
[
|
||||||
Token('text', '', 0, content=f'Example {start}'),
|
Token('text', '', 0, content=f'{prefix} {start}'),
|
||||||
Token('text', '', 0, content='. ')
|
Token('text', '', 0, content='. ')
|
||||||
] + title.children
|
] + title.children
|
||||||
)
|
)
|
||||||
start += 1
|
start += 1
|
||||||
elif token.type.startswith('included_') and token.type != 'included_options':
|
elif token.type.startswith('included_') and token.type != 'included_options':
|
||||||
for sub, _path in token.meta['included']:
|
for sub, _path in token.meta['included']:
|
||||||
start = self._number_examples(sub, start)
|
start = self._number_block(block, prefix, sub, start)
|
||||||
return start
|
return start
|
||||||
|
|
||||||
# xref | (id, type, heading inlines, file, starts new file)
|
# xref | (id, type, heading inlines, file, starts new file)
|
||||||
@@ -636,7 +637,7 @@ class HTMLConverter(BaseConverter[ManualHTMLRenderer]):
|
|||||||
toc_html = f"{n}. {title_html}"
|
toc_html = f"{n}. {title_html}"
|
||||||
title_html = f"Appendix {n}"
|
title_html = f"Appendix {n}"
|
||||||
elif typ == 'example':
|
elif typ == 'example':
|
||||||
# skip the prepended `Example N. ` from _number_examples
|
# skip the prepended `Example N. ` from numbering
|
||||||
toc_html, title = self._renderer.renderInline(inlines.children[2:]), title_html
|
toc_html, title = self._renderer.renderInline(inlines.children[2:]), title_html
|
||||||
# xref title wants only the prepended text, sans the trailing colon and space
|
# xref title wants only the prepended text, sans the trailing colon and space
|
||||||
title_html = self._renderer.renderInline(inlines.children[0:1])
|
title_html = self._renderer.renderInline(inlines.children[0:1])
|
||||||
@@ -651,7 +652,7 @@ class HTMLConverter(BaseConverter[ManualHTMLRenderer]):
|
|||||||
return XrefTarget(id, title_html, toc_html, re.sub('<.*?>', '', title), path, drop_fragment)
|
return XrefTarget(id, title_html, toc_html, re.sub('<.*?>', '', title), path, drop_fragment)
|
||||||
|
|
||||||
def _postprocess(self, infile: Path, outfile: Path, tokens: Sequence[Token]) -> None:
|
def _postprocess(self, infile: Path, outfile: Path, tokens: Sequence[Token]) -> None:
|
||||||
self._number_examples(tokens)
|
self._number_block('example', "Example", tokens)
|
||||||
xref_queue = self._collect_ids(tokens, outfile.name, 'book', True)
|
xref_queue = self._collect_ids(tokens, outfile.name, 'book', True)
|
||||||
|
|
||||||
failed = False
|
failed = False
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from abc import ABC
|
from abc import ABC
|
||||||
from collections.abc import Mapping, MutableMapping, Sequence
|
from collections.abc import Mapping, MutableMapping, Sequence
|
||||||
from typing import Any, cast, Generic, get_args, Iterable, Literal, NoReturn, Optional, TypeVar
|
from typing import Any, Callable, cast, Generic, get_args, Iterable, Literal, NoReturn, Optional, TypeVar
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import re
|
import re
|
||||||
@@ -426,31 +426,37 @@ def _block_attr(md: markdown_it.MarkdownIt) -> None:
|
|||||||
|
|
||||||
md.core.ruler.push("block_attr", block_attr)
|
md.core.ruler.push("block_attr", block_attr)
|
||||||
|
|
||||||
def _example_titles(md: markdown_it.MarkdownIt) -> None:
|
def _block_titles(block: str) -> Callable[[markdown_it.MarkdownIt], None]:
|
||||||
|
open, close = f'{block}_open', f'{block}_close'
|
||||||
|
title_open, title_close = f'{block}_title_open', f'{block}_title_close'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
find title headings of examples and stick them into meta for renderers, then
|
find title headings of blocks and stick them into meta for renderers, then
|
||||||
remove them from the token stream. also checks whether any example contains a
|
remove them from the token stream. also checks whether any block contains a
|
||||||
non-title heading since those would make toc generation extremely complicated.
|
non-title heading since those would make toc generation extremely complicated.
|
||||||
"""
|
"""
|
||||||
def example_titles(state: markdown_it.rules_core.StateCore) -> None:
|
def block_titles(state: markdown_it.rules_core.StateCore) -> None:
|
||||||
in_example = [False]
|
in_example = [False]
|
||||||
for i, token in enumerate(state.tokens):
|
for i, token in enumerate(state.tokens):
|
||||||
if token.type == 'example_open':
|
if token.type == open:
|
||||||
if state.tokens[i + 1].type == 'heading_open':
|
if state.tokens[i + 1].type == 'heading_open':
|
||||||
assert state.tokens[i + 3].type == 'heading_close'
|
assert state.tokens[i + 3].type == 'heading_close'
|
||||||
state.tokens[i + 1].type = 'example_title_open'
|
state.tokens[i + 1].type = title_open
|
||||||
state.tokens[i + 3].type = 'example_title_close'
|
state.tokens[i + 3].type = title_close
|
||||||
else:
|
else:
|
||||||
assert token.map
|
assert token.map
|
||||||
raise RuntimeError(f"found example without title in line {token.map[0] + 1}")
|
raise RuntimeError(f"found {block} without title in line {token.map[0] + 1}")
|
||||||
in_example.append(True)
|
in_example.append(True)
|
||||||
elif token.type == 'example_close':
|
elif token.type == close:
|
||||||
in_example.pop()
|
in_example.pop()
|
||||||
elif token.type == 'heading_open' and in_example[-1]:
|
elif token.type == 'heading_open' and in_example[-1]:
|
||||||
assert token.map
|
assert token.map
|
||||||
raise RuntimeError(f"unexpected non-title heading in example in line {token.map[0] + 1}")
|
raise RuntimeError(f"unexpected non-title heading in {block} in line {token.map[0] + 1}")
|
||||||
|
|
||||||
md.core.ruler.push("example_titles", example_titles)
|
def do_add(md: markdown_it.MarkdownIt) -> None:
|
||||||
|
md.core.ruler.push(f"{block}_titles", block_titles)
|
||||||
|
|
||||||
|
return do_add
|
||||||
|
|
||||||
TR = TypeVar('TR', bound='Renderer')
|
TR = TypeVar('TR', bound='Renderer')
|
||||||
|
|
||||||
@@ -494,7 +500,7 @@ class Converter(ABC, Generic[TR]):
|
|||||||
self._md.use(_heading_ids)
|
self._md.use(_heading_ids)
|
||||||
self._md.use(_compact_list_attr)
|
self._md.use(_compact_list_attr)
|
||||||
self._md.use(_block_attr)
|
self._md.use(_block_attr)
|
||||||
self._md.use(_example_titles)
|
self._md.use(_block_titles("example"))
|
||||||
self._md.enable(["smartquotes", "replacements"])
|
self._md.enable(["smartquotes", "replacements"])
|
||||||
|
|
||||||
def _parse(self, src: str) -> list[Token]:
|
def _parse(self, src: str) -> list[Token]:
|
||||||
|
|||||||
Reference in New Issue
Block a user