diff --git a/muppet/output/__init__.py b/muppet/output/__init__.py index a1fb41e214e68a46f5abc02ba778a11082289f31..d03a471fdfd4b9c0bc57d9e8b3b69a27187d940c 100644 --- a/muppet/output/__init__.py +++ b/muppet/output/__init__.py @@ -14,7 +14,6 @@ import json from glob import glob from typing import ( Optional, - Protocol, Sequence, cast, ) @@ -47,6 +46,7 @@ from muppet.cache import AbstractCache from .docstring import format_docstring from .puppet_source import hyperlink_puppet_source +from .util import HtmlSerializable jinja = Environment( @@ -58,18 +58,6 @@ jinja = Environment( logger = logging.getLogger(__name__) -class HtmlSerializable(Protocol): - """Classes which can be serialized as HTML.""" - - def to_html(self) -> str: # pragma: no cover - """Return HTML string.""" - ... - - def to_html_list(self) -> str: # pragma: no cover - """Return HTML suitable for a list.""" - ... - - class Templates: """ Namespace for templates. @@ -95,7 +83,9 @@ class Templates: def code_page(self, *, title: str, content: str, - breadcrumbs: Optional[Breadcrumbs] = None + breadcrumbs: Optional[Breadcrumbs] = None, + left_sidebar: Optional[str] = None, + right_sidebar: Optional[str] = None, ) -> str: # pragma: no cover """ Template for a page containing puppet code. @@ -111,6 +101,8 @@ class Templates: title=title, content=content, path_base=self.path_base, + left_sidebar=left_sidebar, + right_sidebar=right_sidebar, breadcrumbs=breadcrumbs) def content(self, *, @@ -148,7 +140,7 @@ class Templates: def module_index(self, *, # content: list[], # something with to_html_list and to_html content: Sequence[HtmlSerializable], - module_author: str, + module_author: Optional[str], module_name: str, doc_files: list[tuple[str, str]], breadcrumbs: Optional[Breadcrumbs] = None, @@ -196,7 +188,6 @@ class Templates: @dataclass class ResourceTypeOutput: - """Basic HTML implementation.""" title: str module_name: str @@ -211,6 +202,7 @@ class ResourceTypeOutput: If the node has a link, create a hyperlink, otherwise return it's title directly. """ + # TODO make links absolute to root, to allow inclusion anywhere if self.link: return f'<a href="{ self.link }">{ self.title }</a>' else: @@ -223,6 +215,7 @@ class ResourceTypeOutput: .get_template('snippets/ResourceType-index-entry.html') \ .render(item=self, module_name=self.module_name, + # TODO don't hardcode prefix prefix='/code/muppet-strings/output') def to_html_list(self) -> str: @@ -253,6 +246,7 @@ class IndexItem: def base(self) -> str: """Return link to self.""" + # TODO make links absolute to root, to allow inclusion anywhere return f'<a href="{self.file}">{ self.name }</a>' def to_html(self) -> str: @@ -350,7 +344,6 @@ class IndexCategory: @dataclass class ResourceIndex: - """Placeholder.""" title: str children: list[HtmlSerializable] @@ -430,8 +423,23 @@ class PuppetModule: except FileNotFoundError: self.metadata = {} + self.module_toc: str = ''.join([ + '<ul class="toc">', + *(e.to_html_list() for e in self._build_module_toc()), + '</ul>']) + def _build_module_toc(self) -> Sequence[ResourceIndex | IndexCategory]: - """Build the TOC of the module.""" + """ + Build the TOC of the module. + + This method looks into the strings output of the current + object, and creates a TOC tree which more or less has the + strings output keys as top level entries, and all + resources/classes/... of that type as child entries. + + :returns: + A list of categories. + """ content: list[ResourceIndex | IndexCategory] = [] if puppet_classes := self.strings_output.puppet_classes: @@ -522,16 +530,12 @@ class PuppetModule: with open(os.path.join(destination, 'index.html'), 'w') as f: f.write(self.templates.module_index( module_name=self.name, - module_author='TODO', # module.metadata['author'], + module_author=self.metadata.get('author'), breadcrumbs=crumbs, content=toc, doc_files=doc_files_toc, # left_sidebar=(), - right_sidebar=''.join([ - '<ul class="toc">', - *(e.to_html_list() for e in toc), - '</ul>', - ]))) + right_sidebar=self.module_toc)) def _generate_classes(self, destination: str) -> None: for puppet_class in self.strings_output.puppet_classes \ @@ -559,20 +563,14 @@ class PuppetModule: ) with open(os.path.join(dir, 'source.pp.html'), 'w') as f: - with open(self.file(puppet_class.file), 'r') as g: f.write(self.templates.code_page( + # TODO handle title? title='', + left_sidebar=self.module_toc, content=highlight(g.read(), 'puppet'), breadcrumbs=crumbs)) - # TODO reimplement this? - # with open(os.path.join(dir, 'source.json'), 'w') as f: - # json.dump(puppet_class, f, indent=2) - - # with open(os.path.join(dir, 'source.pp.html'), 'w') as f: - # f.write(format_class(puppet_class)) - crumbs = breadcrumbs( ('Environment', ''), self.name, @@ -583,8 +581,9 @@ class PuppetModule: title, body = format_class(puppet_class) with open(os.path.join(dir, 'index.html'), 'w') as f: f.write(self.templates.code_page( - title=self.name, + title=title, content=body, + left_sidebar=self.module_toc, breadcrumbs=crumbs)) # puppet_class['file'] @@ -601,10 +600,6 @@ class PuppetModule: with open(os.path.join(dir, 'source.pp.txt'), 'w') as f: f.write(type_alias.alias_of) - # TODO reimplement this? - # with open(os.path.join(dir, 'source.json'), 'w') as f: - # json.dump(type_alias, f, indent=2) - title, body = format_type_alias(type_alias, type_alias.name) with open(os.path.join(dir, 'index.html'), 'w') as f: f.write(self.templates.code_page( @@ -854,7 +849,9 @@ def type_aliases_index(alias_list: list[DataTypeAlias]) -> IndexCategory: def resource_type_index(resource_types: list[ResourceType], module_name: str) -> list[HtmlSerializable]: - """Generate index for all known resource types.""" + """ + Generate index of all known resource types. + """ lst: list[HtmlSerializable] = [] for resource_type in resource_types: @@ -910,7 +907,7 @@ def format_class(d_type: DefinedType | PuppetClass) -> tuple[str, str]: out = '' logger.info("Formatting class %s", d_type.name) # print(name, file=sys.stderr) - name, body = format_docstring(d_type.name, d_type.docstring) + body = format_docstring(d_type.name, d_type.docstring) out += body # ------ Old --------------------------------------- @@ -947,7 +944,7 @@ def format_class(d_type: DefinedType | PuppetClass) -> tuple[str, str]: else: out += d_type.source out += '</code></pre>' - return name, out + return d_type.name, out def build_param_dict(docstring: DocString) -> dict[str, str]: @@ -985,7 +982,7 @@ def format_type_alias(d_type: DataTypeAlias, file: str) -> tuple[str, str]: name = d_type.name logger.info("Formatting type alias %s", name) # print(name, file=sys.stderr) - title, body = format_docstring(name, d_type.docstring) + body = format_docstring(name, d_type.docstring) out += body out += '\n' out += '<pre class="highlight-muppet"><code class="puppet">' @@ -995,7 +992,7 @@ def format_type_alias(d_type: DataTypeAlias, file: str) -> tuple[str, str]: logger.error("Parsing %(name)s failed: %(err)s", {'name': d_type.alias_of, 'err': e}) out += '</code></pre>\n' - return title, out + return d_type.name, out def format_defined_type(d_type: DefinedType, file: str) -> tuple[str, str]: @@ -1005,7 +1002,7 @@ def format_defined_type(d_type: DefinedType, file: str) -> tuple[str, str]: name = d_type.name logger.info("Formatting defined type %s", name) # print(name, file=sys.stderr) - title, body = format_docstring(name, d_type.docstring) + body = format_docstring(name, d_type.docstring) out += body out += '<pre class="highlight-muppet"><code class="puppet">' @@ -1015,7 +1012,7 @@ def format_defined_type(d_type: DefinedType, file: str) -> tuple[str, str]: logger.error("Parsing %(name)s failed: %(err)s", {'name': d_type.source, 'err': e}) out += '</code></pre>\n' - return title, out + return d_type.name, out def format_resource_type(r_type: ResourceType) -> str: diff --git a/muppet/output/docstring.py b/muppet/output/docstring.py index b85676c2a27788c61070fb6dd7c2f8fb62ba8610..8b0ea37dd675238791f28c432368f8c11c099db6 100644 --- a/muppet/output/docstring.py +++ b/muppet/output/docstring.py @@ -33,10 +33,6 @@ from muppet.puppet.strings import ( ) -# TODO what even is this for? -param_doc: dict[str, str] = {} - - @dataclass class GroupedTags: """ @@ -106,7 +102,7 @@ def parse_author(author: str) -> str: return html.escape(m['any']) -def format_docstring(name: str, docstring: DocString) -> tuple[str, str]: +def format_docstring(name: str, docstring: DocString) -> str: """ Format docstrings as they appear in some puppet types. @@ -116,16 +112,10 @@ def format_docstring(name: str, docstring: DocString) -> tuple[str, str]: * puppet_type_aliases, and * defined_types """ - global param_doc - # The api tag is ignored, since it instead is shown from context out = '' - param_doc = {tag.name: tag.text or '' - for tag in docstring.tags - if isinstance(tag, DocStringParamTag)} - grouped_tags = GroupedTags.from_taglist(docstring.tags) # -------------------------------------------------- @@ -255,4 +245,4 @@ def format_docstring(name: str, docstring: DocString) -> tuple[str, str]: # since # _other - return (name, out) + return out diff --git a/muppet/output/util.py b/muppet/output/util.py index b1d69f5e973ce332a84d98c9dcb8f6f033c1f787..2c3c3a3256e30d97fde053b6cc109c96f0b58909 100644 --- a/muppet/output/util.py +++ b/muppet/output/util.py @@ -7,6 +7,7 @@ useful than other. The aim is to only have pure functions here. """ +from typing import Protocol from muppet.parser_combinator import ( MatchCompound, MatchObject, @@ -42,3 +43,15 @@ def inner_text(obj: MatchObject | list[MatchObject]) -> str: return ''.join(inner_text(x) for x in xs) case _: raise ValueError('How did we get here') + + +class HtmlSerializable(Protocol): + """Classes which can be serialized as HTML.""" + + def to_html(self) -> str: # pragma: no cover + """Return HTML string.""" + ... + + def to_html_list(self) -> str: # pragma: no cover + """Return HTML suitable for a list.""" + ... diff --git a/templates/code_page.html b/templates/code_page.html index 5b2d3646aa2ef70868dc5ffb44de66d1c3247de3..6aecc8a0546073c91c805c4027e5c64bac955dd0 100644 --- a/templates/code_page.html +++ b/templates/code_page.html @@ -9,9 +9,15 @@ Parameters: {% extends "base.html" %} {% block left_sidebar %} {# Class list, basically the right sidebar from module index #} + {% if left_sidebar %} + {{ left_sidebar }} + {% endif %} {% endblock %} {% block right_sidebar %} {# All defined variables #} + {% if right_sidebar %} + {{ right_sidebar }} + {% endif %} {% endblock %} {% block content %} <h1><code>{{ title }}</code></h1> diff --git a/templates/module_index.html b/templates/module_index.html index 7f76a365a2e8a8a6fcfb63966e97a34f618ab888..d00e73a455625103f85c45d53d32cb4772a6e9c8 100644 --- a/templates/module_index.html +++ b/templates/module_index.html @@ -26,7 +26,7 @@ Parameters: {% endif %} {% endblock %} {% block content %} -<h1>{{ module_author }} / {{ module_name.title() }}</h1> + <h1>{% if module_author %}{{ module_author }} / {% endif %}{{ module_name.title() }}</h1> <ul> {% for name, path in doc_files %}