summary refs log tree commit diff
path: root/pkgs/tools/nix/nixos-render-docs
diff options
context:
space:
mode:
authorpennae <github@quasiparticle.net>2023-02-15 12:57:32 +0100
committerpennae <github@quasiparticle.net>2023-02-21 18:26:40 +0100
commit2ab8e742a541659baa0470e3be8fc7e01ff51175 (patch)
treeb8175b0c7facc60e657070a12f048a2dc49231df /pkgs/tools/nix/nixos-render-docs
parent5b8be28e66a31ba4683d0fc337f67a46d5db8f9a (diff)
downloadnixpkgs-2ab8e742a541659baa0470e3be8fc7e01ff51175.tar
nixpkgs-2ab8e742a541659baa0470e3be8fc7e01ff51175.tar.gz
nixpkgs-2ab8e742a541659baa0470e3be8fc7e01ff51175.tar.bz2
nixpkgs-2ab8e742a541659baa0470e3be8fc7e01ff51175.tar.lz
nixpkgs-2ab8e742a541659baa0470e3be8fc7e01ff51175.tar.xz
nixpkgs-2ab8e742a541659baa0470e3be8fc7e01ff51175.tar.zst
nixpkgs-2ab8e742a541659baa0470e3be8fc7e01ff51175.zip
nixos-render-docs: move recursive manual parsing to base class
the html renderer will need all of these functions as well. some
extensions will be needed, but we'll add those as they become necessary.
Diffstat (limited to 'pkgs/tools/nix/nixos-render-docs')
-rw-r--r--pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/manual.py153
1 files changed, 77 insertions, 76 deletions
diff --git a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/manual.py b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/manual.py
index 780a5f38c32..2e83ac90b5b 100644
--- a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/manual.py
+++ b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/manual.py
@@ -4,16 +4,90 @@ import json
 from abc import abstractmethod
 from collections.abc import Mapping, Sequence
 from pathlib import Path
-from typing import Any, cast, NamedTuple, Optional, Union
+from typing import Any, cast, Generic, NamedTuple, Optional, Union
 from xml.sax.saxutils import escape, quoteattr
 
 import markdown_it
 from markdown_it.token import Token
 
-from . import options
+from . import md, options
 from .docbook import DocBookRenderer, Heading
 from .md import Converter
 
+class BaseConverter(Converter[md.TR], Generic[md.TR]):
+    _base_paths: list[Path]
+
+    def convert(self, file: Path) -> str:
+        self._base_paths = [ file ]
+        try:
+            with open(file, 'r') as f:
+                return self._render(f.read())
+        except Exception as e:
+            raise RuntimeError(f"failed to render manual {file}") from e
+
+    def _parse(self, src: str) -> list[Token]:
+        tokens = super()._parse(src)
+        for token in tokens:
+            if token.type != "fence" or not token.info.startswith("{=include=} "):
+                continue
+            typ = token.info[12:].strip()
+            if typ == 'options':
+                token.type = 'included_options'
+                self._parse_options(token)
+            elif typ in [ 'sections', 'chapters', 'preface', 'parts', 'appendix' ]:
+                token.type = 'included_' + typ
+                self._parse_included_blocks(token)
+            else:
+                raise RuntimeError(f"unsupported structural include type '{typ}'")
+        return tokens
+
+    def _parse_included_blocks(self, token: Token) -> None:
+        assert token.map
+        included = token.meta['included'] = []
+        for (lnum, line) in enumerate(token.content.splitlines(), token.map[0] + 2):
+            line = line.strip()
+            path = self._base_paths[-1].parent / line
+            if path in self._base_paths:
+                raise RuntimeError(f"circular include found in line {lnum}")
+            try:
+                self._base_paths.append(path)
+                with open(path, 'r') as f:
+                    tokens = self._parse(f.read())
+                    included.append((tokens, path))
+                self._base_paths.pop()
+            except Exception as e:
+                raise RuntimeError(f"processing included file {path} from line {lnum}") from e
+
+    def _parse_options(self, token: Token) -> None:
+        assert token.map
+
+        items = {}
+        for (lnum, line) in enumerate(token.content.splitlines(), token.map[0] + 2):
+            if len(args := line.split(":", 1)) != 2:
+                raise RuntimeError(f"options directive with no argument in line {lnum}")
+            (k, v) = (args[0].strip(), args[1].strip())
+            if k in items:
+                raise RuntimeError(f"duplicate options directive {k} in line {lnum}")
+            items[k] = v
+        try:
+            id_prefix = items.pop('id-prefix')
+            varlist_id = items.pop('list-id')
+            source = items.pop('source')
+        except KeyError as e:
+            raise RuntimeError(f"options directive {e} missing in block at line {token.map[0] + 1}")
+        if items.keys():
+            raise RuntimeError(
+                f"unsupported options directives in block at line {token.map[0] + 1}",
+                " ".join(items.keys()))
+
+        try:
+            with open(self._base_paths[-1].parent / source, 'r') as f:
+                token.meta['id-prefix'] = id_prefix
+                token.meta['list-id'] = varlist_id
+                token.meta['source'] = json.load(f)
+        except Exception as e:
+            raise RuntimeError(f"processing options block in line {token.map[0] + 1}") from e
+
 class ManualDocBookRenderer(DocBookRenderer):
     _toplevel_tag: str
     _revision: str
@@ -113,84 +187,11 @@ class ManualDocBookRenderer(DocBookRenderer):
         info = f" language={quoteattr(token.info)}" if token.info != "" else ""
         return f"<programlisting{info}>\n{escape(token.content)}</programlisting>"
 
-class DocBookConverter(Converter[ManualDocBookRenderer]):
-    _base_paths: list[Path]
-
+class DocBookConverter(BaseConverter[ManualDocBookRenderer]):
     def __init__(self, manpage_urls: Mapping[str, str], revision: str):
         super().__init__()
         self._renderer = ManualDocBookRenderer('book', revision, manpage_urls)
 
-    def convert(self, file: Path) -> str:
-        self._base_paths = [ file ]
-        try:
-            with open(file, 'r') as f:
-                return self._render(f.read())
-        except Exception as e:
-            raise RuntimeError(f"failed to render manual {file}") from e
-
-    def _parse(self, src: str) -> list[Token]:
-        tokens = super()._parse(src)
-        for token in tokens:
-            if token.type != "fence" or not token.info.startswith("{=include=} "):
-                continue
-            typ = token.info[12:].strip()
-            if typ == 'options':
-                token.type = 'included_options'
-                self._parse_options(token)
-            elif typ in [ 'sections', 'chapters', 'preface', 'parts', 'appendix' ]:
-                token.type = 'included_' + typ
-                self._parse_included_blocks(token)
-            else:
-                raise RuntimeError(f"unsupported structural include type '{typ}'")
-        return tokens
-
-    def _parse_included_blocks(self, token: Token) -> None:
-        assert token.map
-        included = token.meta['included'] = []
-        for (lnum, line) in enumerate(token.content.splitlines(), token.map[0] + 2):
-            line = line.strip()
-            path = self._base_paths[-1].parent / line
-            if path in self._base_paths:
-                raise RuntimeError(f"circular include found in line {lnum}")
-            try:
-                self._base_paths.append(path)
-                with open(path, 'r') as f:
-                    tokens = self._parse(f.read())
-                    included.append((tokens, path))
-                self._base_paths.pop()
-            except Exception as e:
-                raise RuntimeError(f"processing included file {path} from line {lnum}") from e
-
-    def _parse_options(self, token: Token) -> None:
-        assert token.map
-
-        items = {}
-        for (lnum, line) in enumerate(token.content.splitlines(), token.map[0] + 2):
-            if len(args := line.split(":", 1)) != 2:
-                raise RuntimeError(f"options directive with no argument in line {lnum}")
-            (k, v) = (args[0].strip(), args[1].strip())
-            if k in items:
-                raise RuntimeError(f"duplicate options directive {k} in line {lnum}")
-            items[k] = v
-        try:
-            id_prefix = items.pop('id-prefix')
-            varlist_id = items.pop('list-id')
-            source = items.pop('source')
-        except KeyError as e:
-            raise RuntimeError(f"options directive {e} missing in block at line {token.map[0] + 1}")
-        if items.keys():
-            raise RuntimeError(
-                f"unsupported options directives in block at line {token.map[0] + 1}",
-                " ".join(items.keys()))
-
-        try:
-            with open(self._base_paths[-1].parent / source, 'r') as f:
-                token.meta['id-prefix'] = id_prefix
-                token.meta['list-id'] = varlist_id
-                token.meta['source'] = json.load(f)
-        except Exception as e:
-            raise RuntimeError(f"processing options block in line {token.map[0] + 1}") from e
-
 
 
 def _build_cli_db(p: argparse.ArgumentParser) -> None: